From af0d53cdc90f6b1b43d9acff69e23c34ddd713e9 Mon Sep 17 00:00:00 2001 From: Alex Klimaj Date: Tue, 20 Sep 2022 16:53:48 -0600 Subject: [PATCH] boards: V1.13.0 ARKV6X Backport (#20253) --- .github/workflows/compile_nuttx.yml | 1 + .vscode/cmake-variants.yaml | 10 + boards/ark/fmu-v6x/bootloader.px4board | 3 + boards/ark/fmu-v6x/default.px4board | 94 ++ .../fmu-v6x/extras/ark_fmu-v6x_bootloader.bin | Bin 0 -> 45172 bytes .../ark/fmu-v6x/extras/px4_io-v2_default.bin | Bin 0 -> 39952 bytes boards/ark/fmu-v6x/firmware.prototype | 13 + boards/ark/fmu-v6x/init/rc.board_defaults | 19 + boards/ark/fmu-v6x/init/rc.board_sensors | 47 + boards/ark/fmu-v6x/nuttx-config/Kconfig | 17 + .../fmu-v6x/nuttx-config/bootloader/defconfig | 96 +++ .../ark/fmu-v6x/nuttx-config/include/board.h | 558 ++++++++++++ .../nuttx-config/include/board_dma_map.h | 87 ++ boards/ark/fmu-v6x/nuttx-config/nsh/defconfig | 295 +++++++ .../nuttx-config/scripts/bootloader_script.ld | 215 +++++ .../fmu-v6x/nuttx-config/scripts/script.ld | 229 +++++ boards/ark/fmu-v6x/src/CMakeLists.txt | 70 ++ boards/ark/fmu-v6x/src/board_config.h | 515 +++++++++++ boards/ark/fmu-v6x/src/bootloader_main.c | 75 ++ boards/ark/fmu-v6x/src/can.c | 128 +++ boards/ark/fmu-v6x/src/hw_config.h | 128 +++ boards/ark/fmu-v6x/src/i2c.cpp | 41 + boards/ark/fmu-v6x/src/init.c | 276 ++++++ boards/ark/fmu-v6x/src/led.c | 235 +++++ boards/ark/fmu-v6x/src/manifest.c | 150 ++++ boards/ark/fmu-v6x/src/mtd.cpp | 108 +++ boards/ark/fmu-v6x/src/sdio.c | 177 ++++ boards/ark/fmu-v6x/src/spi.cpp | 87 ++ boards/ark/fmu-v6x/src/timer_config.cpp | 80 ++ boards/ark/fmu-v6x/src/usb.c | 105 +++ src/drivers/drv_sensor.h | 41 +- .../imu/invensense/iim42652/CMakeLists.txt | 47 + .../imu/invensense/iim42652/IIM42652.cpp | 800 ++++++++++++++++++ .../imu/invensense/iim42652/IIM42652.hpp | 206 +++++ .../InvenSense_IIM42652_registers.hpp | 322 +++++++ src/drivers/imu/invensense/iim42652/Kconfig | 5 + .../imu/invensense/iim42652/iim42652_main.cpp | 87 ++ 37 files changed, 5352 insertions(+), 15 deletions(-) create mode 100644 boards/ark/fmu-v6x/bootloader.px4board create mode 100644 boards/ark/fmu-v6x/default.px4board create mode 100755 boards/ark/fmu-v6x/extras/ark_fmu-v6x_bootloader.bin create mode 100755 boards/ark/fmu-v6x/extras/px4_io-v2_default.bin create mode 100644 boards/ark/fmu-v6x/firmware.prototype create mode 100644 boards/ark/fmu-v6x/init/rc.board_defaults create mode 100644 boards/ark/fmu-v6x/init/rc.board_sensors create mode 100644 boards/ark/fmu-v6x/nuttx-config/Kconfig create mode 100644 boards/ark/fmu-v6x/nuttx-config/bootloader/defconfig create mode 100644 boards/ark/fmu-v6x/nuttx-config/include/board.h create mode 100644 boards/ark/fmu-v6x/nuttx-config/include/board_dma_map.h create mode 100644 boards/ark/fmu-v6x/nuttx-config/nsh/defconfig create mode 100644 boards/ark/fmu-v6x/nuttx-config/scripts/bootloader_script.ld create mode 100644 boards/ark/fmu-v6x/nuttx-config/scripts/script.ld create mode 100644 boards/ark/fmu-v6x/src/CMakeLists.txt create mode 100644 boards/ark/fmu-v6x/src/board_config.h create mode 100644 boards/ark/fmu-v6x/src/bootloader_main.c create mode 100644 boards/ark/fmu-v6x/src/can.c create mode 100644 boards/ark/fmu-v6x/src/hw_config.h create mode 100644 boards/ark/fmu-v6x/src/i2c.cpp create mode 100644 boards/ark/fmu-v6x/src/init.c create mode 100644 boards/ark/fmu-v6x/src/led.c create mode 100644 boards/ark/fmu-v6x/src/manifest.c create mode 100644 boards/ark/fmu-v6x/src/mtd.cpp create mode 100644 boards/ark/fmu-v6x/src/sdio.c create mode 100644 boards/ark/fmu-v6x/src/spi.cpp create mode 100644 boards/ark/fmu-v6x/src/timer_config.cpp create mode 100644 boards/ark/fmu-v6x/src/usb.c create mode 100644 src/drivers/imu/invensense/iim42652/CMakeLists.txt create mode 100644 src/drivers/imu/invensense/iim42652/IIM42652.cpp create mode 100644 src/drivers/imu/invensense/iim42652/IIM42652.hpp create mode 100644 src/drivers/imu/invensense/iim42652/InvenSense_IIM42652_registers.hpp create mode 100644 src/drivers/imu/invensense/iim42652/Kconfig create mode 100644 src/drivers/imu/invensense/iim42652/iim42652_main.cpp diff --git a/.github/workflows/compile_nuttx.yml b/.github/workflows/compile_nuttx.yml index 186bbb45aa..7410596b6b 100644 --- a/.github/workflows/compile_nuttx.yml +++ b/.github/workflows/compile_nuttx.yml @@ -21,6 +21,7 @@ jobs: ark_can-gps, ark_can-rtk-gps, ark_cannode, + ark_fmu-v6x, atl_mantis-edu, av_x-v1, bitcraze_crazyflie, diff --git a/.vscode/cmake-variants.yaml b/.vscode/cmake-variants.yaml index 9e983b46b5..6f903ba908 100644 --- a/.vscode/cmake-variants.yaml +++ b/.vscode/cmake-variants.yaml @@ -131,6 +131,16 @@ CONFIG: buildType: MinSizeRel settings: CONFIG: ark_cannode_canbootloader + ark_fmu-v6x_bootloader: + short: ark_fmu-v6x_bootloader + buildType: MinSizeRel + settings: + CONFIG: ark_fmu-v6x_bootloader + ark_fmu-v6x_default: + short: ark_fmu-v6x_default + buildType: MinSizeRel + settings: + CONFIG: ark_fmu-v6x_default atl_mantis-edu_default: short: atl_mantis-edu buildType: MinSizeRel diff --git a/boards/ark/fmu-v6x/bootloader.px4board b/boards/ark/fmu-v6x/bootloader.px4board new file mode 100644 index 0000000000..19b6e662be --- /dev/null +++ b/boards/ark/fmu-v6x/bootloader.px4board @@ -0,0 +1,3 @@ +CONFIG_BOARD_TOOLCHAIN="arm-none-eabi" +CONFIG_BOARD_ARCHITECTURE="cortex-m7" +CONFIG_BOARD_ROMFSROOT="" diff --git a/boards/ark/fmu-v6x/default.px4board b/boards/ark/fmu-v6x/default.px4board new file mode 100644 index 0000000000..f57fd7df13 --- /dev/null +++ b/boards/ark/fmu-v6x/default.px4board @@ -0,0 +1,94 @@ +CONFIG_BOARD_TOOLCHAIN="arm-none-eabi" +CONFIG_BOARD_ARCHITECTURE="cortex-m7" +CONFIG_BOARD_ETHERNET=y +CONFIG_BOARD_SERIAL_GPS1="/dev/ttyS0" +CONFIG_BOARD_SERIAL_GPS2="/dev/ttyS7" +CONFIG_BOARD_SERIAL_TEL1="/dev/ttyS6" +CONFIG_BOARD_SERIAL_TEL2="/dev/ttyS4" +CONFIG_BOARD_SERIAL_TEL3="/dev/ttyS1" +CONFIG_DRIVERS_ADC_BOARD_ADC=y +CONFIG_DRIVERS_BAROMETER_BMP388=y +CONFIG_DRIVERS_BAROMETER_MS5611=y +CONFIG_DRIVERS_BATT_SMBUS=y +CONFIG_DRIVERS_CAMERA_CAPTURE=y +CONFIG_DRIVERS_CAMERA_TRIGGER=y +CONFIG_COMMON_DIFFERENTIAL_PRESSURE=y +CONFIG_COMMON_DISTANCE_SENSOR=y +CONFIG_DRIVERS_DSHOT=y +CONFIG_DRIVERS_GPS=y +CONFIG_DRIVERS_HEATER=y +CONFIG_DRIVERS_IMU_INVENSENSE_ICM42688P=y +CONFIG_DRIVERS_IMU_INVENSENSE_IIM42652=y +CONFIG_COMMON_LIGHT=y +CONFIG_COMMON_MAGNETOMETER=y +CONFIG_COMMON_OPTICAL_FLOW=y +CONFIG_DRIVERS_POWER_MONITOR_INA226=y +CONFIG_DRIVERS_POWER_MONITOR_INA228=y +CONFIG_DRIVERS_POWER_MONITOR_INA238=y +CONFIG_DRIVERS_PWM_OUT=y +CONFIG_DRIVERS_PWM_OUT_SIM=y +CONFIG_DRIVERS_PX4IO=y +CONFIG_DRIVERS_RC_INPUT=y +CONFIG_DRIVERS_SAFETY_BUTTON=y +CONFIG_COMMON_TELEMETRY=y +CONFIG_DRIVERS_TONE_ALARM=y +CONFIG_DRIVERS_UAVCAN=y +CONFIG_BOARD_UAVCAN_TIMER_OVERRIDE=2 +CONFIG_MODULES_AIRSPEED_SELECTOR=y +CONFIG_MODULES_CAMERA_FEEDBACK=y +CONFIG_MODULES_COMMANDER=y +CONFIG_MODULES_CONTROL_ALLOCATOR=y +CONFIG_MODULES_DATAMAN=y +CONFIG_MODULES_EKF2=y +CONFIG_MODULES_ESC_BATTERY=y +CONFIG_MODULES_EVENTS=y +CONFIG_MODULES_FLIGHT_MODE_MANAGER=y +CONFIG_MODULES_FW_ATT_CONTROL=y +CONFIG_MODULES_FW_AUTOTUNE_ATTITUDE_CONTROL=y +CONFIG_MODULES_FW_POS_CONTROL_L1=y +CONFIG_MODULES_GIMBAL=y +CONFIG_MODULES_GYRO_CALIBRATION=y +CONFIG_MODULES_GYRO_FFT=y +CONFIG_MODULES_LAND_DETECTOR=y +CONFIG_MODULES_LANDING_TARGET_ESTIMATOR=y +CONFIG_MODULES_LOAD_MON=y +CONFIG_MODULES_LOGGER=y +CONFIG_MODULES_MAG_BIAS_ESTIMATOR=y +CONFIG_MODULES_MANUAL_CONTROL=y +CONFIG_MODULES_MAVLINK=y +CONFIG_MODULES_MC_ATT_CONTROL=y +CONFIG_MODULES_MC_AUTOTUNE_ATTITUDE_CONTROL=y +CONFIG_MODULES_MC_HOVER_THRUST_ESTIMATOR=y +CONFIG_MODULES_MC_POS_CONTROL=y +CONFIG_MODULES_MC_RATE_CONTROL=y +CONFIG_MODULES_NAVIGATOR=y +CONFIG_MODULES_RC_UPDATE=y +CONFIG_MODULES_ROVER_POS_CONTROL=y +CONFIG_MODULES_SENSORS=y +CONFIG_MODULES_SIH=y +CONFIG_MODULES_TEMPERATURE_COMPENSATION=y +CONFIG_MODULES_VTOL_ATT_CONTROL=y +CONFIG_SYSTEMCMDS_ACTUATOR_TEST=y +CONFIG_SYSTEMCMDS_BL_UPDATE=y +CONFIG_SYSTEMCMDS_DMESG=y +CONFIG_SYSTEMCMDS_HARDFAULT_LOG=y +CONFIG_SYSTEMCMDS_I2CDETECT=y +CONFIG_SYSTEMCMDS_LED_CONTROL=y +CONFIG_SYSTEMCMDS_MFT=y +CONFIG_SYSTEMCMDS_MIXER=y +CONFIG_SYSTEMCMDS_MOTOR_TEST=y +CONFIG_SYSTEMCMDS_MTD=y +CONFIG_SYSTEMCMDS_NETMAN=y +CONFIG_SYSTEMCMDS_NSHTERM=y +CONFIG_SYSTEMCMDS_PARAM=y +CONFIG_SYSTEMCMDS_PERF=y +CONFIG_SYSTEMCMDS_PWM=y +CONFIG_SYSTEMCMDS_REBOOT=y +CONFIG_SYSTEMCMDS_SD_BENCH=y +CONFIG_SYSTEMCMDS_SYSTEM_TIME=y +CONFIG_SYSTEMCMDS_TOP=y +CONFIG_SYSTEMCMDS_TOPIC_LISTENER=y +CONFIG_SYSTEMCMDS_TUNE_CONTROL=y +CONFIG_SYSTEMCMDS_UORB=y +CONFIG_SYSTEMCMDS_VER=y +CONFIG_SYSTEMCMDS_WORK_QUEUE=y diff --git a/boards/ark/fmu-v6x/extras/ark_fmu-v6x_bootloader.bin b/boards/ark/fmu-v6x/extras/ark_fmu-v6x_bootloader.bin new file mode 100755 index 0000000000000000000000000000000000000000..b1228d100638a2df84f37247e7c75b315a7c5336 GIT binary patch literal 45172 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 R{OQju_{@w{2q>Zbe*lQ^7s~(u literal 0 HcmV?d00001 diff --git a/boards/ark/fmu-v6x/extras/px4_io-v2_default.bin b/boards/ark/fmu-v6x/extras/px4_io-v2_default.bin new file mode 100755 index 0000000000000000000000000000000000000000..25a00e2357d781ece9c0d41447c3f4a52c6d19f6 GIT binary patch literal 39952 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 literal 0 HcmV?d00001 diff --git a/boards/ark/fmu-v6x/firmware.prototype b/boards/ark/fmu-v6x/firmware.prototype new file mode 100644 index 0000000000..39718c37a3 --- /dev/null +++ b/boards/ark/fmu-v6x/firmware.prototype @@ -0,0 +1,13 @@ +{ + "board_id": 57, + "magic": "ARKFWv1", + "description": "Firmware for the ARKFMUv6X board", + "image": "", + "build_time": 0, + "summary": "ARKFMUv6X", + "version": "0.1", + "image_size": 0, + "image_maxsize": 1966080, + "git_identity": "", + "board_revision": 0 +} diff --git a/boards/ark/fmu-v6x/init/rc.board_defaults b/boards/ark/fmu-v6x/init/rc.board_defaults new file mode 100644 index 0000000000..a7d844a366 --- /dev/null +++ b/boards/ark/fmu-v6x/init/rc.board_defaults @@ -0,0 +1,19 @@ +#!/bin/sh +# +# board specific defaults +#------------------------------------------------------------------------------ + +# Mavlink ethernet (CFG 1000) +param set-default MAV_2_CONFIG 1000 +param set-default MAV_2_BROADCAST 1 +param set-default MAV_2_MODE 0 +param set-default MAV_2_RADIO_CTL 0 +param set-default MAV_2_RATE 100000 +param set-default MAV_2_REMOTE_PRT 14550 +param set-default MAV_2_UDP_PRT 14550 + +param set-default SENS_EN_INA238 0 +param set-default SENS_EN_INA228 0 +param set-default SENS_EN_INA226 1 + +safety_button start diff --git a/boards/ark/fmu-v6x/init/rc.board_sensors b/boards/ark/fmu-v6x/init/rc.board_sensors new file mode 100644 index 0000000000..caf6b2e8de --- /dev/null +++ b/boards/ark/fmu-v6x/init/rc.board_sensors @@ -0,0 +1,47 @@ +#!/bin/sh +# +# PX4 FMUv6X specific board sensors init +#------------------------------------------------------------------------------ +board_adc start + +if param compare SENS_EN_INA226 1 +then + # Start Digital power monitors + ina226 -X -b 1 -t 1 -k start + ina226 -X -b 2 -t 2 -k start +fi + +if param compare SENS_EN_INA228 1 +then + # Start Digital power monitors + ina228 -X -b 1 -t 1 -k start + ina228 -X -b 2 -t 2 -k start +fi + +if param compare SENS_EN_INA238 1 +then + # Start Digital power monitors + ina238 -X -b 1 -t 1 -k start + ina238 -X -b 2 -t 2 -k start +fi + +# Internal SPI bus IIM42652 +iim42652 -R 3 -s -b 1 start + +# Internal SPI bus ICM42688p +icm42688p -R 9 -s -b 2 start + +# Internal SPI bus ICM42688p +icm42688p -R 6 -s -b 3 start + +# Internal magnetometer on I2c +bmm150 -I start + +# External compass on GPS1/I2C1 (the 3rd external bus): standard Holybro Pixhawk 4 or CUAV V5 GPS/compass puck (with lights, safety button, and buzzer) +ist8310 -X -b 1 -R 10 start + +# Possible internal Baro +bmp388 -I start + +# Baro on I2C3 +ms5611 -X start diff --git a/boards/ark/fmu-v6x/nuttx-config/Kconfig b/boards/ark/fmu-v6x/nuttx-config/Kconfig new file mode 100644 index 0000000000..bb33d3cfda --- /dev/null +++ b/boards/ark/fmu-v6x/nuttx-config/Kconfig @@ -0,0 +1,17 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# +config BOARD_HAS_PROBES + bool "Board provides GPIO or other Hardware for signaling to timing analyze." + default y + ---help--- + This board provides GPIO FMU-CH1-5, CAP1-6 as PROBE_1-11 to provide timing signals from selected drivers. + +config BOARD_USE_PROBES + bool "Enable the use the board provided FMU-CH1-5, CAP1-6 as PROBE_1-11" + default n + depends on BOARD_HAS_PROBES + + ---help--- + Select to use GPIO FMU-CH1-5, CAP1-6 to provide timing signals from selected drivers. diff --git a/boards/ark/fmu-v6x/nuttx-config/bootloader/defconfig b/boards/ark/fmu-v6x/nuttx-config/bootloader/defconfig new file mode 100644 index 0000000000..8910e823ae --- /dev/null +++ b/boards/ark/fmu-v6x/nuttx-config/bootloader/defconfig @@ -0,0 +1,96 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_DEV_CONSOLE is not set +# CONFIG_DISABLE_PSEUDOFS_OPERATIONS is not set +# CONFIG_SPI_EXCHANGE is not set +# CONFIG_STM32H7_SYSCFG is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD_CUSTOM=y +CONFIG_ARCH_BOARD_CUSTOM_DIR="../../../../boards/ark/fmu-v6x/nuttx-config" +CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y +CONFIG_ARCH_BOARD_CUSTOM_NAME="px4" +CONFIG_ARCH_CHIP="stm32h7" +CONFIG_ARCH_CHIP_STM32H753II=y +CONFIG_ARCH_CHIP_STM32H7=y +CONFIG_ARCH_INTERRUPTSTACK=512 +CONFIG_ARMV7M_BASEPRI_WAR=y +CONFIG_ARMV7M_ICACHE=y +CONFIG_ARMV7M_MEMCPY=y +CONFIG_ARMV7M_USEBASEPRI=y +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_INITTHREAD_PRIORITY=254 +CONFIG_BOARD_LATE_INITIALIZE=y +CONFIG_BOARD_LOOPSPERMSEC=95150 +CONFIG_BOARD_RESET_ON_ASSERT=2 +CONFIG_C99_BOOL8=y +CONFIG_CDCACM=y +CONFIG_CDCACM_IFLOWCONTROL=y +CONFIG_CDCACM_PRODUCTID=0x0035 +CONFIG_CDCACM_PRODUCTSTR="ARK BL FMU v6X.x" +CONFIG_CDCACM_RXBUFSIZE=600 +CONFIG_CDCACM_TXBUFSIZE=12000 +CONFIG_CDCACM_VENDORID=0x3185 +CONFIG_CDCACM_VENDORSTR="ARK" +CONFIG_CLOCK_MONOTONIC=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_SMALL=y +CONFIG_DISABLE_MQUEUE=y +CONFIG_DISABLE_PTHREAD=y +CONFIG_EXPERIMENTAL=y +CONFIG_FDCLONE_DISABLE=y +CONFIG_FDCLONE_STDIO=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=750 +CONFIG_LIBC_FLOATINGPOINT=y +CONFIG_LIBC_LONG_LONG=y +CONFIG_LIBC_STRERROR=y +CONFIG_LIB_BOARDCTL=y +CONFIG_MEMSET_64BIT=y +CONFIG_MEMSET_OPTSPEED=y +CONFIG_PREALLOC_TIMERS=50 +CONFIG_PTHREAD_STACK_MIN=512 +CONFIG_RAM_SIZE=245760 +CONFIG_RAM_START=0x20010000 +CONFIG_RAW_BINARY=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_SERIAL_TERMIOS=y +CONFIG_SIG_DEFAULT=y +CONFIG_SIG_SIGALRM_ACTION=y +CONFIG_SIG_SIGUSR1_ACTION=y +CONFIG_SIG_SIGUSR2_ACTION=y +CONFIG_SPI=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=30 +CONFIG_START_MONTH=11 +CONFIG_STDIO_BUFFER_SIZE=32 +CONFIG_STM32H7_BKPSRAM=y +CONFIG_STM32H7_DMA1=y +CONFIG_STM32H7_DMA2=y +CONFIG_STM32H7_OTGFS=y +CONFIG_STM32H7_PROGMEM=y +CONFIG_STM32H7_SERIAL_DISABLE_REORDERING=y +CONFIG_STM32H7_TIM1=y +CONFIG_STM32H7_UART7=y +CONFIG_SYSTEMTICK_HOOK=y +CONFIG_SYSTEM_CDCACM=y +CONFIG_TASK_NAME_SIZE=24 +CONFIG_TTY_SIGINT=y +CONFIG_TTY_SIGINT_CHAR=0x03 +CONFIG_TTY_SIGTSTP=y +CONFIG_UART7_RXBUFSIZE=512 +CONFIG_UART7_RXDMA=y +CONFIG_UART7_TXBUFSIZE=512 +CONFIG_UART7_TXDMA=y +CONFIG_USBDEV=y +CONFIG_USBDEV_BUSPOWERED=y +CONFIG_USBDEV_MAXPOWER=500 +CONFIG_USEC_PER_TICK=1000 +CONFIG_USERMAIN_STACKSIZE=2944 +CONFIG_USER_ENTRYPOINT="bootloader_main" diff --git a/boards/ark/fmu-v6x/nuttx-config/include/board.h b/boards/ark/fmu-v6x/nuttx-config/include/board.h new file mode 100644 index 0000000000..38ce2f9d50 --- /dev/null +++ b/boards/ark/fmu-v6x/nuttx-config/include/board.h @@ -0,0 +1,558 @@ +/************************************************************************************ + * nuttx-configs/px4_fmu-v6x/include/board.h + * + * Copyright (C) 2016-2019 Gregory Nutt. All rights reserved. + * Authors: David Sidrane + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX 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. + * + ************************************************************************************/ +#ifndef __NUTTX_CONFIG_PX4_FMU_V6X_INCLUDE_BOARD_H +#define __NUTTX_CONFIG_PX4_FMU_V6X_INCLUDE_BOARD_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include "board_dma_map.h" + +#include + +#ifndef __ASSEMBLY__ +# include +#endif + +#include "stm32_rcc.h" +#include "stm32_sdmmc.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Clocking *************************************************************************/ +/* The px4_fmu-v6X board provides the following clock sources: + * + * X1: 16 MHz crystal for HSE + * + * So we have these clock source available within the STM32 + * + * HSI: 16 MHz RC factory-trimmed + * HSE: 16 MHz crystal for HSE + */ + +#define STM32_BOARD_XTAL 16000000ul + +#define STM32_HSI_FREQUENCY 16000000ul +#define STM32_LSI_FREQUENCY 32000 +#define STM32_HSE_FREQUENCY STM32_BOARD_XTAL +#define STM32_LSE_FREQUENCY 32768 + +/* Main PLL Configuration. + * + * PLL source is HSE = 16,000,000 + * + * PLL_VCOx = (STM32_HSE_FREQUENCY / PLLM) * PLLN + * Subject to: + * + * 1 <= PLLM <= 63 + * 4 <= PLLN <= 512 + * 150 MHz <= PLL_VCOL <= 420MHz + * 192 MHz <= PLL_VCOH <= 836MHz + * + * SYSCLK = PLL_VCO / PLLP + * CPUCLK = SYSCLK / D1CPRE + * Subject to + * + * PLLP1 = {2, 4, 6, 8, ..., 128} + * PLLP2,3 = {2, 3, 4, ..., 128} + * CPUCLK <= 480 MHz + */ + +#define STM32_BOARD_USEHSE + +#define STM32_PLLCFG_PLLSRC RCC_PLLCKSELR_PLLSRC_HSE + +/* PLL1, wide 4 - 8 MHz input, enable DIVP, DIVQ, DIVR + * + * PLL1_VCO = (16,000,000 / 1) * 60 = 960 MHz + * + * PLL1P = PLL1_VCO/2 = 960 MHz / 2 = 480 MHz + * PLL1Q = PLL1_VCO/4 = 960 MHz / 4 = 240 MHz + * PLL1R = PLL1_VCO/8 = 960 MHz / 8 = 120 MHz + */ + +#define STM32_PLLCFG_PLL1CFG (RCC_PLLCFGR_PLL1VCOSEL_WIDE | \ + RCC_PLLCFGR_PLL1RGE_4_8_MHZ | \ + RCC_PLLCFGR_DIVP1EN | \ + RCC_PLLCFGR_DIVQ1EN | \ + RCC_PLLCFGR_DIVR1EN) +#define STM32_PLLCFG_PLL1M RCC_PLLCKSELR_DIVM1(1) +#define STM32_PLLCFG_PLL1N RCC_PLL1DIVR_N1(60) +#define STM32_PLLCFG_PLL1P RCC_PLL1DIVR_P1(2) +#define STM32_PLLCFG_PLL1Q RCC_PLL1DIVR_Q1(4) +#define STM32_PLLCFG_PLL1R RCC_PLL1DIVR_R1(8) + +#define STM32_VCO1_FREQUENCY ((STM32_HSE_FREQUENCY / 1) * 60) +#define STM32_PLL1P_FREQUENCY (STM32_VCO1_FREQUENCY / 2) +#define STM32_PLL1Q_FREQUENCY (STM32_VCO1_FREQUENCY / 4) +#define STM32_PLL1R_FREQUENCY (STM32_VCO1_FREQUENCY / 8) + +/* PLL2 */ + +#define STM32_PLLCFG_PLL2CFG (RCC_PLLCFGR_PLL2VCOSEL_WIDE | \ + RCC_PLLCFGR_PLL2RGE_4_8_MHZ | \ + RCC_PLLCFGR_DIVP2EN | \ + RCC_PLLCFGR_DIVQ2EN | \ + RCC_PLLCFGR_DIVR2EN) +#define STM32_PLLCFG_PLL2M RCC_PLLCKSELR_DIVM2(4) +#define STM32_PLLCFG_PLL2N RCC_PLL2DIVR_N2(48) +#define STM32_PLLCFG_PLL2P RCC_PLL2DIVR_P2(2) +#define STM32_PLLCFG_PLL2Q RCC_PLL2DIVR_Q2(2) +#define STM32_PLLCFG_PLL2R RCC_PLL2DIVR_R2(2) + +#define STM32_VCO2_FREQUENCY ((STM32_HSE_FREQUENCY / 4) * 48) +#define STM32_PLL2P_FREQUENCY (STM32_VCO2_FREQUENCY / 2) +#define STM32_PLL2Q_FREQUENCY (STM32_VCO2_FREQUENCY / 2) +#define STM32_PLL2R_FREQUENCY (STM32_VCO2_FREQUENCY / 2) + +/* PLL3 */ + +#define STM32_PLLCFG_PLL3CFG (RCC_PLLCFGR_PLL3VCOSEL_WIDE | \ + RCC_PLLCFGR_PLL3RGE_4_8_MHZ | \ + RCC_PLLCFGR_DIVQ3EN) +#define STM32_PLLCFG_PLL3M RCC_PLLCKSELR_DIVM3(4) +#define STM32_PLLCFG_PLL3N RCC_PLL3DIVR_N3(48) +#define STM32_PLLCFG_PLL3P RCC_PLL3DIVR_P3(2) +#define STM32_PLLCFG_PLL3Q RCC_PLL3DIVR_Q3(4) +#define STM32_PLLCFG_PLL3R RCC_PLL3DIVR_R3(2) + +#define STM32_VCO3_FREQUENCY ((STM32_HSE_FREQUENCY / 4) * 48) +#define STM32_PLL3P_FREQUENCY (STM32_VCO3_FREQUENCY / 2) +#define STM32_PLL3Q_FREQUENCY (STM32_VCO3_FREQUENCY / 4) +#define STM32_PLL3R_FREQUENCY (STM32_VCO3_FREQUENCY / 2) + +/* SYSCLK = PLL1P = 480MHz + * CPUCLK = SYSCLK / 1 = 480 MHz + */ + +#define STM32_RCC_D1CFGR_D1CPRE (RCC_D1CFGR_D1CPRE_SYSCLK) +#define STM32_SYSCLK_FREQUENCY (STM32_PLL1P_FREQUENCY) +#define STM32_CPUCLK_FREQUENCY (STM32_SYSCLK_FREQUENCY / 1) + +/* Configure Clock Assignments */ + +/* AHB clock (HCLK) is SYSCLK/2 (240 MHz max) + * HCLK1 = HCLK2 = HCLK3 = HCLK4 = 240 + */ + +#define STM32_RCC_D1CFGR_HPRE RCC_D1CFGR_HPRE_SYSCLKd2 /* HCLK = SYSCLK / 2 */ +#define STM32_ACLK_FREQUENCY (STM32_CPUCLK_FREQUENCY / 2) /* ACLK in D1, HCLK3 in D1 */ +#define STM32_HCLK_FREQUENCY (STM32_CPUCLK_FREQUENCY / 2) /* HCLK in D2, HCLK4 in D3 */ +#define STM32_BOARD_HCLK STM32_HCLK_FREQUENCY /* same as above, to satisfy compiler */ + +/* APB1 clock (PCLK1) is HCLK/2 (120 MHz) */ + +#define STM32_RCC_D2CFGR_D2PPRE1 RCC_D2CFGR_D2PPRE1_HCLKd2 /* PCLK1 = HCLK / 2 */ +#define STM32_PCLK1_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* APB2 clock (PCLK2) is HCLK/2 (120 MHz) */ + +#define STM32_RCC_D2CFGR_D2PPRE2 RCC_D2CFGR_D2PPRE2_HCLKd2 /* PCLK2 = HCLK / 2 */ +#define STM32_PCLK2_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* APB3 clock (PCLK3) is HCLK/2 (120 MHz) */ + +#define STM32_RCC_D1CFGR_D1PPRE RCC_D1CFGR_D1PPRE_HCLKd2 /* PCLK3 = HCLK / 2 */ +#define STM32_PCLK3_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* APB4 clock (PCLK4) is HCLK/4 (120 MHz) */ + +#define STM32_RCC_D3CFGR_D3PPRE RCC_D3CFGR_D3PPRE_HCLKd2 /* PCLK4 = HCLK / 2 */ +#define STM32_PCLK4_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* Timer clock frequencies */ + +/* Timers driven from APB1 will be twice PCLK1 */ + +#define STM32_APB1_TIM2_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM3_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM4_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM5_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM6_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM7_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM12_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM13_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM14_CLKIN (2*STM32_PCLK1_FREQUENCY) + +/* Timers driven from APB2 will be twice PCLK2 */ + +#define STM32_APB2_TIM1_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM8_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM15_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM16_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM17_CLKIN (2*STM32_PCLK2_FREQUENCY) + +/* Kernel Clock Configuration + * + * Note: look at Table 54 in ST Manual + */ + +/* I2C123 clock source */ + +#define STM32_RCC_D2CCIP2R_I2C123SRC RCC_D2CCIP2R_I2C123SEL_HSI + +/* I2C4 clock source */ + +#define STM32_RCC_D3CCIPR_I2C4SRC RCC_D3CCIPR_I2C4SEL_HSI + +/* SPI123 clock source */ + +#define STM32_RCC_D2CCIP1R_SPI123SRC RCC_D2CCIP1R_SPI123SEL_PLL2 + +/* SPI45 clock source */ + +#define STM32_RCC_D2CCIP1R_SPI45SRC RCC_D2CCIP1R_SPI45SEL_PLL2 + +/* SPI6 clock source */ + +#define STM32_RCC_D3CCIPR_SPI6SRC RCC_D3CCIPR_SPI6SEL_PLL2 + +/* USB 1 and 2 clock source */ + +#define STM32_RCC_D2CCIP2R_USBSRC RCC_D2CCIP2R_USBSEL_PLL3 + +/* ADC 1 2 3 clock source */ + +#define STM32_RCC_D3CCIPR_ADCSEL RCC_D3CCIPR_ADCSEL_PLL2 + +/* FDCAN 1 2 clock source */ + +#define STM32_RCC_D2CCIP1R_FDCANSEL RCC_D2CCIP1R_FDCANSEL_HSE /* FDCAN 1 2 clock source */ + +#define STM32_FDCANCLK STM32_HSE_FREQUENCY + +/* FLASH wait states + * + * ------------ ---------- ----------- + * Vcore MAX ACLK WAIT STATES + * ------------ ---------- ----------- + * 1.15-1.26 V 70 MHz 0 + * (VOS1 level) 140 MHz 1 + * 210 MHz 2 + * 1.05-1.15 V 55 MHz 0 + * (VOS2 level) 110 MHz 1 + * 165 MHz 2 + * 220 MHz 3 + * 0.95-1.05 V 45 MHz 0 + * (VOS3 level) 90 MHz 1 + * 135 MHz 2 + * 180 MHz 3 + * 225 MHz 4 + * ------------ ---------- ----------- + */ + +#define BOARD_FLASH_WAITSTATES 2 + +/* SDMMC definitions ********************************************************/ + +/* Init 400kHz, freq = PLL1Q/(2*div) div = PLL1Q/(2*freq) */ + +#define STM32_SDMMC_INIT_CLKDIV (300 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) + +/* 25 MHz Max for now, 25 mHZ = PLL1Q/(2*div), div = PLL1Q/(2*freq) + * div = 4.8 = 240 / 50, So round up to 5 for default speed 24 MB/s + */ + +#if defined(CONFIG_STM32H7_SDMMC_XDMA) || defined(CONFIG_STM32H7_SDMMC_IDMA) +# define STM32_SDMMC_MMCXFR_CLKDIV (5 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#else +# define STM32_SDMMC_MMCXFR_CLKDIV (100 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#endif +#if defined(CONFIG_STM32H7_SDMMC_XDMA) || defined(CONFIG_STM32H7_SDMMC_IDMA) +# define STM32_SDMMC_SDXFR_CLKDIV (5 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#else +# define STM32_SDMMC_SDXFR_CLKDIV (100 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#endif + +#define STM32_SDMMC_CLKCR_EDGE STM32_SDMMC_CLKCR_NEGEDGE + +/* LED definitions ******************************************************************/ +/* The PX4 FMUV6X board has three, LED_GREEN a Green LED, LED_BLUE a Blue LED and + * LED_RED a Red LED, that can be controlled by software. + * + * If CONFIG_ARCH_LEDS is not defined, then the user can control the LEDs in any way. + * The following definitions are used to access individual LEDs. + */ + +/* LED index values for use with board_userled() */ + +/* LED definitions ******************************************************************/ +/* The px4_fmu-v6x board has three, LED_GREEN a Green LED, LED_BLUE a Blue LED and + * LED_RED a Red LED, that can be controlled by software. + * + * If CONFIG_ARCH_LEDS is not defined, then the user can control the LEDs in any way. + * The following definitions are used to access individual LEDs. + */ + +/* LED index values for use with board_userled() */ + +#define BOARD_LED1 0 +#define BOARD_LED2 1 +#define BOARD_LED3 2 +#define BOARD_NLEDS 3 + +#define BOARD_LED_RED BOARD_LED1 +#define BOARD_LED_GREEN BOARD_LED2 +#define BOARD_LED_BLUE BOARD_LED3 + +/* LED bits for use with board_userled_all() */ + +#define BOARD_LED1_BIT (1 << BOARD_LED1) +#define BOARD_LED2_BIT (1 << BOARD_LED2) +#define BOARD_LED3_BIT (1 << BOARD_LED3) + +/* If CONFIG_ARCH_LEDS is defined, the usage by the board port is defined in + * include/board.h and src/stm32_leds.c. The LEDs are used to encode OS-related + * events as follows: + * + * + * SYMBOL Meaning LED state + * Red Green Blue + * ---------------------- -------------------------- ------ ------ ----*/ + +#define LED_STARTED 0 /* NuttX has been started OFF OFF OFF */ +#define LED_HEAPALLOCATE 1 /* Heap has been allocated OFF OFF ON */ +#define LED_IRQSENABLED 2 /* Interrupts enabled OFF ON OFF */ +#define LED_STACKCREATED 3 /* Idle stack created OFF ON ON */ +#define LED_INIRQ 4 /* In an interrupt N/C N/C GLOW */ +#define LED_SIGNAL 5 /* In a signal handler N/C GLOW N/C */ +#define LED_ASSERTION 6 /* An assertion failed GLOW N/C GLOW */ +#define LED_PANIC 7 /* The system has crashed Blink OFF N/C */ +#define LED_IDLE 8 /* MCU is is sleep mode ON OFF OFF */ + +/* Thus if the Green LED is statically on, NuttX has successfully booted and + * is, apparently, running normally. If the Red LED is flashing at + * approximately 2Hz, then a fatal error has been detected and the system + * has halted. + */ + +/* Alternate function pin selections ************************************************/ + +#define GPIO_USART1_RX GPIO_USART1_RX_3 /* PB7 */ +#define GPIO_USART1_TX GPIO_USART1_TX_3 /* PB6 */ + +#define GPIO_USART2_RX GPIO_USART2_RX_1 /* PA3 */ +#define GPIO_USART2_TX GPIO_USART2_TX_2 /* PD5 */ +#define GPIO_USART2_RTS GPIO_USART2_RTS_2 /* PD4 */ +#define GPIO_USART2_CTS GPIO_USART2_CTS_NSS_2 /* PD3 */ + +#define GPIO_USART3_RX GPIO_USART3_RX_3 /* PD9 */ +#define GPIO_USART3_TX GPIO_USART3_TX_3 /* PD8 */ + +#define GPIO_UART4_RX GPIO_UART4_RX_6 /* PH14 */ +#define GPIO_UART4_TX GPIO_UART4_TX_6 /* PH13 */ + +#define GPIO_UART5_RX GPIO_UART5_RX_3 /* PD2 */ +#define GPIO_UART5_TX GPIO_UART5_TX_3 /* PC12 */ +// GPIO_UART5_RTS no remap /* PC8 */ +// GPIO_UART5_CTS No remap /* PC9 */ + +#define GPIO_USART6_RX GPIO_USART6_RX_1 /* PC7 */ +#define GPIO_USART6_TX GPIO_USART6_TX_1 /* PC6 */ + +#define GPIO_UART7_RX GPIO_UART7_RX_4 /* PF6 */ +#define GPIO_UART7_TX GPIO_UART7_TX_3 /* PE8 */ +#define GPIO_UART7_RTS GPIO_UART7_RTS_2 /* PF8 */ +#define GPIO_UART7_CTS GPIO_UART7_CTS_1 /* PE10 */ + +#define GPIO_UART8_RX GPIO_UART8_RX_1 /* PE0 */ +#define GPIO_UART8_TX GPIO_UART8_TX_1 /* PE1 */ + + +/* CAN + * + * CAN1 is routed to transceiver. + * CAN2 is routed to transceiver. + */ +#define GPIO_CAN1_RX GPIO_CAN1_RX_3 /* PD0 */ +#define GPIO_CAN1_TX GPIO_CAN1_TX_3 /* PD1 */ +#define GPIO_CAN2_RX GPIO_CAN2_RX_1 /* PB12 */ +#define GPIO_CAN2_TX GPIO_CAN2_TX_1 /* PB13 */ + +/* SPI + * SPI1 is sensors1 + * SPI2 is sensors2 + * SPI3 is sensors3 + * SPI4 is Not Used + * SPI5 is FRAM + * SPI6 is EXTERNAL1 + * + */ + +#define ADJ_SLEW_RATE(p) (((p) & ~GPIO_SPEED_MASK) | (GPIO_SPEED_2MHz)) + +#define GPIO_SPI1_MISO GPIO_SPI1_MISO_3 /* PG9 */ +#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_2 /* PB5 */ +#define GPIO_SPI1_SCK ADJ_SLEW_RATE(GPIO_SPI1_SCK_1) /* PA5 */ + +#define GPIO_SPI2_MISO GPIO_SPI2_MISO_3 /* PI2 */ +#define GPIO_SPI2_MOSI GPIO_SPI2_MOSI_4 /* PI3 */ +#define GPIO_SPI2_SCK ADJ_SLEW_RATE(GPIO_SPI2_SCK_6) /* PI1 */ + +#define GPIO_SPI3_MISO GPIO_SPI3_MISO_2 /* PC11 */ +#define GPIO_SPI3_MOSI GPIO_SPI3_MOSI_3 /* PB2 */ +#define GPIO_SPI3_SCK ADJ_SLEW_RATE(GPIO_SPI3_SCK_2) /* PC10 */ + +#define GPIO_SPI5_MISO GPIO_SPI5_MISO_2 /* PH7 */ +#define GPIO_SPI5_MOSI GPIO_SPI5_MOSI_1 /* PF11 */ +#define GPIO_SPI5_SCK ADJ_SLEW_RATE(GPIO_SPI5_SCK_1) /* PF7 */ + +#define GPIO_SPI6_MISO GPIO_SPI6_MISO_2 /* PA6 */ +#define GPIO_SPI6_MOSI GPIO_SPI6_MOSI_1 /* PG14 */ +#define GPIO_SPI6_SCK ADJ_SLEW_RATE(GPIO_SPI6_SCK_3) /* PB3 */ + +/* I2C + * + * The optional _GPIO configurations allow the I2C driver to manually + * reset the bus to clear stuck slaves. They match the pin configuration, + * but are normally-high GPIOs. + * + */ + +#define GPIO_I2C1_SCL GPIO_I2C1_SCL_2 /* PB8 */ +#define GPIO_I2C1_SDA GPIO_I2C1_SDA_2 /* PB9 */ + +#define GPIO_I2C1_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTB | GPIO_PIN8) +#define GPIO_I2C1_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTB | GPIO_PIN9) + +#define GPIO_I2C2_SCL GPIO_I2C2_SCL_2 /* PF1 */ +#define GPIO_I2C2_SDA GPIO_I2C2_SDA_2 /* PF0 */ + +#define GPIO_I2C2_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN1) +#define GPIO_I2C2_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN0) + +#define GPIO_I2C3_SCL GPIO_I2C3_SCL_1 /* PA8 */ +#define GPIO_I2C3_SDA GPIO_I2C3_SDA_2 /* PH8 */ + +#define GPIO_I2C3_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTA | GPIO_PIN8) +#define GPIO_I2C3_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTH | GPIO_PIN8) + +#define GPIO_I2C4_SCL GPIO_I2C4_SCL_2 /* PF14 */ +#define GPIO_I2C4_SDA GPIO_I2C4_SDA_2 /* PF15 */ + +#define GPIO_I2C4_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN | GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN14) +#define GPIO_I2C4_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN | GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN15) + +/* SDMMC2 + * + * VDD 3.3 + * GND + * SDMMC2_CK PD6 + * SDMMC2_CMD PD7 + * SDMMC2_D0 PB14 + * SDMMC2_D1 PB15 + * SDMMC2_D2 PG11 + * SDMMC2_D3 PB4 + */ + +#define GPIO_SDMMC2_CK GPIO_SDMMC2_CK_1 /* PD6 */ +#define GPIO_SDMMC2_CMD GPIO_SDMMC2_CMD_1 /* PD7 */ +// GPIO_SDMMC2_D0 No Remap /* PB14 */ +// GPIO_SDMMC2_D1 No Remap /* PB15 */ +#define GPIO_SDMMC2_D2 GPIO_SDMMC2_D2_1 /* PG11 */ +// GPIO_SDMMC2_D3 No Remap /* PB4 */ + +/* The STM32 H7 connects to a TI DP83848TSQ/NOPB + * using RMII + * + * STM32 H7 BOARD DP83848TSQ/NOPB + * GPIO SIGNAL PIN NAME + * -------- ------------ ------------- + * PA7 ETH_CRS_DV CRS_DV + * PC1 ETH_MDC MDC + * PA2 ETH_MDIO MDIO + * PA1 ETH_REF_CL X1 + * PC4 ETH_RXD0 RX_D0 + * PC5 ETH_RXD1 RX_D1 + * PB11 ETH_TX_EN TX_EN + * PG13 ETH_TXD0 TX_D0 + * PG12 ETH_TXD1 TX_D1 + * + * The PHY address is 1, since COL/PHYAD0 features a pull up. + */ + +#define GPIO_ETH_RMII_TX_EN GPIO_ETH_RMII_TX_EN_1 /* PB11 */ +#define GPIO_ETH_RMII_TXD0 GPIO_ETH_RMII_TXD0_2 /* PG13 */ +#define GPIO_ETH_RMII_TXD1 GPIO_ETH_RMII_TXD1_2 /* PG12 */ + + +/* USB + * + * OTG_FS_DM PA11 + * OTG_FS_DP PA12 + * VBUS PA9 + */ + + +/* Board provides GPIO or other Hardware for signaling to timing analyzer */ + +#if defined(CONFIG_BOARD_USE_PROBES) +# include "stm32_gpio.h" +# define PROBE_N(n) (1<<((n)-1)) +# define PROBE_1 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTI|GPIO_PIN0) /* PI0 AUX1 */ +# define PROBE_2 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN12) /* PH12 AUX2 */ +# define PROBE_3 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN11) /* PH11 AUX3 */ +# define PROBE_4 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN10) /* PH10 AUX4 */ +# define PROBE_5 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN13) /* PD13 AUX5 */ +# define PROBE_6 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN14) /* PD14 AUX6 */ +# define PROBE_7 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN6) /* PH6 AUX7 */ +# define PROBE_8 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN9) /* PH9 AUX8 */ +# define PROBE_9 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN11) /* PE11 CAP1 */ + +# define PROBE_INIT(mask) \ + do { \ + if ((mask)& PROBE_N(1)) { stm32_configgpio(PROBE_1); } \ + if ((mask)& PROBE_N(2)) { stm32_configgpio(PROBE_2); } \ + if ((mask)& PROBE_N(3)) { stm32_configgpio(PROBE_3); } \ + if ((mask)& PROBE_N(4)) { stm32_configgpio(PROBE_4); } \ + if ((mask)& PROBE_N(5)) { stm32_configgpio(PROBE_5); } \ + if ((mask)& PROBE_N(6)) { stm32_configgpio(PROBE_6); } \ + if ((mask)& PROBE_N(7)) { stm32_configgpio(PROBE_7); } \ + if ((mask)& PROBE_N(8)) { stm32_configgpio(PROBE_8); } \ + if ((mask)& PROBE_N(9)) { stm32_configgpio(PROBE_9); } \ + } while(0) + +# define PROBE(n,s) do {stm32_gpiowrite(PROBE_##n,(s));}while(0) +# define PROBE_MARK(n) PROBE(n,false);PROBE(n,true) +#else +# define PROBE_INIT(mask) +# define PROBE(n,s) +# define PROBE_MARK(n) +#endif + +#endif /*__NUTTX_CONFIG_PX4_FMU_V6X_INCLUDE_BOARD_H */ diff --git a/boards/ark/fmu-v6x/nuttx-config/include/board_dma_map.h b/boards/ark/fmu-v6x/nuttx-config/include/board_dma_map.h new file mode 100644 index 0000000000..1f45efc569 --- /dev/null +++ b/boards/ark/fmu-v6x/nuttx-config/include/board_dma_map.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * Copyright (c) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +#pragma once + +// DMAMUX1 Using at most 8 Channels on DMA1 -------- Assigned +// V + +#define DMAMAP_SPI1_RX DMAMAP_DMA12_SPI1RX_0 /* 1 DMA1:37 ICM-20649 */ +#define DMAMAP_SPI1_TX DMAMAP_DMA12_SPI1TX_0 /* 2 DMA1:38 ICM-20649 */ + +#define DMAMAP_SPI2_RX DMAMAP_DMA12_SPI2RX_0 /* 3 DMA1:39 ICM-42688-P */ +#define DMAMAP_SPI2_TX DMAMAP_DMA12_SPI2TX_0 /* 4 DMA1:40 ICM-42688-P */ + +//#define DMAMAP_USART1_RX DMAMAP_DMA12_USART1RX_0 /* DMA1:41 GPS1 */ +//#define DMAMAP_USART1_TX DMAMAP_DMA12_USART1TX_0 /* DMA1:42 GPS1 */ + +//#define DMAMAP_USART2_RX DMAMAP_DMA12_USART2RX_0 /* DMA1:43 Telem3 */ +//#define DMAMAP_USART2_TX DMAMAP_DMA12_USART2TX_0 /* DMA1:44 Telem3 */ + +//#define DMAMAP_USART3_RX DMAMAP_DMA12_USART3RX_0 /* DMA1:45 DEBUG */ +//#define DMAMAP_USART3_TX DMAMAP_DMA12_USART3TX_0 /* DMA1:46 DEBUG */ + +//#define DMAMAP_UART4_RX DMAMAP_DMA12_UART4RX_0 /* DMA1:63 EXT2 */ +//#define DMAMAP_UART4_TX DMAMAP_DMA12_UART4TX_0 /* DMA1:64 EXT2 */ + +#define DMAMAP_USART6_RX DMAMAP_DMA12_USART6RX_0 /* 5 DMA1:71 PX4IO */ +#define DMAMAP_USART6_TX DMAMAP_DMA12_USART6TX_0 /* 6 DMA1:72 PX4IO */ + +// Assigned in timer_config.cpp + +// Timer 4 /* 7 DMA1:32 TIM4UP */ +// Timer 5 /* 8 DMA1:50 TIM5UP */ + +// DMAMUX2 Using at most 8 Channels on DMA2 -------- Assigned +// V + +#define DMAMAP_SPI3_RX DMAMAP_DMA12_SPI3RX_1 /* 1 DMA2:61 BMI088 */ +#define DMAMAP_SPI3_TX DMAMAP_DMA12_SPI3TX_1 /* 2 DMA2:62 BMI088 */ + +#define DMAMAP_USART3_RX DMAMAP_DMA12_USART3RX_1 /* 3 DMA2:45 DEBUG */ +#define DMAMAP_USART3_TX DMAMAP_DMA12_USART3TX_1 /* 4 DMA2:46 DEBUG */ + +#define DMAMAP_UART5_RX DMAMAP_DMA12_UART5RX_1 /* 5 DMA2:65 TELEM2 */ +#define DMAMAP_UART5_TX DMAMAP_DMA12_UART5TX_1 /* 6 DMA2:66 TELEM2 */ + +#define DMAMAP_UART7_RX DMAMAP_DMA12_UART7RX_1 /* 7 DMA1:79 TELEM1 */ +#define DMAMAP_UART7_TX DMAMAP_DMA12_UART7TX_1 /* 8 DMA1:80 TELEM1 */ + +//#define DMAMAP_UART8_RX DMAMAP_DMA12_UART8RX_1 /* DMA1:81 GPS2 */ +//#define DMAMAP_UART8_TX DMAMAP_DMA12_UART8TX_1 /* DMA1:82 GPS2 */ + +// DMAMUX2 Using at most 8 Channels on BDMA -------- Assigned +// V + +#define DMAMAP_SPI6_RX DMAMAP_BDMA_SPI6_RX /* 1 BDMA:11 SPI J11 */ +#define DMAMAP_SPI6_TX DMAMAP_BDMA_SPI6_TX /* 2 BDMA:12 SPI J11 */ diff --git a/boards/ark/fmu-v6x/nuttx-config/nsh/defconfig b/boards/ark/fmu-v6x/nuttx-config/nsh/defconfig new file mode 100644 index 0000000000..082db6c827 --- /dev/null +++ b/boards/ark/fmu-v6x/nuttx-config/nsh/defconfig @@ -0,0 +1,295 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_DISABLE_ENVIRON is not set +# CONFIG_DISABLE_PSEUDOFS_OPERATIONS is not set +# CONFIG_MMCSD_HAVE_CARDDETECT is not set +# CONFIG_MMCSD_HAVE_WRITEPROTECT is not set +# CONFIG_MMCSD_MMCSUPPORT is not set +# CONFIG_MMCSD_SPI is not set +# CONFIG_NSH_DISABLEBG is not set +# CONFIG_NSH_DISABLESCRIPT is not set +# CONFIG_NSH_DISABLE_ARP is not set +# CONFIG_NSH_DISABLE_DF is not set +# CONFIG_NSH_DISABLE_EXEC is not set +# CONFIG_NSH_DISABLE_GET is not set +# CONFIG_NSH_DISABLE_ITEF is not set +# CONFIG_NSH_DISABLE_LOOPS is not set +# CONFIG_NSH_DISABLE_MKFATFS is not set +# CONFIG_NSH_DISABLE_SEMICOLON is not set +# CONFIG_NSH_DISABLE_TIME is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD_CUSTOM=y +CONFIG_ARCH_BOARD_CUSTOM_DIR="../../../../boards/ark/fmu-v6x/nuttx-config" +CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y +CONFIG_ARCH_BOARD_CUSTOM_NAME="px4" +CONFIG_ARCH_CHIP="stm32h7" +CONFIG_ARCH_CHIP_STM32H753II=y +CONFIG_ARCH_CHIP_STM32H7=y +CONFIG_ARCH_INTERRUPTSTACK=512 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV7M_BASEPRI_WAR=y +CONFIG_ARMV7M_DCACHE=y +CONFIG_ARMV7M_DTCM=y +CONFIG_ARMV7M_ICACHE=y +CONFIG_ARMV7M_MEMCPY=y +CONFIG_ARMV7M_USEBASEPRI=y +CONFIG_ARM_MPU_EARLY_RESET=y +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_CRASHDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=95751 +CONFIG_BOARD_RESET_ON_ASSERT=2 +CONFIG_BUILTIN=y +CONFIG_C99_BOOL8=y +CONFIG_CDCACM=y +CONFIG_CDCACM_IFLOWCONTROL=y +CONFIG_CDCACM_PRODUCTID=0x0035 +CONFIG_CDCACM_PRODUCTSTR="ARK FMU v6X.x" +CONFIG_CDCACM_RXBUFSIZE=600 +CONFIG_CDCACM_TXBUFSIZE=12000 +CONFIG_CDCACM_VENDORID=0x3185 +CONFIG_CDCACM_VENDORSTR="ARK" +CONFIG_CLOCK_MONOTONIC=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_HARDFAULT_ALERT=y +CONFIG_DEBUG_MEMFAULT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_SMALL=y +CONFIG_DEV_FIFO_SIZE=0 +CONFIG_DEV_PIPE_MAXSIZE=1024 +CONFIG_DEV_PIPE_SIZE=70 +CONFIG_ETH0_PHY_LAN8742A=y +CONFIG_EXPERIMENTAL=y +CONFIG_FAT_DMAMEMORY=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FAT_LFN_ALIAS_HASH=y +CONFIG_FDCLONE_STDIO=y +CONFIG_FSUTILS_IPCFG=y +CONFIG_FS_BINFS=y +CONFIG_FS_CROMFS=y +CONFIG_FS_FAT=y +CONFIG_FS_FATTIME=y +CONFIG_FS_PROCFS=y +CONFIG_FS_PROCFS_INCLUDE_PROGMEM=y +CONFIG_FS_PROCFS_MAX_TASKS=64 +CONFIG_FS_PROCFS_REGISTER=y +CONFIG_FS_ROMFS=y +CONFIG_GRAN=y +CONFIG_GRAN_INTR=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_I2C=y +CONFIG_I2C_RESET=y +CONFIG_IDLETHREAD_STACKSIZE=750 +CONFIG_IOB_NBUFFERS=24 +CONFIG_IOB_THROTTLE=0 +CONFIG_IPCFG_BINARY=y +CONFIG_IPCFG_CHARDEV=y +CONFIG_IPCFG_PATH="/fs/mtd_net" +CONFIG_LIBC_FLOATINGPOINT=y +CONFIG_LIBC_LONG_LONG=y +CONFIG_LIBC_STRERROR=y +CONFIG_MEMSET_64BIT=y +CONFIG_MEMSET_OPTSPEED=y +CONFIG_MMCSD=y +CONFIG_MMCSD_SDIO=y +CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE=y +CONFIG_MM_REGIONS=4 +CONFIG_MTD=y +CONFIG_MTD_BYTE_WRITE=y +CONFIG_MTD_PARTITION=y +CONFIG_MTD_PROGMEM=y +CONFIG_MTD_RAMTRON=y +CONFIG_NAME_MAX=40 +CONFIG_NET=y +CONFIG_NETDB_DNSCLIENT=y +CONFIG_NETDB_DNSCLIENT_ENTRIES=8 +CONFIG_NETDB_DNSSERVER_NOADDR=y +CONFIG_NETDEV_PHY_IOCTL=y +CONFIG_NETINIT_DHCPC=y +CONFIG_NETINIT_DNS=y +CONFIG_NETINIT_DNSIPADDR=0XC0A800FE +CONFIG_NETINIT_DRIPADDR=0XC0A800FE +CONFIG_NETINIT_THREAD=y +CONFIG_NETINIT_THREAD_PRIORITY=49 +CONFIG_NETUTILS_TELNETD=y +CONFIG_NET_ARP_IPIN=y +CONFIG_NET_ARP_SEND=y +CONFIG_NET_BROADCAST=y +CONFIG_NET_ETH_PKTSIZE=1518 +CONFIG_NET_ICMP=y +CONFIG_NET_ICMP_SOCKET=y +CONFIG_NET_SOLINGER=y +CONFIG_NET_TCP=y +CONFIG_NET_TCPBACKLOG=y +CONFIG_NET_TCP_DELAYED_ACK=y +CONFIG_NET_TCP_WRITE_BUFFERS=y +CONFIG_NET_UDP=y +CONFIG_NET_UDP_CHECKSUMS=y +CONFIG_NET_UDP_WRITE_BUFFERS=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_ARGCAT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_CMDPARMS=y +CONFIG_NSH_CROMFSETC=y +CONFIG_NSH_LINELEN=128 +CONFIG_NSH_MAXARGUMENTS=15 +CONFIG_NSH_NESTDEPTH=8 +CONFIG_NSH_QUOTE=y +CONFIG_NSH_ROMFSETC=y +CONFIG_NSH_ROMFSSECTSIZE=128 +CONFIG_NSH_STRERROR=y +CONFIG_NSH_TELNET=y +CONFIG_NSH_TELNET_LOGIN=y +CONFIG_NSH_VARS=y +CONFIG_OTG_ID_GPIO_DISABLE=y +CONFIG_PIPES=y +CONFIG_PREALLOC_TIMERS=50 +CONFIG_PRIORITY_INHERITANCE=y +CONFIG_PTHREAD_MUTEX_ROBUST=y +CONFIG_PTHREAD_STACK_MIN=512 +CONFIG_RAMTRON_SETSPEED=y +CONFIG_RAM_SIZE=245760 +CONFIG_RAM_START=0x20010000 +CONFIG_RAW_BINARY=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_READLINE_TABCOMPLETION=y +CONFIG_RTC_DATETIME=y +CONFIG_SCHED_ATEXIT=y +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=249 +CONFIG_SCHED_HPWORKSTACKSIZE=1280 +CONFIG_SCHED_INSTRUMENTATION=y +CONFIG_SCHED_INSTRUMENTATION_EXTERNAL=y +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_LPWORKPRIORITY=50 +CONFIG_SCHED_LPWORKSTACKSIZE=1632 +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_SDMMC2_SDIO_PULLUP=y +CONFIG_SEM_PREALLOCHOLDERS=32 +CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS=y +CONFIG_SERIAL_TERMIOS=y +CONFIG_SIG_DEFAULT=y +CONFIG_SIG_SIGALRM_ACTION=y +CONFIG_SIG_SIGUSR1_ACTION=y +CONFIG_SIG_SIGUSR2_ACTION=y +CONFIG_SIG_SIGWORK=4 +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=30 +CONFIG_START_MONTH=11 +CONFIG_STDIO_BUFFER_SIZE=256 +CONFIG_STM32H7_ADC1=y +CONFIG_STM32H7_ADC3=y +CONFIG_STM32H7_BBSRAM=y +CONFIG_STM32H7_BBSRAM_FILES=5 +CONFIG_STM32H7_BDMA=y +CONFIG_STM32H7_BKPSRAM=y +CONFIG_STM32H7_DMA1=y +CONFIG_STM32H7_DMA2=y +CONFIG_STM32H7_DMACAPABLE=y +CONFIG_STM32H7_ETHMAC=y +CONFIG_STM32H7_FLASH_OVERRIDE_I=y +CONFIG_STM32H7_FLOWCONTROL_BROKEN=y +CONFIG_STM32H7_I2C1=y +CONFIG_STM32H7_I2C2=y +CONFIG_STM32H7_I2C3=y +CONFIG_STM32H7_I2C4=y +CONFIG_STM32H7_I2C_DYNTIMEO=y +CONFIG_STM32H7_I2C_DYNTIMEO_STARTSTOP=10 +CONFIG_STM32H7_OTGFS=y +CONFIG_STM32H7_PHYSR=31 +CONFIG_STM32H7_PHYSR_100MBPS=0x8 +CONFIG_STM32H7_PHYSR_FULLDUPLEX=0x10 +CONFIG_STM32H7_PHYSR_MODE=0x10 +CONFIG_STM32H7_PHYSR_SPEED=0x8 +CONFIG_STM32H7_PROGMEM=y +CONFIG_STM32H7_RTC=y +CONFIG_STM32H7_RTC_AUTO_LSECLOCK_START_DRV_CAPABILITY=y +CONFIG_STM32H7_RTC_MAGIC_REG=1 +CONFIG_STM32H7_SAVE_CRASHDUMP=y +CONFIG_STM32H7_SDMMC2=y +CONFIG_STM32H7_SERIALBRK_BSDCOMPAT=y +CONFIG_STM32H7_SERIAL_DISABLE_REORDERING=y +CONFIG_STM32H7_SPI1=y +CONFIG_STM32H7_SPI1_DMA=y +CONFIG_STM32H7_SPI1_DMA_BUFFER=1024 +CONFIG_STM32H7_SPI2=y +CONFIG_STM32H7_SPI2_DMA=y +CONFIG_STM32H7_SPI2_DMA_BUFFER=4096 +CONFIG_STM32H7_SPI3=y +CONFIG_STM32H7_SPI3_DMA=y +CONFIG_STM32H7_SPI3_DMA_BUFFER=1024 +CONFIG_STM32H7_SPI5=y +CONFIG_STM32H7_SPI6=y +CONFIG_STM32H7_SPI6_DMA=y +CONFIG_STM32H7_SPI6_DMA_BUFFER=1024 +CONFIG_STM32H7_SPI_DMA=y +CONFIG_STM32H7_TIM12=y +CONFIG_STM32H7_TIM1=y +CONFIG_STM32H7_TIM4=y +CONFIG_STM32H7_TIM5=y +CONFIG_STM32H7_UART4=y +CONFIG_STM32H7_UART5=y +CONFIG_STM32H7_UART7=y +CONFIG_STM32H7_UART8=y +CONFIG_STM32H7_USART1=y +CONFIG_STM32H7_USART2=y +CONFIG_STM32H7_USART3=y +CONFIG_STM32H7_USART6=y +CONFIG_STM32H7_USART_BREAKS=y +CONFIG_STM32H7_USART_INVERT=y +CONFIG_STM32H7_USART_SINGLEWIRE=y +CONFIG_STM32H7_USART_SWAP=y +CONFIG_SYSTEM_CDCACM=y +CONFIG_SYSTEM_DHCPC_RENEW=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_PING=y +CONFIG_TASK_NAME_SIZE=24 +CONFIG_UART4_BAUD=57600 +CONFIG_UART4_RXBUFSIZE=600 +CONFIG_UART4_TXBUFSIZE=1500 +CONFIG_UART5_IFLOWCONTROL=y +CONFIG_UART5_OFLOWCONTROL=y +CONFIG_UART5_RXDMA=y +CONFIG_UART5_TXBUFSIZE=3000 +CONFIG_UART5_TXDMA=y +CONFIG_UART7_BAUD=57600 +CONFIG_UART7_IFLOWCONTROL=y +CONFIG_UART7_OFLOWCONTROL=y +CONFIG_UART7_RXBUFSIZE=600 +CONFIG_UART7_RXDMA=y +CONFIG_UART7_TXBUFSIZE=3000 +CONFIG_UART7_TXDMA=y +CONFIG_UART8_BAUD=57600 +CONFIG_UART8_RXBUFSIZE=600 +CONFIG_UART8_TXBUFSIZE=1500 +CONFIG_USART1_BAUD=57600 +CONFIG_USART1_RXBUFSIZE=600 +CONFIG_USART1_TXBUFSIZE=1500 +CONFIG_USART2_BAUD=57600 +CONFIG_USART2_IFLOWCONTROL=y +CONFIG_USART2_OFLOWCONTROL=y +CONFIG_USART2_RXBUFSIZE=600 +CONFIG_USART2_TXBUFSIZE=3000 +CONFIG_USART3_BAUD=57600 +CONFIG_USART3_RXBUFSIZE=180 +CONFIG_USART3_RXDMA=y +CONFIG_USART3_SERIAL_CONSOLE=y +CONFIG_USART3_TXBUFSIZE=1500 +CONFIG_USART3_TXDMA=y +CONFIG_USART6_BAUD=57600 +CONFIG_USART6_RXBUFSIZE=600 +CONFIG_USART6_TXBUFSIZE=1500 +CONFIG_USBDEV=y +CONFIG_USBDEV_BUSPOWERED=y +CONFIG_USBDEV_MAXPOWER=500 +CONFIG_USEC_PER_TICK=1000 +CONFIG_USERMAIN_STACKSIZE=2944 +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_WATCHDOG=y diff --git a/boards/ark/fmu-v6x/nuttx-config/scripts/bootloader_script.ld b/boards/ark/fmu-v6x/nuttx-config/scripts/bootloader_script.ld new file mode 100644 index 0000000000..2e6eba3607 --- /dev/null +++ b/boards/ark/fmu-v6x/nuttx-config/scripts/bootloader_script.ld @@ -0,0 +1,215 @@ +/**************************************************************************** + * scripts/script.ld + * + * Copyright (C) 2016, 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX 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. + * + ****************************************************************************/ + +/* The PX4 FMUV6X uses an STM32H753II has 2048Kb of main FLASH memory. + * The flash memory is partitioned into a User Flash memory and a System + * Flash memory. Each of these memories has two banks: + * + * 1) User Flash memory: + * + * Bank 1: Start address 0x0800:0000 to 0x080F:FFFF with 8 sectors, 128Kb each + * Bank 2: Start address 0x0810:0000 to 0x081F:FFFF with 8 sectors, 128Kb each + * + * 2) System Flash memory: + * + * Bank 1: Start address 0x1FF0:0000 to 0x1FF1:FFFF with 1 x 128Kb sector + * Bank 1: Start address 0x1FF4:0000 to 0x1FF5:FFFF with 1 x 128Kb sector + * + * 3) User option bytes for user configuration, only in Bank 1. + * + * In the STM32H743II, two different boot spaces can be selected through + * the BOOT pin and the boot base address programmed in the BOOT_ADD0 and + * BOOT_ADD1 option bytes: + * + * 1) BOOT=0: Boot address defined by user option byte BOOT_ADD0[15:0]. + * ST programmed value: Flash memory at 0x0800:0000 + * 2) BOOT=1: Boot address defined by user option byte BOOT_ADD1[15:0]. + * ST programmed value: System bootloader at 0x1FF0:0000 + * + * The PX4 FMUV6X has a test point on board, the BOOT0 pin is at ground so by + * default, the STM32 will boot to address 0x0800:0000 in FLASH unless the test + * point is pulled to 3.3V.then the boot will be from 0x1FF0:0000 + * + * The STM32H743II also has 1024Kb of data SRAM. + * SRAM is split up into several blocks and into three power domains: + * + * 1) TCM SRAMs are dedicated to the Cortex-M7 and are accessible with + * 0 wait states by the Cortex-M7 and by MDMA through AHBS slave bus + * + * 1.1) 128Kb of DTCM-RAM beginning at address 0x2000:0000 + * + * The DTCM-RAM is organized as 2 x 64Kb DTCM-RAMs on 2 x 32 bit + * DTCM ports. The DTCM-RAM could be used for critical real-time + * data, such as interrupt service routines or stack / heap memory. + * Both DTCM-RAMs can be used in parallel (for load/store operations) + * thanks to the Cortex-M7 dual issue capability. + * + * 1.2) 64Kb of ITCM-RAM beginning at address 0x0000:0000 + * + * This RAM is connected to ITCM 64-bit interface designed for + * execution of critical real-times routines by the CPU. + * + * 2) AXI SRAM (D1 domain) accessible by all system masters except BDMA + * through D1 domain AXI bus matrix + * + * 2.1) 512Kb of SRAM beginning at address 0x2400:0000 + * + * 3) AHB SRAM (D2 domain) accessible by all system masters except BDMA + * through D2 domain AHB bus matrix + * + * 3.1) 128Kb of SRAM1 beginning at address 0x3000:0000 + * 3.2) 128Kb of SRAM2 beginning at address 0x3002:0000 + * 3.3) 32Kb of SRAM3 beginning at address 0x3004:0000 + * + * SRAM1 - SRAM3 are one contiguous block: 288Kb at address 0x3000:0000 + * + * 4) AHB SRAM (D3 domain) accessible by most of system masters + * through D3 domain AHB bus matrix + * + * 4.1) 64Kb of SRAM4 beginning at address 0x3800:0000 + * 4.1) 4Kb of backup RAM beginning at address 0x3880:0000 + * + * When booting from FLASH, FLASH memory is aliased to address 0x0000:0000 + * where the code expects to begin execution by jumping to the entry point in + * the 0x0800:0000 address range. + * + * The bootloader uses the first sector of the flash, which is 128K in length. + */ + +MEMORY +{ + itcm (rwx) : ORIGIN = 0x00000000, LENGTH = 64K + flash (rx) : ORIGIN = 0x08000000, LENGTH = 128K + dtcm1 (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + dtcm2 (rwx) : ORIGIN = 0x20010000, LENGTH = 64K + sram (rwx) : ORIGIN = 0x24000000, LENGTH = 512K + sram1 (rwx) : ORIGIN = 0x30000000, LENGTH = 128K + sram2 (rwx) : ORIGIN = 0x30020000, LENGTH = 128K + sram3 (rwx) : ORIGIN = 0x30040000, LENGTH = 32K + sram4 (rwx) : ORIGIN = 0x38000000, LENGTH = 64K + bbram (rwx) : ORIGIN = 0x38800000, LENGTH = 4K +} + +OUTPUT_ARCH(arm) +EXTERN(_vectors) +ENTRY(_stext) + +/* + * Ensure that abort() is present in the final object. The exception handling + * code pulled in by libgcc.a requires it (and that code cannot be easily avoided). + */ +EXTERN(abort) +EXTERN(_bootdelay_signature) + +SECTIONS +{ + .text : { + _stext = ABSOLUTE(.); + *(.vectors) + . = ALIGN(32); + /* + This signature provides the bootloader with a way to delay booting + */ + _bootdelay_signature = ABSOLUTE(.); + FILL(0xffecc2925d7d05c5) + . += 8; + *(.text .text.*) + *(.fixup) + *(.gnu.warning) + *(.rodata .rodata.*) + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + _etext = ABSOLUTE(.); + + } > flash + + /* + * Init functions (static constructors and the like) + */ + .init_section : { + _sinit = ABSOLUTE(.); + KEEP(*(.init_array .init_array.*)) + _einit = ABSOLUTE(.); + } > flash + + + .ARM.extab : { + *(.ARM.extab*) + } > flash + + __exidx_start = ABSOLUTE(.); + .ARM.exidx : { + *(.ARM.exidx*) + } > flash + __exidx_end = ABSOLUTE(.); + + _eronly = ABSOLUTE(.); + + .data : { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.gnu.linkonce.d.*) + CONSTRUCTORS + _edata = ABSOLUTE(.); + } > sram AT > flash + + .bss : { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = ABSOLUTE(.); + } > sram + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/boards/ark/fmu-v6x/nuttx-config/scripts/script.ld b/boards/ark/fmu-v6x/nuttx-config/scripts/script.ld new file mode 100644 index 0000000000..b6b341d4e8 --- /dev/null +++ b/boards/ark/fmu-v6x/nuttx-config/scripts/script.ld @@ -0,0 +1,229 @@ +/**************************************************************************** + * scripts/script.ld + * + * Copyright (C) 2016, 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX 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. + * + ****************************************************************************/ + +/* The PX4 FMUV6X uses an STM32H753II has 2048Kb of main FLASH memory. + * The flash memory is partitioned into a User Flash memory and a System + * Flash memory. Each of these memories has two banks: + * + * 1) User Flash memory: + * + * Bank 1: Start address 0x0800:0000 to 0x080F:FFFF with 8 sectors, 128Kb each + * Bank 2: Start address 0x0810:0000 to 0x081F:FFFF with 8 sectors, 128Kb each + * + * 2) System Flash memory: + * + * Bank 1: Start address 0x1FF0:0000 to 0x1FF1:FFFF with 1 x 128Kb sector + * Bank 1: Start address 0x1FF4:0000 to 0x1FF5:FFFF with 1 x 128Kb sector + * + * 3) User option bytes for user configuration, only in Bank 1. + * + * In the STM32H743II, two different boot spaces can be selected through + * the BOOT pin and the boot base address programmed in the BOOT_ADD0 and + * BOOT_ADD1 option bytes: + * + * 1) BOOT=0: Boot address defined by user option byte BOOT_ADD0[15:0]. + * ST programmed value: Flash memory at 0x0800:0000 + * 2) BOOT=1: Boot address defined by user option byte BOOT_ADD1[15:0]. + * ST programmed value: System bootloader at 0x1FF0:0000 + * + * The PX4 FMUV6X has a test point on board, the BOOT0 pin is at ground so by + * default, the STM32 will boot to address 0x0800:0000 in FLASH unless the test + * point is pulled to 3.3V.then the boot will be from 0x1FF0:0000 + * + * The STM32H743II also has 1024Kb of data SRAM. + * SRAM is split up into several blocks and into three power domains: + * + * 1) TCM SRAMs are dedicated to the Cortex-M7 and are accessible with + * 0 wait states by the Cortex-M7 and by MDMA through AHBS slave bus + * + * 1.1) 128Kb of DTCM-RAM beginning at address 0x2000:0000 + * + * The DTCM-RAM is organized as 2 x 64Kb DTCM-RAMs on 2 x 32 bit + * DTCM ports. The DTCM-RAM could be used for critical real-time + * data, such as interrupt service routines or stack / heap memory. + * Both DTCM-RAMs can be used in parallel (for load/store operations) + * thanks to the Cortex-M7 dual issue capability. + * + * 1.2) 64Kb of ITCM-RAM beginning at address 0x0000:0000 + * + * This RAM is connected to ITCM 64-bit interface designed for + * execution of critical real-times routines by the CPU. + * + * 2) AXI SRAM (D1 domain) accessible by all system masters except BDMA + * through D1 domain AXI bus matrix + * + * 2.1) 512Kb of SRAM beginning at address 0x2400:0000 + * + * 3) AHB SRAM (D2 domain) accessible by all system masters except BDMA + * through D2 domain AHB bus matrix + * + * 3.1) 128Kb of SRAM1 beginning at address 0x3000:0000 + * 3.2) 128Kb of SRAM2 beginning at address 0x3002:0000 + * 3.3) 32Kb of SRAM3 beginning at address 0x3004:0000 + * + * SRAM1 - SRAM3 are one contiguous block: 288Kb at address 0x3000:0000 + * + * 4) AHB SRAM (D3 domain) accessible by most of system masters + * through D3 domain AHB bus matrix + * + * 4.1) 64Kb of SRAM4 beginning at address 0x3800:0000 + * 4.1) 4Kb of backup RAM beginning at address 0x3880:0000 + * + * When booting from FLASH, FLASH memory is aliased to address 0x0000:0000 + * where the code expects to begin execution by jumping to the entry point in + * the 0x0800:0000 address range. + */ + +MEMORY +{ + ITCM_RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 64K + FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 1920K + + DTCM1_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + DTCM2_RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 64K + AXI_SRAM (rwx) : ORIGIN = 0x24000000, LENGTH = 512K /* D1 domain AXI bus */ + SRAM1 (rwx) : ORIGIN = 0x30000000, LENGTH = 128K /* D2 domain AHB bus */ + SRAM2 (rwx) : ORIGIN = 0x30020000, LENGTH = 128K /* D2 domain AHB bus */ + SRAM3 (rwx) : ORIGIN = 0x30040000, LENGTH = 32K /* D2 domain AHB bus */ + SRAM4 (rwx) : ORIGIN = 0x38000000, LENGTH = 64K /* D3 domain */ + BKPRAM (rwx) : ORIGIN = 0x38800000, LENGTH = 4K +} + +OUTPUT_ARCH(arm) +EXTERN(_vectors) +ENTRY(_stext) + +/* + * Ensure that abort() is present in the final object. The exception handling + * code pulled in by libgcc.a requires it (and that code cannot be easily avoided). + */ +EXTERN(abort) +EXTERN(_bootdelay_signature) +EXTERN(board_get_manifest) + +SECTIONS +{ + .text : { + _stext = ABSOLUTE(.); + *(.vectors) + . = ALIGN(32); + /* + This signature provides the bootloader with a way to delay booting + */ + _bootdelay_signature = ABSOLUTE(.); + FILL(0xffecc2925d7d05c5) + . += 8; + *(.text .text.*) + *(.fixup) + *(.gnu.warning) + *(.rodata .rodata.*) + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + _etext = ABSOLUTE(.); + + } > FLASH + + /* + * Init functions (static constructors and the like) + */ + .init_section : { + _sinit = ABSOLUTE(.); + KEEP(*(.init_array .init_array.*)) + _einit = ABSOLUTE(.); + } > FLASH + + + .ARM.extab : { + *(.ARM.extab*) + } > FLASH + + __exidx_start = ABSOLUTE(.); + .ARM.exidx : { + *(.ARM.exidx*) + } > FLASH + __exidx_end = ABSOLUTE(.); + + _eronly = ABSOLUTE(.); + + .data : { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.gnu.linkonce.d.*) + CONSTRUCTORS + _edata = ABSOLUTE(.); + + /* Pad out last section as the STM32H7 Flash write size is 256 bits. 32 bytes */ + . = ALIGN(16); + FILL(0xffff) + . += 16; + } > AXI_SRAM AT > FLASH = 0xffff + + .bss : { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = ABSOLUTE(.); + } > AXI_SRAM + + /* Emit the the D3 power domain section for locating BDMA data */ + + .sram4_reserve (NOLOAD) : + { + *(.sram4) + . = ALIGN(4); + _sram4_heap_start = ABSOLUTE(.); + } > SRAM4 + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/boards/ark/fmu-v6x/src/CMakeLists.txt b/boards/ark/fmu-v6x/src/CMakeLists.txt new file mode 100644 index 0000000000..7ff9cdf5f5 --- /dev/null +++ b/boards/ark/fmu-v6x/src/CMakeLists.txt @@ -0,0 +1,70 @@ +############################################################################ +# +# Copyright (c) 2016 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 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. +# +############################################################################ +if("${PX4_BOARD_LABEL}" STREQUAL "bootloader") + add_library(drivers_board + bootloader_main.c + usb.c + ) + target_link_libraries(drivers_board + PRIVATE + nuttx_arch # sdio + nuttx_drivers # sdio + bootloader + ) + target_include_directories(drivers_board PRIVATE ${PX4_SOURCE_DIR}/platforms/nuttx/src/bootloader/common) + +else() + add_library(drivers_board + can.c + i2c.cpp + init.c + led.c + mtd.cpp + manifest.c + sdio.c + spi.cpp + timer_config.cpp + usb.c + ) + add_dependencies(drivers_board arch_board_hw_info) + + target_link_libraries(drivers_board + PRIVATE + arch_spi + arch_board_hw_info + drivers__led # drv_led_start + nuttx_arch # sdio + nuttx_drivers # sdio + px4_layer + ) +endif() diff --git a/boards/ark/fmu-v6x/src/board_config.h b/boards/ark/fmu-v6x/src/board_config.h new file mode 100644 index 0000000000..14827405b4 --- /dev/null +++ b/boards/ark/fmu-v6x/src/board_config.h @@ -0,0 +1,515 @@ +/**************************************************************************** + * + * Copyright (c) 2016, 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file board_config.h + * + * PX4FMU-v6x internal definitions + */ + +#pragma once + +/**************************************************************************************************** + * Included Files + ****************************************************************************************************/ + +#include +#include +#include + + +#include + +/**************************************************************************************************** + * Definitions + ****************************************************************************************************/ + +#undef TRACE_PINS + +/* PX4IO connection configuration */ + +#define BOARD_USES_PX4IO_VERSION 2 +#define PX4IO_SERIAL_DEVICE "/dev/ttyS5" +#define PX4IO_SERIAL_TX_GPIO GPIO_USART6_TX +#define PX4IO_SERIAL_RX_GPIO GPIO_USART6_RX +#define PX4IO_SERIAL_BASE STM32_USART6_BASE +#define PX4IO_SERIAL_VECTOR STM32_IRQ_USART6 +#define PX4IO_SERIAL_TX_DMAMAP DMAMAP_USART6_TX +#define PX4IO_SERIAL_RX_DMAMAP DMAMAP_USART6_RX +#define PX4IO_SERIAL_RCC_REG STM32_RCC_APB2ENR +#define PX4IO_SERIAL_RCC_EN RCC_APB2ENR_USART6EN +#define PX4IO_SERIAL_CLOCK STM32_PCLK2_FREQUENCY +#define PX4IO_SERIAL_BITRATE 1500000 /* 1.5Mbps -> max rate for IO */ + +/* Configuration ************************************************************************************/ + +#define BOARD_HAS_LTC44XX_VALIDS 2 // N Bricks +#define BOARD_HAS_USB_VALID 1 // LTC Has USB valid +#define BOARD_HAS_NBAT_V 2d // 2 Digital Voltage +#define BOARD_HAS_NBAT_I 2d // 2 Digital Current + +/* PX4FMU GPIOs ***********************************************************************************/ + +/* Trace Clock and D0-D3 are available on the trace connector + * + * TRACECLK PE2 - Dedicated - Trace Connector Pin 1 + * TRACED0 PE3 - nLED_RED - Trace Connector Pin 3 + * TRACED1 PE4 - nLED_GREEN - Trace Connector Pin 5 + * TRACED2 PE5 - nLED_BLUE - Trace Connector Pin 7 + * TRACED3 PE6 - nARMED - Trace Connector Pin 8 + + */ + +/* LEDs are driven with push open drain to support Anode to 5V or 3.3V or used as TRACE0-2 */ + +#if !defined(TRACE_PINS) +# define GPIO_nLED_RED /* PE3 */ (GPIO_OUTPUT|GPIO_OPENDRAIN|GPIO_SPEED_50MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN3) +# define GPIO_nLED_GREEN /* PE4 */ (GPIO_OUTPUT|GPIO_OPENDRAIN|GPIO_SPEED_50MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN4) +# define GPIO_nLED_BLUE /* PE5 */ (GPIO_OUTPUT|GPIO_OPENDRAIN|GPIO_SPEED_50MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN5) + +# define BOARD_HAS_CONTROL_STATUS_LEDS 1 +# define BOARD_OVERLOAD_LED LED_RED +# define BOARD_ARMED_STATE_LED LED_BLUE + +#else + +# define GPIO_TRACECLK1 (GPIO_TRACECLK |GPIO_PULLUP|GPIO_SPEED_100MHz|GPIO_PUSHPULL) //(GPIO_ALT|GPIO_AF0|GPIO_PORTE|GPIO_PIN2) +# define GPIO_TRACED0 (GPIO_TRACED0_2|GPIO_PULLUP|GPIO_SPEED_100MHz|GPIO_PUSHPULL) //(GPIO_ALT|GPIO_AF0|GPIO_PORTE|GPIO_PIN3) +# define GPIO_TRACED1 (GPIO_TRACED1_2|GPIO_PULLUP|GPIO_SPEED_100MHz|GPIO_PUSHPULL) //(GPIO_ALT|GPIO_AF0|GPIO_PORTE|GPIO_PIN4) +# define GPIO_TRACED2 (GPIO_TRACED2_2|GPIO_PULLUP|GPIO_SPEED_100MHz|GPIO_PUSHPULL) //(GPIO_ALT|GPIO_AF0|GPIO_PORTE|GPIO_PIN5) +# define GPIO_TRACED3 (GPIO_TRACED3_2|GPIO_PULLUP|GPIO_SPEED_100MHz|GPIO_PUSHPULL) //(GPIO_ALT|GPIO_AF0|GPIO_PORTE|GPIO_PIN6) +//#define GPIO_TRACESWO //(GPIO_ALT|GPIO_AF0|GPIO_PORTB|GPIO_PIN3) + +# undef BOARD_HAS_CONTROL_STATUS_LEDS +# undef BOARD_OVERLOAD_LED +# undef BOARD_ARMED_STATE_LED + +# define GPIO_nLED_RED GPIO_TRACED0 +# define GPIO_nLED_GREEN GPIO_TRACED1 +# define GPIO_nLED_BLUE GPIO_TRACED2 +# define GPIO_nARMED GPIO_TRACED3 +# define GPIO_nARMED_INIT GPIO_TRACED3 +#endif + + +/* SPI */ + +#define SPI6_nRESET_EXTERNAL1 /* PF10 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTF|GPIO_PIN10) +#define GPIO_SYNC /* PE9 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_100MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN9) + +/* I2C busses */ + +/* Devices on the onboard buses. + * + * Note that these are unshifted addresses. + */ +#define GPIO_I2C2_DRDY1_BMP388 /* PG5 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTG|GPIO_PIN5) + +/* + * ADC channels + * + * These are the channel numbers of the ADCs of the microcontroller that + * can be used by the Px4 Firmware in the adc driver + */ + +/* ADC defines to be used in sensors.cpp to read from a particular channel */ + +#define ADC1_CH(n) (n) + +/* N.B. there is no offset mapping needed for ADC3 because */ +#define ADC3_CH(n) (n) + +/* We are only use ADC3 for REV/VER. + * ADC3_6V6 and ADC3_3V3 are mapped back to ADC1 + * To do this We are relying on PC2_C, PC3_C being connected to PC2, PC3 + * respectively by the SYSCFG_PMCR default of setting for PC3SO PC2SO PA1SO + * PA0SO of 0. + * + * 0 Analog switch closed (pads are connected through the analog switch) + * + * So ADC3_INP0 is GPIO_ADC123_INP12 + * ADC3_INP1 is GPIO_ADC12_INP13 + */ + +/* Define GPIO pins used as ADC N.B. Channel numbers must match below */ + +#define PX4_ADC_GPIO \ + /* PA0 */ GPIO_ADC1_INP16, \ + /* PA4 */ GPIO_ADC12_INP18, \ + /* PB0 */ GPIO_ADC12_INP9, \ + /* PB1 */ GPIO_ADC12_INP5, \ + /* PC2 */ GPIO_ADC123_INP12, \ + /* PC3 */ GPIO_ADC12_INP13, \ + /* PF12 */ GPIO_ADC1_INP6, \ + /* PH3 */ GPIO_ADC3_INP14, \ + /* PH4 */ GPIO_ADC3_INP15 + +/* Define Channel numbers must match above GPIO pin IN(n)*/ +#define ADC_SCALED_VDD_3V3_SENSORS1_CHANNEL /* PA0 */ ADC1_CH(16) +#define ADC_SCALED_VDD_3V3_SENSORS2_CHANNEL /* PA4 */ ADC1_CH(18) +#define ADC_SCALED_VDD_3V3_SENSORS3_CHANNEL /* PB0 */ ADC1_CH(9) +#define ADC_SCALED_V5_CHANNEL /* PB1 */ ADC1_CH(5) +#define ADC_ADC3_6V6_CHANNEL /* PC2 */ ADC3_CH(12) +#define ADC_ADC3_3V3_CHANNEL /* PC3 */ ADC3_CH(13) +#define ADC_SCALED_VDD_3V3_SENSORS4_CHANNEL /* PF12 */ ADC1_CH(6) +#define ADC_HW_VER_SENSE_CHANNEL /* PH3 */ ADC3_CH(14) +#define ADC_HW_REV_SENSE_CHANNEL /* PH4 */ ADC3_CH(15) + +#define ADC_CHANNELS \ + ((1 << ADC_SCALED_VDD_3V3_SENSORS1_CHANNEL) | \ + (1 << ADC_SCALED_VDD_3V3_SENSORS2_CHANNEL) | \ + (1 << ADC_SCALED_VDD_3V3_SENSORS3_CHANNEL) | \ + (1 << ADC_SCALED_V5_CHANNEL) | \ + (1 << ADC_ADC3_6V6_CHANNEL) | \ + (1 << ADC_ADC3_3V3_CHANNEL)) | \ + (1 << ADC_SCALED_VDD_3V3_SENSORS4_CHANNEL) + +/* HW has to large of R termination on ADC todo:change when HW value is chosen */ + +#define HW_REV_VER_ADC_BASE STM32_ADC3_BASE + +#define SYSTEM_ADC_BASE STM32_ADC1_BASE + +/* HW has to large of R termination on ADC todo:change when HW value is chosen */ +#define BOARD_ADC_OPEN_CIRCUIT_V (5.6f) + +/* HW Version and Revision drive signals Default to 1 to detect */ +#define BOARD_HAS_HW_VERSIONING + +#define GPIO_HW_VER_REV_DRIVE /* PG0 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTG|GPIO_PIN0) +#define GPIO_HW_REV_SENSE /* PH4 */ GPIO_ADC3_INP15 +#define GPIO_HW_VER_SENSE /* PH3 */ GPIO_ADC3_INP14 +#define HW_INFO_INIT {'V','6','X','x', 'x',0} +#define HW_INFO_INIT_VER 3 /* Offset in above string of the VER */ +#define HW_INFO_INIT_REV 4 /* Offset in above string of the REV */ + +#define BOARD_NUM_SPI_CFG_HW_VERSIONS 2 // Rev 0 and Rev 3 Sensor sets +// Base/FMUM +#define V6X00 HW_VER_REV(0x0,0x0) // FMUV6X, Rev 0 +#define V6X01 HW_VER_REV(0x0,0x1) // FMUV6X, BMI388 I2C2 Rev 1 +#define V6X03 HW_VER_REV(0x0,0x3) // FMUV6X, Sensor Set Rev 3 +#define V6X10 HW_VER_REV(0x1,0x0) // NO PX4IO, Rev 0 +#define V6X13 HW_VER_REV(0x1,0x3) // NO PX4IO, Sensor Set Rev 3 + + +/* HEATER + * PWM in future + */ +#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10) +#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true)) + +/* PE6 is nARMED + * The GPIO will be set as input while not armed HW will have external HW Pull UP. + * While armed it shall be configured at a GPIO OUT set LOW + */ +#if !defined(TRACE_PINS) +#define GPIO_nARMED_INIT /* PE6 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTE|GPIO_PIN6) +#define GPIO_nARMED /* PE6 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN6) +#define BOARD_INDICATE_EXTERNAL_LOCKOUT_STATE(enabled) px4_arch_configgpio((enabled) ? GPIO_nARMED : GPIO_nARMED_INIT) +#define BOARD_GET_EXTERNAL_LOCKOUT_STATE() px4_arch_gpioread(GPIO_nARMED) +#endif + + +/* PWM + */ +#define DIRECT_PWM_OUTPUT_CHANNELS 9 + + +/* Power supply control and monitoring GPIOs */ + +#define GPIO_nPOWER_IN_A /* PG1 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTG|GPIO_PIN1) +#define GPIO_nPOWER_IN_B /* PG2 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTG|GPIO_PIN2) +#define GPIO_nPOWER_IN_C /* PG3 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTG|GPIO_PIN3) + +#define GPIO_nVDD_BRICK1_VALID GPIO_nPOWER_IN_A /* Brick 1 Is Chosen */ +#define GPIO_nVDD_BRICK2_VALID GPIO_nPOWER_IN_B /* Brick 2 Is Chosen */ +#define BOARD_NUMBER_BRICKS 2 +#define BOARD_NUMBER_DIGITAL_BRICKS 2 +#define GPIO_nVDD_USB_VALID GPIO_nPOWER_IN_C /* USB Is Chosen */ + +#define GPIO_VDD_5V_PERIPH_nEN /* PG4 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTG|GPIO_PIN4) +#define GPIO_VDD_5V_PERIPH_nOC /* PE15 */ (GPIO_INPUT |GPIO_FLOAT|GPIO_PORTE|GPIO_PIN15) +#define GPIO_VDD_5V_HIPOWER_nEN /* PG10 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTG|GPIO_PIN10) +#define GPIO_VDD_5V_HIPOWER_nOC /* PF13 */ (GPIO_INPUT |GPIO_FLOAT|GPIO_PORTF|GPIO_PIN13) +#define GPIO_VDD_3V3_SENSORS4_EN /* PG8 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN8) +#define GPIO_VDD_3V3_SPEKTRUM_POWER_EN /* PH2 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN2) +#define GPIO_VDD_3V3_SD_CARD_EN /* PC13 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTC|GPIO_PIN13) + +/* Spare GPIO */ + +#define GPIO_PG6 /* PG6 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTG|GPIO_PIN6) +#define GPIO_PD15 /* PD15 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_PORTD|GPIO_PIN15) + +/* ETHERNET GPIO */ + +#define GPIO_ETH_POWER_EN /* PG15 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN15) + +/* NFC GPIO */ + +#define GPIO_NFC_GPIO /* PC0 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTC|GPIO_PIN0) + + +/* Define True logic Power Control in arch agnostic form */ + +#define VDD_5V_PERIPH_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_5V_PERIPH_nEN, !(on_true)) +#define VDD_5V_HIPOWER_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_5V_HIPOWER_nEN, !(on_true)) +#define VDD_3V3_SENSORS4_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SENSORS4_EN, (on_true)) +#define VDD_3V3_SPEKTRUM_POWER_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SPEKTRUM_POWER_EN, (on_true)) +#define READ_VDD_3V3_SPEKTRUM_POWER_EN() px4_arch_gpioread(GPIO_VDD_3V3_SPEKTRUM_POWER_EN) +#define VDD_3V3_SD_CARD_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SD_CARD_EN, (on_true)) +#define VDD_3V3_ETH_POWER_EN(on_true) px4_arch_gpiowrite(GPIO_ETH_POWER_EN, (on_true)) + + +/* Tone alarm output */ + +#define TONE_ALARM_TIMER 14 /* Timer 14 */ +#define TONE_ALARM_CHANNEL 1 /* PF9 GPIO_TIM14_CH1OUT_2 */ + +#define GPIO_BUZZER_1 /* PF9 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTF|GPIO_PIN9) + +#define GPIO_TONE_ALARM_IDLE GPIO_BUZZER_1 +#define GPIO_TONE_ALARM GPIO_TIM14_CH1OUT_2 + +/* USB OTG FS + * + * PA9 OTG_FS_VBUS VBUS sensing + */ +#define GPIO_OTGFS_VBUS /* PA9 */ (GPIO_INPUT|GPIO_PULLDOWN|GPIO_SPEED_100MHz|GPIO_PORTA|GPIO_PIN9) + +/* High-resolution timer */ +#define HRT_TIMER 8 /* use timer8 for the HRT */ +#define HRT_TIMER_CHANNEL 3 /* use capture/compare channel 3 */ + +#define HRT_PPM_CHANNEL /* T8C1 */ 1 /* use capture/compare channel 1 */ +#define GPIO_PPM_IN /* PI5 T8C1 */ GPIO_TIM8_CH1IN_2 + +/* RC Serial port */ + +#define RC_SERIAL_PORT "/dev/ttyS5" +#define RC_SERIAL_SINGLEWIRE + +/* Input Capture Channels. */ +#define INPUT_CAP1_TIMER 1 +#define INPUT_CAP1_CHANNEL /* T1C2 */ 2 +#define GPIO_INPUT_CAP1 /* PE11 */ GPIO_TIM1_CH2IN + +/* PWM input driver. Use FMU AUX5 pins attached to timer4 channel 2 */ +#define PWMIN_TIMER 4 +#define PWMIN_TIMER_CHANNEL /* T4C2 */ 2 +#define GPIO_PWM_IN /* PD13 */ GPIO_TIM4_CH2IN_2 + +/* Safety Switch is HW version dependent on having an PX4IO + * So we init to a benign state with the _INIT definition + * and provide the the non _INIT one for the driver to make a run time + * decision to use it. + */ +#define GPIO_nSAFETY_SWITCH_LED_OUT_INIT /* PD10 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_PORTD|GPIO_PIN10) +#define GPIO_nSAFETY_SWITCH_LED_OUT /* PD10 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTD|GPIO_PIN10) + +/* Enable the FMU to control it if there is no px4io fixme:This should be BOARD_SAFETY_LED(__ontrue) */ +#define GPIO_LED_SAFETY GPIO_nSAFETY_SWITCH_LED_OUT + +#define GPIO_SAFETY_SWITCH_IN /* PF5 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTF|GPIO_PIN5) +/* Enable the FMU to use the switch it if there is no px4io fixme:This should be BOARD_SAFTY_BUTTON() */ +#define GPIO_BTN_SAFETY GPIO_SAFETY_SWITCH_IN /* Enable the FMU to control it if there is no px4io */ + +/* Power switch controls ******************************************************/ + +#define SPEKTRUM_POWER(_on_true) VDD_3V3_SPEKTRUM_POWER_EN(_on_true) + +/* + * FMUv6X has a separate RC_IN + * + * GPIO PPM_IN on PI5 T8CH1 + * SPEKTRUM_RX (it's TX or RX in Bind) on UART6 PC7 + * Inversion is possible in the UART and can drive GPIO PPM_IN as an output + */ + +#define GPIO_PPM_IN_AS_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTI|GPIO_PIN5) +#define SPEKTRUM_RX_AS_GPIO_OUTPUT() px4_arch_configgpio(GPIO_PPM_IN_AS_OUT) +#define SPEKTRUM_RX_AS_UART() /* Can be left as uart */ +#define SPEKTRUM_OUT(_one_true) px4_arch_gpiowrite(GPIO_PPM_IN_AS_OUT, (_one_true)) + +#define SDIO_SLOTNO 0 /* Only one slot */ +#define SDIO_MINOR 0 + +/* SD card bringup does not work if performed on the IDLE thread because it + * will cause waiting. Use either: + * + * CONFIG_LIB_BOARDCTL=y, OR + * CONFIG_BOARD_INITIALIZE=y && CONFIG_BOARD_INITTHREAD=y + */ + +#if defined(CONFIG_BOARD_INITIALIZE) && !defined(CONFIG_LIB_BOARDCTL) && \ + !defined(CONFIG_BOARD_INITTHREAD) +# warning SDIO initialization cannot be perfomed on the IDLE thread +#endif + +/* By Providing BOARD_ADC_USB_CONNECTED (using the px4_arch abstraction) + * this board support the ADC system_power interface, and therefore + * provides the true logic GPIO BOARD_ADC_xxxx macros. + */ +#define BOARD_ADC_USB_CONNECTED (px4_arch_gpioread(GPIO_OTGFS_VBUS)) +#define BOARD_ADC_USB_VALID (!px4_arch_gpioread(GPIO_nVDD_USB_VALID)) + +/* FMUv6X never powers off the Servo rail */ + +#define BOARD_ADC_SERVO_VALID (1) + +#if !defined(BOARD_HAS_LTC44XX_VALIDS) || BOARD_HAS_LTC44XX_VALIDS == 0 +# define BOARD_ADC_BRICK1_VALID (1) +# define BOARD_ADC_BRICK2_VALID (0) +#elif BOARD_HAS_LTC44XX_VALIDS == 1 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (0) +#elif BOARD_HAS_LTC44XX_VALIDS == 2 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK2_VALID)) +#elif BOARD_HAS_LTC44XX_VALIDS == 3 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK2_VALID)) +# define BOARD_ADC_BRICK3_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK3_VALID)) +#elif BOARD_HAS_LTC44XX_VALIDS == 4 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK2_VALID)) +# define BOARD_ADC_BRICK3_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK3_VALID)) +# define BOARD_ADC_BRICK4_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK4_VALID)) +#else +# error Unsupported BOARD_HAS_LTC44XX_VALIDS value +#endif + +#define BOARD_ADC_PERIPH_5V_OC (!px4_arch_gpioread(GPIO_VDD_5V_PERIPH_nOC)) +#define BOARD_ADC_HIPOWER_5V_OC (!px4_arch_gpioread(GPIO_VDD_5V_HIPOWER_nOC)) + + +/* This board provides a DMA pool and APIs */ +#define BOARD_DMA_ALLOC_POOL_SIZE 5120 + +/* This board provides the board_on_reset interface */ + +#define BOARD_HAS_ON_RESET 1 + +#if defined(TRACE_PINS) +#define GPIO_TRACE \ + GPIO_TRACECLK1, \ + GPIO_TRACED0, \ + GPIO_TRACED1, \ + GPIO_TRACED2, \ + GPIO_TRACED3 +#else +#define GPIO_TRACE (GPIO_OUTPUT|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN2) +#endif + +#define PX4_GPIO_INIT_LIST { \ + GPIO_TRACE, \ + PX4_ADC_GPIO, \ + GPIO_HW_VER_REV_DRIVE, \ + GPIO_CAN1_TX, \ + GPIO_CAN1_RX, \ + GPIO_CAN2_TX, \ + GPIO_CAN2_RX, \ + GPIO_HEATER_OUTPUT, \ + GPIO_nPOWER_IN_A, \ + GPIO_nPOWER_IN_B, \ + GPIO_nPOWER_IN_C, \ + GPIO_VDD_5V_PERIPH_nEN, \ + GPIO_VDD_5V_PERIPH_nOC, \ + GPIO_VDD_5V_HIPOWER_nEN, \ + GPIO_VDD_5V_HIPOWER_nOC, \ + GPIO_VDD_3V3_SENSORS4_EN, \ + GPIO_VDD_3V3_SPEKTRUM_POWER_EN, \ + GPIO_VDD_3V3_SD_CARD_EN, \ + GPIO_PD15, \ + GPIO_SYNC, \ + SPI6_nRESET_EXTERNAL1, \ + GPIO_ETH_POWER_EN, \ + GPIO_NFC_GPIO, \ + GPIO_TONE_ALARM_IDLE, \ + GPIO_nSAFETY_SWITCH_LED_OUT_INIT, \ + GPIO_SAFETY_SWITCH_IN, \ + GPIO_PG6, \ + GPIO_nARMED_INIT \ + } + +#define BOARD_ENABLE_CONSOLE_BUFFER + +#define PX4_I2C_BUS_MTD 4,5 + + +#define BOARD_NUM_IO_TIMERS 5 + +__BEGIN_DECLS + +/**************************************************************************************************** + * Public Types + ****************************************************************************************************/ + +/**************************************************************************************************** + * Public data + ****************************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************************************** + * Public Functions + ****************************************************************************************************/ + +/**************************************************************************** + * Name: stm32_sdio_initialize + * + * Description: + * Initialize SDIO-based MMC/SD card support + * + ****************************************************************************/ + +int stm32_sdio_initialize(void); + +/**************************************************************************************************** + * Name: stm32_spiinitialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the PX4FMU board. + * + ****************************************************************************************************/ + +extern void stm32_spiinitialize(void); + +extern void stm32_usbinitialize(void); + +extern void board_peripheral_reset(int ms); + +#include + +#endif /* __ASSEMBLY__ */ + +__END_DECLS diff --git a/boards/ark/fmu-v6x/src/bootloader_main.c b/boards/ark/fmu-v6x/src/bootloader_main.c new file mode 100644 index 0000000000..915b7f3d29 --- /dev/null +++ b/boards/ark/fmu-v6x/src/bootloader_main.c @@ -0,0 +1,75 @@ +/**************************************************************************** + * + * Copyright (c) 2019, 2021 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file bootloader_main.c + * + * FMU-specific early startup code for bootloader +*/ + +#include "board_config.h" +#include "bl.h" + +#include +#include +#include +#include +#include +#include "arm_internal.h" +#include + +extern int sercon_main(int c, char **argv); + +__EXPORT void board_on_reset(int status) {} + +__EXPORT void stm32_boardinitialize(void) +{ + /* configure USB interfaces */ + stm32_usbinitialize(); +} + +__EXPORT int board_app_initialize(uintptr_t arg) +{ + return 0; +} + +void board_late_initialize(void) +{ + sercon_main(0, NULL); +} + +extern void sys_tick_handler(void); +void board_timerhook(void) +{ + sys_tick_handler(); +} diff --git a/boards/ark/fmu-v6x/src/can.c b/boards/ark/fmu-v6x/src/can.c new file mode 100644 index 0000000000..3834074f37 --- /dev/null +++ b/boards/ark/fmu-v6x/src/can.c @@ -0,0 +1,128 @@ +/**************************************************************************** + * + * Copyright (C) 2012 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file px4fmu_can.c + * + * Board-specific CAN functions. + */ + +#ifdef CONFIG_CAN + +#include +#include + +#include +#include + +#include "chip.h" +#include "arm_arch.h" + +#include "chip.h" +#include "stm32_can.h" +#include "board_config.h" + +#ifdef CONFIG_CAN + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +#if defined(CONFIG_STM32_CAN1) && defined(CONFIG_STM32_CAN2) +# warning "Both CAN1 and CAN2 are enabled. Assuming only CAN1." +# undef CONFIG_STM32_CAN2 +#endif + +#ifdef CONFIG_STM32_CAN1 +# define CAN_PORT 1 +#else +# define CAN_PORT 2 +#endif + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ +int can_devinit(void); + +/************************************************************************************ + * Name: can_devinit + * + * Description: + * All STM32 architectures must provide the following interface to work with + * examples/can. + * + ************************************************************************************/ + +int can_devinit(void) +{ + static bool initialized = false; + struct can_dev_s *can; + int ret; + + /* Check if we have already initialized */ + + if (!initialized) { + /* Call stm32_caninitialize() to get an instance of the CAN interface */ + + can = stm32_caninitialize(CAN_PORT); + + if (can == NULL) { + canerr("ERROR: Failed to get CAN interface\n"); + return -ENODEV; + } + + /* Register the CAN driver at "/dev/can0" */ + + ret = can_register("/dev/can0", can); + + if (ret < 0) { + canerr("ERROR: can_register failed: %d\n", ret); + return ret; + } + + /* Now we are initialized */ + + initialized = true; + } + + return OK; +} + +#endif + +#endif /* CONFIG_CAN */ diff --git a/boards/ark/fmu-v6x/src/hw_config.h b/boards/ark/fmu-v6x/src/hw_config.h new file mode 100644 index 0000000000..3a0205c87b --- /dev/null +++ b/boards/ark/fmu-v6x/src/hw_config.h @@ -0,0 +1,128 @@ +/* + * hw_config.h + * + * Created on: May 17, 2015 + * Author: david_s5 + */ + +#ifndef HW_CONFIG_H_ +#define HW_CONFIG_H_ + +/**************************************************************************** + * 10-8--2016: + * To simplify the ripple effect on the tools, we will be using + * /dev/serial/by-id/PX4 to locate PX4 devices. Therefore + * moving forward all Bootloaders must contain the prefix "PX4 BL " + * in the USBDEVICESTRING + * This Change will be made in an upcoming BL release + ****************************************************************************/ +/* + * Define usage to configure a bootloader + * + * + * Constant example Usage + * APP_LOAD_ADDRESS 0x08004000 - The address in Linker Script, where the app fw is org-ed + * BOOTLOADER_DELAY 5000 - Ms to wait while under USB pwr or bootloader request + * BOARD_FMUV2 + * INTERFACE_USB 1 - (Optional) Scan and use the USB interface for bootloading + * INTERFACE_USART 1 - (Optional) Scan and use the Serial interface for bootloading + * USBDEVICESTRING "PX4 BL FMU v2.x" - USB id string + * USBPRODUCTID 0x0011 - PID Should match defconfig + * BOOT_DELAY_ADDRESS 0x000001a0 - (Optional) From the linker script from Linker Script to get a custom + * delay provided by an APP FW + * BOARD_TYPE 9 - Must match .prototype boad_id + * _FLASH_KBYTES (*(uint16_t *)0x1fff7a22) - Run time flash size detection + * BOARD_FLASH_SECTORS ((_FLASH_KBYTES == 0x400) ? 11 : 23) - Run time determine the physical last sector + * BOARD_FLASH_SECTORS 11 - Hard coded zero based last sector + * BOARD_FLASH_SIZE (_FLASH_KBYTES*1024)- Total Flash size of device, determined at run time. + * (1024 * 1024) - Hard coded Total Flash of device - The bootloader and app reserved will be deducted + * programmatically + * + * BOARD_FIRST_FLASH_SECTOR_TO_ERASE 2 - Optional sectors index in the flash_sectors table (F4 only), to begin erasing. + * This is to allow sectors to be reserved for app fw usage. That will NOT be erased + * during a FW upgrade. + * The default is 0, and selects the first sector to be erased, as the 0th entry in the + * flash_sectors table. Which is the second physical sector of FLASH in the device. + * The first physical sector of FLASH is used by the bootloader, and is not defined + * in the table. + * + * APP_RESERVATION_SIZE (BOARD_FIRST_FLASH_SECTOR_TO_ERASE * 16 * 1024) - Number of bytes reserved by the APP FW. This number plus + * BOOTLOADER_RESERVATION_SIZE will be deducted from + * BOARD_FLASH_SIZE to determine the size of the App FW + * and hence the address space of FLASH to erase and program. + * USBMFGSTRING "PX4 AP" - Optional USB MFG string (default is '3D Robotics' if not defined.) + * SERIAL_BREAK_DETECT_DISABLED - Optional prevent break selection on Serial port from entering or staying in BL + * + * * Other defines are somewhat self explanatory. + */ + +/* Boot device selection list*/ +#define USB0_DEV 0x01 +#define SERIAL0_DEV 0x02 +#define SERIAL1_DEV 0x04 + +#define APP_LOAD_ADDRESS 0x08020000 +#define BOOTLOADER_DELAY 5000 +#define INTERFACE_USB 1 +#define INTERFACE_USB_CONFIG "/dev/ttyACM0" +#define BOARD_VBUS MK_GPIO_INPUT(GPIO_OTGFS_VBUS) + +//#define USE_VBUS_PULL_DOWN +#define INTERFACE_USART 1 +#define INTERFACE_USART_CONFIG "/dev/ttyS0,115200" +#define BOOT_DELAY_ADDRESS 0x000001a0 +#define BOARD_TYPE 57 +#define _FLASH_KBYTES (*(uint32_t *)0x1FF1E880) +#define BOARD_FLASH_SECTORS (15) +#define BOARD_FLASH_SIZE (_FLASH_KBYTES * 1024) + +#define OSC_FREQ 16 + +#define BOARD_PIN_LED_ACTIVITY GPIO_nLED_BLUE // BLUE +#define BOARD_PIN_LED_BOOTLOADER GPIO_nLED_GREEN // GREEN +#define BOARD_LED_ON 0 +#define BOARD_LED_OFF 1 + +#define SERIAL_BREAK_DETECT_DISABLED 1 + +/* + * Uncommenting this allows to force the bootloader through + * a PWM output pin. As this can accidentally initialize + * an ESC prematurely, it is not recommended. This feature + * has not been used and hence defaults now to off. + * + * # define BOARD_FORCE_BL_PIN_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN14) + * # define BOARD_FORCE_BL_PIN_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTE|GPIO_PIN11) + * + * # define BOARD_POWER_PIN_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTA|GPIO_PIN4) + * # define BOARD_POWER_ON 1 + * # define BOARD_POWER_OFF 0 + * # undef BOARD_POWER_PIN_RELEASE // Leave pin enabling Power - un comment to release (disable power) + * +*/ + +#if !defined(ARCH_SN_MAX_LENGTH) +# define ARCH_SN_MAX_LENGTH 12 +#endif + +#if !defined(APP_RESERVATION_SIZE) +# define APP_RESERVATION_SIZE 0 +#endif + +#if !defined(BOARD_FIRST_FLASH_SECTOR_TO_ERASE) +# define BOARD_FIRST_FLASH_SECTOR_TO_ERASE 1 +#endif + +#if !defined(USB_DATA_ALIGN) +# define USB_DATA_ALIGN +#endif + +#ifndef BOOT_DEVICES_SELECTION +# define BOOT_DEVICES_SELECTION USB0_DEV|SERIAL0_DEV|SERIAL1_DEV +#endif + +#ifndef BOOT_DEVICES_FILTER_ONUSB +# define BOOT_DEVICES_FILTER_ONUSB USB0_DEV|SERIAL0_DEV|SERIAL1_DEV +#endif + +#endif /* HW_CONFIG_H_ */ diff --git a/boards/ark/fmu-v6x/src/i2c.cpp b/boards/ark/fmu-v6x/src/i2c.cpp new file mode 100644 index 0000000000..8a557078e0 --- /dev/null +++ b/boards/ark/fmu-v6x/src/i2c.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * Copyright (C) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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 + +constexpr px4_i2c_bus_t px4_i2c_buses[I2C_BUS_MAX_BUS_ITEMS] = { + initI2CBusExternal(1), + initI2CBusExternal(2), + initI2CBusExternal(3), + initI2CBusInternal(4), +}; diff --git a/boards/ark/fmu-v6x/src/init.c b/boards/ark/fmu-v6x/src/init.c new file mode 100644 index 0000000000..a96a406496 --- /dev/null +++ b/boards/ark/fmu-v6x/src/init.c @@ -0,0 +1,276 @@ +/**************************************************************************** + * + * Copyright (c) 2012-2022 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file init.c + * + * PX4FMU-specific early startup code. This file implements the + * board_app_initialize() function that is called early by nsh during startup. + * + * Code here is run before the rcS script is invoked; it should start required + * subsystems and perform board-specific initialization. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "board_config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arm_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* + * Ideally we'd be able to get these from arm_internal.h, + * but since we want to be able to disable the NuttX use + * of leds for system indication at will and there is no + * separate switch, we need to build independent of the + * CONFIG_ARCH_LEDS configuration switch. + */ +__BEGIN_DECLS +extern void led_init(void); +extern void led_on(int led); +extern void led_off(int led); +__END_DECLS + + +/************************************************************************************ + * Name: board_peripheral_reset + * + * Description: + * + ************************************************************************************/ +__EXPORT void board_peripheral_reset(int ms) +{ + /* set the peripheral rails off */ + + VDD_5V_PERIPH_EN(false); + board_control_spi_sensors_power(false, 0xffff); + VDD_3V3_SENSORS4_EN(false); + + bool last = READ_VDD_3V3_SPEKTRUM_POWER_EN(); + /* Keep Spektum on to discharge rail*/ + VDD_3V3_SPEKTRUM_POWER_EN(false); + + /* wait for the peripheral rail to reach GND */ + usleep(ms * 1000); + syslog(LOG_DEBUG, "reset done, %d ms\n", ms); + + /* re-enable power */ + + /* switch the peripheral rail back on */ + VDD_3V3_SPEKTRUM_POWER_EN(last); + board_control_spi_sensors_power(true, 0xffff); + VDD_3V3_SENSORS4_EN(true); + VDD_5V_PERIPH_EN(true); + +} + +/************************************************************************************ + * Name: board_on_reset + * + * Description: + * Optionally provided function called on entry to board_system_reset + * It should perform any house keeping prior to the rest. + * + * status - 1 if resetting to boot loader + * 0 if just resetting + * + ************************************************************************************/ +__EXPORT void board_on_reset(int status) +{ + for (int i = 0; i < DIRECT_PWM_OUTPUT_CHANNELS; ++i) { + px4_arch_configgpio(PX4_MAKE_GPIO_INPUT(io_timer_channel_get_as_pwm_input(i))); + } + + if (status >= 0) { + up_mdelay(6); + } +} + +/************************************************************************************ + * Name: stm32_boardinitialize + * + * Description: + * All STM32 architectures must provide the following entry point. This entry point + * is called early in the initialization -- after all memory has been configured + * and mapped but before any devices have been initialized. + * + ************************************************************************************/ + +__EXPORT void +stm32_boardinitialize(void) +{ + board_on_reset(-1); /* Reset PWM first thing */ + + /* configure LEDs */ + + board_autoled_initialize(); + + /* configure pins */ + + const uint32_t gpio[] = PX4_GPIO_INIT_LIST; + px4_gpio_init(gpio, arraySize(gpio)); + + /* configure USB interfaces */ + + stm32_usbinitialize(); + + VDD_3V3_ETH_POWER_EN(true); + +} + +/**************************************************************************** + * Name: board_app_initialize + * + * Description: + * Perform application specific initialization. This function is never + * called directly from application code, but only indirectly via the + * (non-standard) boardctl() interface using the command BOARDIOC_INIT. + * + * Input Parameters: + * arg - The boardctl() argument is passed to the board_app_initialize() + * implementation without modification. The argument has no + * meaning to NuttX; the meaning of the argument is a contract + * between the board-specific initalization logic and the the + * matching application logic. The value cold be such things as a + * mode enumeration value, a set of DIP switch switch settings, a + * pointer to configuration data read from a file or serial FLASH, + * or whatever you would like to do with it. Every implementation + * should accept zero/NULL as a default configuration. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +__EXPORT int board_app_initialize(uintptr_t arg) +{ + /* Power on Interfaces */ + VDD_3V3_SD_CARD_EN(true); + VDD_5V_PERIPH_EN(true); + VDD_5V_HIPOWER_EN(true); + VDD_3V3_SENSORS4_EN(true); + VDD_3V3_SPEKTRUM_POWER_EN(true); + + /* Need hrt running before using the ADC */ + + px4_platform_init(); + + + if (OK == board_determine_hw_info()) { + syslog(LOG_INFO, "[boot] Rev 0x%1x : Ver 0x%1x %s\n", board_get_hw_revision(), board_get_hw_version(), + board_get_hw_type_name()); + + } else { + syslog(LOG_ERR, "[boot] Failed to read HW revision and version\n"); + } + + stm32_spiinitialize(); + + board_spi_reset(10, 0xffff); + + /* configure the DMA allocator */ + + if (board_dma_alloc_init() < 0) { + syslog(LOG_ERR, "[boot] DMA alloc FAILED\n"); + } + +#if defined(SERIAL_HAVE_RXDMA) + // set up the serial DMA polling at 1ms intervals for received bytes that have not triggered a DMA event. + static struct hrt_call serial_dma_call; + hrt_call_every(&serial_dma_call, 1000, 1000, (hrt_callout)stm32_serial_dma_poll, NULL); +#endif + + /* initial LED state */ + drv_led_start(); + led_off(LED_RED); + led_on(LED_GREEN); // Indicate Power. + led_off(LED_BLUE); + + if (board_hardfault_init(2, true) != 0) { + led_on(LED_RED); + } + + // Ensure Power is off for > 10 mS + usleep(15 * 1000); + VDD_3V3_SD_CARD_EN(true); + usleep(500 * 1000); + +#ifdef CONFIG_MMCSD + int ret = stm32_sdio_initialize(); + + if (ret != OK) { + led_on(LED_RED); + } + +#endif /* CONFIG_MMCSD */ + + /* Configure the HW based on the manifest */ + + px4_platform_configure(); + + return OK; +} diff --git a/boards/ark/fmu-v6x/src/led.c b/boards/ark/fmu-v6x/src/led.c new file mode 100644 index 0000000000..bff4b09864 --- /dev/null +++ b/boards/ark/fmu-v6x/src/led.c @@ -0,0 +1,235 @@ +/**************************************************************************** + * + * Copyright (c) 2013 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file px4fmu2_led.c + * + * PX4FMU LED backend. + */ + +#include + +#include + +#include "chip.h" +#include "stm32_gpio.h" +#include "board_config.h" + +#include +#include + +/* + * Ideally we'd be able to get these from arm_internal.h, + * but since we want to be able to disable the NuttX use + * of leds for system indication at will and there is no + * separate switch, we need to build independent of the + * CONFIG_ARCH_LEDS configuration switch. + */ +__BEGIN_DECLS +extern void led_init(void); +extern void led_on(int led); +extern void led_off(int led); +extern void led_toggle(int led); +__END_DECLS + +#ifdef CONFIG_ARCH_LEDS +static bool nuttx_owns_leds = true; +// B R S G +// 0 1 2 3 +static const uint8_t xlatpx4[] = {1, 2, 4, 0}; +# define xlat(p) xlatpx4[(p)] +static uint32_t g_ledmap[] = { + GPIO_nLED_GREEN, // Indexed by BOARD_LED_GREEN + GPIO_nLED_BLUE, // Indexed by BOARD_LED_BLUE + GPIO_nLED_RED, // Indexed by BOARD_LED_RED + GPIO_nSAFETY_SWITCH_LED_OUT, // Indexed by LED_SAFETY by xlatpx4 +}; + +#else + +# define xlat(p) (p) +static uint32_t g_ledmap[] = { + GPIO_nLED_BLUE, // Indexed by LED_BLUE + GPIO_nLED_RED, // Indexed by LED_RED, LED_AMBER + 0, // Indexed by LED_SAFETY (defaulted to an input) + GPIO_nLED_GREEN, // Indexed by LED_GREEN +}; + +#endif + +__EXPORT void led_init(void) +{ + for (size_t l = 0; l < (sizeof(g_ledmap) / sizeof(g_ledmap[0])); l++) { + if (g_ledmap[l] != 0) { + stm32_configgpio(g_ledmap[l]); + } + } +} + +static void phy_set_led(int led, bool state) +{ + /* Drive Low to switch on */ + + if (g_ledmap[led] != 0) { + stm32_gpiowrite(g_ledmap[led], !state); + } +} + +static bool phy_get_led(int led) +{ + /* If Low it is on */ + if (g_ledmap[led] != 0) { + return !stm32_gpioread(g_ledmap[led]); + } + + return false; +} + +__EXPORT void led_on(int led) +{ + phy_set_led(xlat(led), true); +} + +__EXPORT void led_off(int led) +{ + phy_set_led(xlat(led), false); +} + +__EXPORT void led_toggle(int led) +{ + phy_set_led(xlat(led), !phy_get_led(xlat(led))); +} + +#ifdef CONFIG_ARCH_LEDS +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_autoled_initialize + ****************************************************************************/ + +void board_autoled_initialize(void) +{ + led_init(); +} + +/**************************************************************************** + * Name: board_autoled_on + ****************************************************************************/ + +void board_autoled_on(int led) +{ + if (!nuttx_owns_leds) { + return; + } + + switch (led) { + default: + break; + + case LED_HEAPALLOCATE: + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_IRQSENABLED: + phy_set_led(BOARD_LED_BLUE, false); + phy_set_led(BOARD_LED_GREEN, true); + break; + + case LED_STACKCREATED: + phy_set_led(BOARD_LED_GREEN, true); + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_INIRQ: + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_SIGNAL: + phy_set_led(BOARD_LED_GREEN, true); + break; + + case LED_ASSERTION: + phy_set_led(BOARD_LED_RED, true); + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_PANIC: + phy_set_led(BOARD_LED_RED, true); + break; + + case LED_IDLE : /* IDLE */ + phy_set_led(BOARD_LED_RED, true); + break; + } +} + +/**************************************************************************** + * Name: board_autoled_off + ****************************************************************************/ + +void board_autoled_off(int led) +{ + if (!nuttx_owns_leds) { + return; + } + + switch (led) { + default: + break; + + case LED_SIGNAL: + phy_set_led(BOARD_LED_GREEN, false); + break; + + case LED_INIRQ: + phy_set_led(BOARD_LED_BLUE, false); + break; + + case LED_ASSERTION: + phy_set_led(BOARD_LED_RED, false); + phy_set_led(BOARD_LED_BLUE, false); + break; + + case LED_PANIC: + phy_set_led(BOARD_LED_RED, false); + break; + + case LED_IDLE : /* IDLE */ + phy_set_led(BOARD_LED_RED, false); + break; + } +} + +#endif /* CONFIG_ARCH_LEDS */ diff --git a/boards/ark/fmu-v6x/src/manifest.c b/boards/ark/fmu-v6x/src/manifest.c new file mode 100644 index 0000000000..3a4fc93bff --- /dev/null +++ b/boards/ark/fmu-v6x/src/manifest.c @@ -0,0 +1,150 @@ +/**************************************************************************** + * + * Copyright (c) 2018, 2021 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file manifest.c + * + * This module supplies the interface to the manifest of hardware that is + * optional and dependent on the HW REV and HW VER IDs + * + * The manifest allows the system to know whether a hardware option + * say for example the PX4IO is an no-pop option vs it is broken. + * + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "systemlib/px4_macros.h" + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +typedef struct { + uint32_t hw_ver_rev; /* the version and revision */ + const px4_hw_mft_item_t *mft; /* The first entry */ + uint32_t entries; /* the lenght of the list */ +} px4_hw_mft_list_entry_t; + +typedef px4_hw_mft_list_entry_t *px4_hw_mft_list_entry; +#define px4_hw_mft_list_uninitialized (px4_hw_mft_list_entry) -1 + +static const px4_hw_mft_item_t device_unsupported = {0, 0, 0}; + +// List of components on a specific board configuration +// The index of those components is given by the enum (px4_hw_mft_item_id_t) +// declared in board_common.h +static const px4_hw_mft_item_t hw_mft_list_v0600[] = { + { + .present = 1, + .mandatory = 1, + .connection = px4_hw_con_onboard, + }, +}; + +static const px4_hw_mft_item_t hw_mft_list_v0610[] = { + { + .present = 0, + .mandatory = 0, + .connection = px4_hw_con_unknown, + }, + { + .present = 1, + .mandatory = 1, + .connection = px4_hw_con_onboard, + }, +}; + + +static px4_hw_mft_list_entry_t mft_lists[] = { +// ver_rev + {V6X00, hw_mft_list_v0600, arraySize(hw_mft_list_v0600)}, + {V6X10, hw_mft_list_v0610, arraySize(hw_mft_list_v0610)}, // No PX4IO + {V6X01, hw_mft_list_v0600, arraySize(hw_mft_list_v0600)}, // BMP388 moved to I2C2 + {V6X03, hw_mft_list_v0600, arraySize(hw_mft_list_v0600)}, // BMP388 moved to I2C2, Sensor Set 3 + {V6X13, hw_mft_list_v0610, arraySize(hw_mft_list_v0610)}, // No PX4IO BMP388 moved to I2C2, Sensor Set 3 +}; + +/************************************************************************************ + * Name: board_query_manifest + * + * Description: + * Optional returns manifest item. + * + * Input Parameters: + * manifest_id - the ID for the manifest item to retrieve + * + * Returned Value: + * 0 - item is not in manifest => assume legacy operations + * pointer to a manifest item + * + ************************************************************************************/ + +__EXPORT px4_hw_mft_item board_query_manifest(px4_hw_mft_item_id_t id) +{ + static px4_hw_mft_list_entry boards_manifest = px4_hw_mft_list_uninitialized; + + if (boards_manifest == px4_hw_mft_list_uninitialized) { + uint32_t ver_rev = board_get_hw_version() << 8; + ver_rev |= board_get_hw_revision(); + + for (unsigned i = 0; i < arraySize(mft_lists); i++) { + if (mft_lists[i].hw_ver_rev == ver_rev) { + boards_manifest = &mft_lists[i]; + break; + } + } + + if (boards_manifest == px4_hw_mft_list_uninitialized) { + syslog(LOG_ERR, "[boot] Board %4" PRIx32 " is not supported!\n", ver_rev); + } + } + + px4_hw_mft_item rv = &device_unsupported; + + if (boards_manifest != px4_hw_mft_list_uninitialized && + id < boards_manifest->entries) { + rv = &boards_manifest->mft[id]; + } + + return rv; +} diff --git a/boards/ark/fmu-v6x/src/mtd.cpp b/boards/ark/fmu-v6x/src/mtd.cpp new file mode 100644 index 0000000000..ce047df328 --- /dev/null +++ b/boards/ark/fmu-v6x/src/mtd.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** + * + * Copyright (C) 2020 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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 +#include +// KiB BS nB +static const px4_mft_device_t spi5 = { // FM25V02A on FMUM 32K 512 X 64 + .bus_type = px4_mft_device_t::SPI, + .devid = SPIDEV_FLASH(0) +}; +static const px4_mft_device_t i2c3 = { // 24LC64T on Base 8K 32 X 256 + .bus_type = px4_mft_device_t::I2C, + .devid = PX4_MK_I2C_DEVID(3, 0x51) +}; +static const px4_mft_device_t i2c4 = { // 24LC64T on IMU 8K 32 X 256 + .bus_type = px4_mft_device_t::I2C, + .devid = PX4_MK_I2C_DEVID(4, 0x50) +}; + + +static const px4_mtd_entry_t fmum_fram = { + .device = &spi5, + .npart = 2, + .partd = { + { + .type = MTD_PARAMETERS, + .path = "/fs/mtd_params", + .nblocks = 32 + }, + { + .type = MTD_WAYPOINTS, + .path = "/fs/mtd_waypoints", + .nblocks = 32 + + } + }, +}; + +static const px4_mtd_entry_t base_eeprom = { + .device = &i2c3, + .npart = 2, + .partd = { + { + .type = MTD_MFT, + .path = "/fs/mtd_mft", + .nblocks = 248 + }, + { + .type = MTD_NET, + .path = "/fs/mtd_net", + .nblocks = 8 // 256 = 32 * 8 + + } + }, +}; + +static const px4_mtd_manifest_t board_mtd_config = { + .nconfigs = 2, + .entries = { + &fmum_fram, + &base_eeprom, + } +}; + +static const px4_mft_entry_s mtd_mft = { + .type = MTD, + .pmft = (void *) &board_mtd_config, +}; + +static const px4_mft_s mft = { + .nmft = 1, + .mfts = &mtd_mft +}; + +const px4_mft_s *board_get_manifest(void) +{ + return &mft; +} diff --git a/boards/ark/fmu-v6x/src/sdio.c b/boards/ark/fmu-v6x/src/sdio.c new file mode 100644 index 0000000000..869d757756 --- /dev/null +++ b/boards/ark/fmu-v6x/src/sdio.c @@ -0,0 +1,177 @@ +/**************************************************************************** + * + * Copyright (C) 2014, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "board_config.h" +#include "stm32_gpio.h" +#include "stm32_sdmmc.h" + +#ifdef CONFIG_MMCSD + + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Card detections requires card support and a card detection GPIO */ + +#define HAVE_NCD 1 +#if !defined(GPIO_SDMMC1_NCD) +# undef HAVE_NCD +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR struct sdio_dev_s *sdio_dev; +#ifdef HAVE_NCD +static bool g_sd_inserted = 0xff; /* Impossible value */ +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_ncd_interrupt + * + * Description: + * Card detect interrupt handler. + * + ****************************************************************************/ + +#ifdef HAVE_NCD +static int stm32_ncd_interrupt(int irq, FAR void *context) +{ + bool present; + + present = !stm32_gpioread(GPIO_SDMMC1_NCD); + + if (sdio_dev && present != g_sd_inserted) { + sdio_mediachange(sdio_dev, present); + g_sd_inserted = present; + } + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_sdio_initialize + * + * Description: + * Initialize SDIO-based MMC/SD card support + * + ****************************************************************************/ + +int stm32_sdio_initialize(void) +{ + int ret; + +#ifdef HAVE_NCD + /* Card detect */ + + bool cd_status; + + /* Configure the card detect GPIO */ + + stm32_configgpio(GPIO_SDMMC1_NCD); + + /* Register an interrupt handler for the card detect pin */ + + stm32_gpiosetevent(GPIO_SDMMC1_NCD, true, true, true, stm32_ncd_interrupt); +#endif + + /* Mount the SDIO-based MMC/SD block driver */ + /* First, get an instance of the SDIO interface */ + + finfo("Initializing SDIO slot %d\n", SDIO_SLOTNO); + + sdio_dev = sdio_initialize(SDIO_SLOTNO); + + if (!sdio_dev) { + syslog(LOG_ERR, "[boot] Failed to initialize SDIO slot %d\n", SDIO_SLOTNO); + return -ENODEV; + } + + /* Now bind the SDIO interface to the MMC/SD driver */ + + finfo("Bind SDIO to the MMC/SD driver, minor=%d\n", SDIO_MINOR); + + ret = mmcsd_slotinitialize(SDIO_MINOR, sdio_dev); + + if (ret != OK) { + syslog(LOG_ERR, "[boot] Failed to bind SDIO to the MMC/SD driver: %d\n", ret); + return ret; + } + + finfo("Successfully bound SDIO to the MMC/SD driver\n"); + +#ifdef HAVE_NCD + /* Use SD card detect pin to check if a card is g_sd_inserted */ + + cd_status = !stm32_gpioread(GPIO_SDMMC1_NCD); + finfo("Card detect : %d\n", cd_status); + + sdio_mediachange(sdio_dev, cd_status); +#else + /* Assume that the SD card is inserted. What choice do we have? */ + + sdio_mediachange(sdio_dev, true); +#endif + + return OK; +} + +#endif /* CONFIG_MMCSD */ diff --git a/boards/ark/fmu-v6x/src/spi.cpp b/boards/ark/fmu-v6x/src/spi.cpp new file mode 100644 index 0000000000..0e08f86bec --- /dev/null +++ b/boards/ark/fmu-v6x/src/spi.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * Copyright (C) 2020, 2022 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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 +#include +#include + +constexpr px4_spi_bus_all_hw_t px4_spi_buses_all_hw[BOARD_NUM_SPI_CFG_HW_VERSIONS] = { + initSPIHWVersion(HW_VER_REV(0, 0), { + initSPIBus(SPI::Bus::SPI1, { + initSPIDevice(DRV_IMU_DEVTYPE_IIM42652, SPI::CS{GPIO::PortI, GPIO::Pin9}, SPI::DRDY{GPIO::PortF, GPIO::Pin2}), + }, {GPIO::PortI, GPIO::Pin11}), + initSPIBus(SPI::Bus::SPI2, { + initSPIDevice(DRV_IMU_DEVTYPE_ICM42688P, SPI::CS{GPIO::PortH, GPIO::Pin5}, SPI::DRDY{GPIO::PortA, GPIO::Pin10}), + }, {GPIO::PortF, GPIO::Pin4}), + initSPIBus(SPI::Bus::SPI3, { + initSPIDevice(DRV_IMU_DEVTYPE_ICM42688P, SPI::CS{GPIO::PortI, GPIO::Pin4}, SPI::DRDY{GPIO::PortI, GPIO::Pin6}), + }, {GPIO::PortE, GPIO::Pin7}), + // initSPIBus(SPI::Bus::SPI4, { + // // no devices + // TODO: if enabled, remove GPIO_VDD_3V3_SENSORS4_EN from board_config.h + // }, {GPIO::PortG, GPIO::Pin8}), + initSPIBus(SPI::Bus::SPI5, { + initSPIDevice(SPIDEV_FLASH(0), SPI::CS{GPIO::PortG, GPIO::Pin7}) + }), + initSPIBusExternal(SPI::Bus::SPI6, { + initSPIConfigExternal(SPI::CS{GPIO::PortI, GPIO::Pin10}, SPI::DRDY{GPIO::PortD, GPIO::Pin11}), + initSPIConfigExternal(SPI::CS{GPIO::PortA, GPIO::Pin15}, SPI::DRDY{GPIO::PortD, GPIO::Pin12}), + }), + }), + + initSPIHWVersion(HW_VER_REV(0, 3), { + initSPIBus(SPI::Bus::SPI1, { + initSPIDevice(DRV_IMU_DEVTYPE_ICM42670P, SPI::CS{GPIO::PortI, GPIO::Pin9}, SPI::DRDY{GPIO::PortF, GPIO::Pin2}), + }, {GPIO::PortI, GPIO::Pin11}), + initSPIBus(SPI::Bus::SPI2, { + initSPIDevice(DRV_IMU_DEVTYPE_ICM42688P, SPI::CS{GPIO::PortH, GPIO::Pin5}, SPI::DRDY{GPIO::PortA, GPIO::Pin10}), + }, {GPIO::PortF, GPIO::Pin4}), + initSPIBus(SPI::Bus::SPI3, { + initSPIDevice(DRV_GYR_DEVTYPE_BMI088, SPI::CS{GPIO::PortI, GPIO::Pin8}, SPI::DRDY{GPIO::PortI, GPIO::Pin7}), + initSPIDevice(DRV_ACC_DEVTYPE_BMI088, SPI::CS{GPIO::PortI, GPIO::Pin4}, SPI::DRDY{GPIO::PortI, GPIO::Pin6}), + }, {GPIO::PortE, GPIO::Pin7}), + // initSPIBus(SPI::Bus::SPI4, { + // // no devices + // TODO: if enabled, remove GPIO_VDD_3V3_SENSORS4_EN from board_config.h + // }, {GPIO::PortG, GPIO::Pin8}), + initSPIBus(SPI::Bus::SPI5, { + initSPIDevice(SPIDEV_FLASH(0), SPI::CS{GPIO::PortG, GPIO::Pin7}) + }), + initSPIBusExternal(SPI::Bus::SPI6, { + initSPIConfigExternal(SPI::CS{GPIO::PortI, GPIO::Pin10}, SPI::DRDY{GPIO::PortD, GPIO::Pin11}), + initSPIConfigExternal(SPI::CS{GPIO::PortA, GPIO::Pin15}, SPI::DRDY{GPIO::PortD, GPIO::Pin12}), + }), + }), +}; + +static constexpr bool unused = validateSPIConfig(px4_spi_buses_all_hw); diff --git a/boards/ark/fmu-v6x/src/timer_config.cpp b/boards/ark/fmu-v6x/src/timer_config.cpp new file mode 100644 index 0000000000..928f4916f0 --- /dev/null +++ b/boards/ark/fmu-v6x/src/timer_config.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** + * + * Copyright (C) 2012 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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 + +/* Timer allocation + * + * TIM5_CH4 T FMU_CH1 + * TIM5_CH3 T FMU_CH2 + * TIM5_CH2 T FMU_CH3 + * TIM5_CH1 T FMU_CH4 + * + * TIM4_CH2 T FMU_CH5 + * TIM4_CH3 T FMU_CH6 + * + * TIM12_CH1 T FMU_CH7 + * TIM12_CH2 T FMU_CH8 + * + * TIM1_CH2 T FMU_CAP1 < Capture + * TIM1_CH3 T SPI2_DRDY2_ISM330_INT2 < Capture or GPIO INT + * TIM1_CH1 T SPIX_SYNC > Pulse or GPIO strobe + * + * TIM2_CH3 T HEATER > PWM OUT or GPIO + * + * TIM14_CH1 T BUZZER_1 - Driven by other driver + * TIM8_CH1_IN T FMU_PPM_INPUT - Sampled byt HRT by other driver + */ + +constexpr io_timers_t io_timers[MAX_IO_TIMERS] = { + initIOTimer(Timer::Timer5, DMA{DMA::Index1}), + initIOTimer(Timer::Timer4, DMA{DMA::Index1}), + initIOTimer(Timer::Timer12), + initIOTimer(Timer::Timer1), + initIOTimer(Timer::Timer2), +}; + +constexpr timer_io_channels_t timer_io_channels[MAX_TIMER_IO_CHANNELS] = { + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel4}, {GPIO::PortI, GPIO::Pin0}), + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel3}, {GPIO::PortH, GPIO::Pin12}), + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel2}, {GPIO::PortH, GPIO::Pin11}), + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel1}, {GPIO::PortH, GPIO::Pin10}), + initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel2}, {GPIO::PortD, GPIO::Pin13}), + initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel3}, {GPIO::PortD, GPIO::Pin14}), + initIOTimerChannel(io_timers, {Timer::Timer12, Timer::Channel1}, {GPIO::PortH, GPIO::Pin6}), + initIOTimerChannel(io_timers, {Timer::Timer12, Timer::Channel2}, {GPIO::PortH, GPIO::Pin9}), + initIOTimerChannelCapture(io_timers, {Timer::Timer1, Timer::Channel2}, {GPIO::PortE, GPIO::Pin11}), +}; + +constexpr io_timers_channel_mapping_t io_timers_channel_mapping = + initIOTimerChannelMapping(io_timers, timer_io_channels); diff --git a/boards/ark/fmu-v6x/src/usb.c b/boards/ark/fmu-v6x/src/usb.c new file mode 100644 index 0000000000..9d5915c0e6 --- /dev/null +++ b/boards/ark/fmu-v6x/src/usb.c @@ -0,0 +1,105 @@ +/**************************************************************************** + * + * Copyright (C) 2016 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file px4fmu_usb.c + * + * Board-specific USB functions. + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "board_config.h" + +/************************************************************************************ + * Definitions + ************************************************************************************/ + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_usbinitialize + * + * Description: + * Called to setup USB-related GPIO pins for the PX4FMU board. + * + ************************************************************************************/ + +__EXPORT void stm32_usbinitialize(void) +{ + /* The OTG FS has an internal soft pull-up */ + + /* Configure the OTG FS VBUS sensing GPIO, Power On, and Overcurrent GPIOs */ + +#ifdef CONFIG_STM32H7_OTGFS + stm32_configgpio(GPIO_OTGFS_VBUS); +#endif +} + +/************************************************************************************ + * Name: stm32_usbsuspend + * + * Description: + * Board logic must provide the stm32_usbsuspend logic if the USBDEV driver is + * used. This function is called whenever the USB enters or leaves suspend mode. + * This is an opportunity for the board logic to shutdown clocks, power, etc. + * while the USB is suspended. + * + ************************************************************************************/ + +__EXPORT void stm32_usbsuspend(FAR struct usbdev_s *dev, bool resume) +{ + uinfo("resume: %d\n", resume); +} diff --git a/src/drivers/drv_sensor.h b/src/drivers/drv_sensor.h index 360c5b9913..b0e45b7655 100644 --- a/src/drivers/drv_sensor.h +++ b/src/drivers/drv_sensor.h @@ -68,6 +68,7 @@ #define DRV_IMU_DEVTYPE_SIM 0x14 #define DRV_DIFF_PRESS_DEVTYPE_SIM 0x15 +#define DRV_FLOW_DEVTYPE_SIM 0x16 #define DRV_IMU_DEVTYPE_MPU6000 0x21 #define DRV_GYR_DEVTYPE_L3GD20 0x22 @@ -78,6 +79,7 @@ #define DRV_IMU_DEVTYPE_ICM20948 0x28 #define DRV_IMU_DEVTYPE_ICM42605 0x29 #define DRV_IMU_DEVTYPE_ICM42670P 0x2A +#define DRV_IMU_DEVTYPE_IIM42652 0x2B #define DRV_RNG_DEVTYPE_MB12XX 0x31 #define DRV_RNG_DEVTYPE_LL40LS 0x32 @@ -126,21 +128,19 @@ #define DRV_BARO_DEVTYPE_MPC2520 0x5F #define DRV_BARO_DEVTYPE_LPS22HB 0x60 -#define DRV_ACC_DEVTYPE_LSM303AGR 0x61 -#define DRV_MAG_DEVTYPE_LSM303AGR 0x62 -#define DRV_IMU_DEVTYPE_ADIS16497 0x63 -#define DRV_BARO_DEVTYPE_BAROSIM 0x65 -#define DRV_GYR_DEVTYPE_BMI088 0x66 -#define DRV_BARO_DEVTYPE_BMP388 0x67 -#define DRV_BARO_DEVTYPE_DPS310 0x68 -#define DRV_PWM_DEVTYPE_PCA9685 0x69 -#define DRV_ACC_DEVTYPE_BMI088 0x6a -#define DRV_OSD_DEVTYPE_ATXXXX 0x6b -#define DRV_FLOW_DEVTYPE_PMW3901 0x6c -#define DRV_FLOW_DEVTYPE_PAW3902 0x6d -#define DRV_FLOW_DEVTYPE_PX4FLOW 0x6e -#define DRV_FLOW_DEVTYPE_PAA3905 0x6f - +#define DRV_ACC_DEVTYPE_LSM303AGR 0x61 +#define DRV_MAG_DEVTYPE_LSM303AGR 0x62 +#define DRV_IMU_DEVTYPE_ADIS16497 0x63 +#define DRV_BARO_DEVTYPE_BAROSIM 0x65 +#define DRV_GYR_DEVTYPE_BMI088 0x66 +#define DRV_BARO_DEVTYPE_BMP388 0x67 +#define DRV_BARO_DEVTYPE_DPS310 0x68 +#define DRV_PWM_DEVTYPE_PCA9685 0x69 +#define DRV_ACC_DEVTYPE_BMI088 0x6a +#define DRV_OSD_DEVTYPE_ATXXXX 0x6b +#define DRV_ACC_DEVTYPE_BMI085 0x6C +#define DRV_GYR_DEVTYPE_BMI085 0x6D +#define DRV_BARO_DEVTYPE_BMP390 0x6E #define DRV_DIST_DEVTYPE_LL40LS 0x70 #define DRV_DIST_DEVTYPE_MAPPYDOT 0x71 @@ -206,8 +206,19 @@ #define DRV_GPS_DEVTYPE_SIM 0xAF +#define DRV_TRNS_DEVTYPE_MXS 0xB0 + #define DRV_HYGRO_DEVTYPE_SHT3X 0xB1 +#define DRV_FLOW_DEVTYPE_MAVLINK 0xB2 +#define DRV_FLOW_DEVTYPE_PMW3901 0xB3 +#define DRV_FLOW_DEVTYPE_PAW3902 0xB4 +#define DRV_FLOW_DEVTYPE_PX4FLOW 0xB5 +#define DRV_FLOW_DEVTYPE_PAA3905 0xB6 + +#define DRV_BARO_DEVTYPE_ICP10100 0xC0 +#define DRV_BARO_DEVTYPE_ICP10111 0xC1 + #define DRV_DEVTYPE_UNUSED 0xff #endif /* _DRV_SENSOR_H */ diff --git a/src/drivers/imu/invensense/iim42652/CMakeLists.txt b/src/drivers/imu/invensense/iim42652/CMakeLists.txt new file mode 100644 index 0000000000..1ce153c6f6 --- /dev/null +++ b/src/drivers/imu/invensense/iim42652/CMakeLists.txt @@ -0,0 +1,47 @@ +############################################################################ +# +# Copyright (c) 2022 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 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. +# +############################################################################ +px4_add_module( + MODULE drivers__imu__invensense__iim42652 + MAIN iim42652 + COMPILE_FLAGS + ${MAX_CUSTOM_OPT_LEVEL} + SRCS + iim42652_main.cpp + IIM42652.cpp + IIM42652.hpp + InvenSense_IIM42652_registers.hpp + DEPENDS + px4_work_queue + drivers_accelerometer + drivers_gyroscope + ) diff --git a/src/drivers/imu/invensense/iim42652/IIM42652.cpp b/src/drivers/imu/invensense/iim42652/IIM42652.cpp new file mode 100644 index 0000000000..dabd688715 --- /dev/null +++ b/src/drivers/imu/invensense/iim42652/IIM42652.cpp @@ -0,0 +1,800 @@ +/**************************************************************************** + * + * Copyright (c) 2022 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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 "IIM42652.hpp" + +using namespace time_literals; + +static constexpr int16_t combine(uint8_t msb, uint8_t lsb) +{ + return (msb << 8u) | lsb; +} + +IIM42652::IIM42652(const I2CSPIDriverConfig &config) : + SPI(config), + I2CSPIDriver(config), + _drdy_gpio(config.drdy_gpio), + _px4_accel(get_device_id(), config.rotation), + _px4_gyro(get_device_id(), config.rotation) +{ + if (config.drdy_gpio != 0) { + _drdy_missed_perf = perf_alloc(PC_COUNT, MODULE_NAME": DRDY missed"); + } + + ConfigureSampleRate(_px4_gyro.get_max_rate_hz()); +} + +IIM42652::~IIM42652() +{ + perf_free(_bad_register_perf); + perf_free(_bad_transfer_perf); + perf_free(_fifo_empty_perf); + perf_free(_fifo_overflow_perf); + perf_free(_fifo_reset_perf); + perf_free(_drdy_missed_perf); +} + +int IIM42652::init() +{ + int ret = SPI::init(); + + if (ret != PX4_OK) { + DEVICE_DEBUG("SPI::init failed (%i)", ret); + return ret; + } + + return Reset() ? 0 : -1; +} + +bool IIM42652::Reset() +{ + _state = STATE::RESET; + DataReadyInterruptDisable(); + ScheduleClear(); + ScheduleNow(); + return true; +} + +void IIM42652::exit_and_cleanup() +{ + DataReadyInterruptDisable(); + I2CSPIDriverBase::exit_and_cleanup(); +} + +void IIM42652::print_status() +{ + I2CSPIDriverBase::print_status(); + + PX4_INFO("FIFO empty interval: %d us (%.1f Hz)", _fifo_empty_interval_us, 1e6 / _fifo_empty_interval_us); + + perf_print_counter(_bad_register_perf); + perf_print_counter(_bad_transfer_perf); + perf_print_counter(_fifo_empty_perf); + perf_print_counter(_fifo_overflow_perf); + perf_print_counter(_fifo_reset_perf); + perf_print_counter(_drdy_missed_perf); +} + +int IIM42652::probe() +{ + for (int i = 0; i < 3; i++) { + uint8_t whoami = RegisterRead(Register::BANK_0::WHO_AM_I); + + if (whoami == WHOAMI) { + return PX4_OK; + + } else { + DEVICE_DEBUG("unexpected WHO_AM_I 0x%02x", whoami); + + uint8_t reg_bank_sel = RegisterRead(Register::BANK_0::REG_BANK_SEL); + int bank = reg_bank_sel >> 4; + + if (bank >= 1 && bank <= 3) { + DEVICE_DEBUG("incorrect register bank for WHO_AM_I REG_BANK_SEL:0x%02x, bank:%d", reg_bank_sel, bank); + // force bank selection and retry + SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0, true); + } + } + } + + return PX4_ERROR; +} + +void IIM42652::RunImpl() +{ + const hrt_abstime now = hrt_absolute_time(); + + switch (_state) { + case STATE::RESET: + // DEVICE_CONFIG: Software reset configuration + RegisterWrite(Register::BANK_0::DEVICE_CONFIG, DEVICE_CONFIG_BIT::SOFT_RESET_CONFIG); + _reset_timestamp = now; + _failure_count = 0; + _state = STATE::WAIT_FOR_RESET; + ScheduleDelayed(1_ms); // wait 1 ms for soft reset to be effective + break; + + case STATE::WAIT_FOR_RESET: + if ((RegisterRead(Register::BANK_0::WHO_AM_I) == WHOAMI) + && (RegisterRead(Register::BANK_0::DEVICE_CONFIG) == 0x00) + && (RegisterRead(Register::BANK_0::INT_STATUS) & INT_STATUS_BIT::RESET_DONE_INT)) { + + // Wakeup accel and gyro and schedule remaining configuration + RegisterWrite(Register::BANK_0::PWR_MGMT0, PWR_MGMT0_BIT::GYRO_MODE_LOW_NOISE | PWR_MGMT0_BIT::ACCEL_MODE_LOW_NOISE); + _state = STATE::CONFIGURE; + ScheduleDelayed(30_ms); // 30 ms gyro startup time, 10 ms accel from sleep to valid data + + } else { + // RESET not complete + if (hrt_elapsed_time(&_reset_timestamp) > 1000_ms) { + PX4_DEBUG("Reset failed, retrying"); + _state = STATE::RESET; + ScheduleDelayed(100_ms); + + } else { + PX4_DEBUG("Reset not complete, check again in 10 ms"); + ScheduleDelayed(10_ms); + } + } + + break; + + case STATE::CONFIGURE: + if (Configure()) { + // if configure succeeded then start reading from FIFO + _state = STATE::FIFO_READ; + + if (DataReadyInterruptConfigure()) { + _data_ready_interrupt_enabled = true; + + // backup schedule as a watchdog timeout + ScheduleDelayed(100_ms); + + } else { + _data_ready_interrupt_enabled = false; + ScheduleOnInterval(_fifo_empty_interval_us, _fifo_empty_interval_us); + } + + FIFOReset(); + + } else { + // CONFIGURE not complete + if (hrt_elapsed_time(&_reset_timestamp) > 1000_ms) { + PX4_DEBUG("Configure failed, resetting"); + _state = STATE::RESET; + + } else { + PX4_DEBUG("Configure failed, retrying"); + } + + ScheduleDelayed(100_ms); + } + + break; + + case STATE::FIFO_READ: { + hrt_abstime timestamp_sample = now; + uint8_t samples = 0; + + if (_data_ready_interrupt_enabled) { + // scheduled from interrupt if _drdy_timestamp_sample was set as expected + const hrt_abstime drdy_timestamp_sample = _drdy_timestamp_sample.fetch_and(0); + + if ((now - drdy_timestamp_sample) < _fifo_empty_interval_us) { + timestamp_sample = drdy_timestamp_sample; + samples = _fifo_gyro_samples; + + } else { + perf_count(_drdy_missed_perf); + } + + // push backup schedule back + ScheduleDelayed(_fifo_empty_interval_us * 2); + } + + if (samples == 0) { + // check current FIFO count + const uint16_t fifo_count = FIFOReadCount(); + + if (fifo_count >= FIFO::SIZE) { + FIFOReset(); + perf_count(_fifo_overflow_perf); + + } else if (fifo_count == 0) { + perf_count(_fifo_empty_perf); + + } else { + // FIFO count (size in bytes) + samples = (fifo_count / sizeof(FIFO::DATA)); + + // tolerate minor jitter, leave sample to next iteration if behind by only 1 + if (samples == _fifo_gyro_samples + 1) { + timestamp_sample -= static_cast(FIFO_SAMPLE_DT); + samples--; + } + + if (samples > FIFO_MAX_SAMPLES) { + // not technically an overflow, but more samples than we expected or can publish + FIFOReset(); + perf_count(_fifo_overflow_perf); + samples = 0; + } + } + } + + bool success = false; + + if (samples >= 1) { + if (FIFORead(timestamp_sample, samples)) { + success = true; + + if (_failure_count > 0) { + _failure_count--; + } + } + } + + if (!success) { + _failure_count++; + + // full reset if things are failing consistently + if (_failure_count > 10) { + Reset(); + return; + } + } + + if (!success || hrt_elapsed_time(&_last_config_check_timestamp) > 100_ms) { + // check configuration registers periodically or immediately following any failure + if (RegisterCheck(_register_bank0_cfg[_checked_register_bank0]) + && RegisterCheck(_register_bank1_cfg[_checked_register_bank1]) + && RegisterCheck(_register_bank2_cfg[_checked_register_bank2]) + ) { + _last_config_check_timestamp = now; + _checked_register_bank0 = (_checked_register_bank0 + 1) % size_register_bank0_cfg; + _checked_register_bank1 = (_checked_register_bank1 + 1) % size_register_bank1_cfg; + _checked_register_bank2 = (_checked_register_bank2 + 1) % size_register_bank2_cfg; + + } else { + // register check failed, force reset + perf_count(_bad_register_perf); + Reset(); + } + } + } + + break; + } +} + +void IIM42652::ConfigureSampleRate(int sample_rate) +{ + // round down to nearest FIFO sample dt + const float min_interval = FIFO_SAMPLE_DT; + _fifo_empty_interval_us = math::max(roundf((1e6f / (float)sample_rate) / min_interval) * min_interval, min_interval); + + _fifo_gyro_samples = roundf(math::min((float)_fifo_empty_interval_us / (1e6f / GYRO_RATE), (float)FIFO_MAX_SAMPLES)); + + // recompute FIFO empty interval (us) with actual gyro sample limit + _fifo_empty_interval_us = _fifo_gyro_samples * (1e6f / GYRO_RATE); + + ConfigureFIFOWatermark(_fifo_gyro_samples); +} + +void IIM42652::ConfigureFIFOWatermark(uint8_t samples) +{ + // FIFO watermark threshold in number of bytes + const uint16_t fifo_watermark_threshold = samples * sizeof(FIFO::DATA); + + for (auto &r : _register_bank0_cfg) { + if (r.reg == Register::BANK_0::FIFO_CONFIG2) { + // FIFO_WM[7:0] FIFO_CONFIG2 + r.set_bits = fifo_watermark_threshold & 0xFF; + + } else if (r.reg == Register::BANK_0::FIFO_CONFIG3) { + // FIFO_WM[11:8] FIFO_CONFIG3 + r.set_bits = (fifo_watermark_threshold >> 8) & 0x0F; + } + } +} + +void IIM42652::SelectRegisterBank(enum REG_BANK_SEL_BIT bank, bool force) +{ + if (bank != _last_register_bank || force) { + // select BANK_0 + uint8_t cmd_bank_sel[2] {}; + cmd_bank_sel[0] = static_cast(Register::BANK_0::REG_BANK_SEL); + cmd_bank_sel[1] = bank; + transfer(cmd_bank_sel, cmd_bank_sel, sizeof(cmd_bank_sel)); + + _last_register_bank = bank; + } +} + +bool IIM42652::Configure() +{ + // first set and clear all configured register bits + for (const auto ®_cfg : _register_bank0_cfg) { + RegisterSetAndClearBits(reg_cfg.reg, reg_cfg.set_bits, reg_cfg.clear_bits); + } + + for (const auto ®_cfg : _register_bank1_cfg) { + RegisterSetAndClearBits(reg_cfg.reg, reg_cfg.set_bits, reg_cfg.clear_bits); + } + + for (const auto ®_cfg : _register_bank2_cfg) { + RegisterSetAndClearBits(reg_cfg.reg, reg_cfg.set_bits, reg_cfg.clear_bits); + } + + // now check that all are configured + bool success = true; + + for (const auto ®_cfg : _register_bank0_cfg) { + if (!RegisterCheck(reg_cfg)) { + success = false; + } + } + + for (const auto ®_cfg : _register_bank1_cfg) { + if (!RegisterCheck(reg_cfg)) { + success = false; + } + } + + for (const auto ®_cfg : _register_bank2_cfg) { + if (!RegisterCheck(reg_cfg)) { + success = false; + } + } + + // 20-bits data format used + // the only FSR settings that are operational are ±2000dps for gyroscope and ±16g for accelerometer + _px4_accel.set_range(16.f * CONSTANTS_ONE_G); + _px4_gyro.set_range(math::radians(2000.f)); + + return success; +} + +int IIM42652::DataReadyInterruptCallback(int irq, void *context, void *arg) +{ + static_cast(arg)->DataReady(); + return 0; +} + +void IIM42652::DataReady() +{ + _drdy_timestamp_sample.store(hrt_absolute_time()); + ScheduleNow(); +} + +bool IIM42652::DataReadyInterruptConfigure() +{ + if (_drdy_gpio == 0) { + return false; + } + + // Setup data ready on falling edge + return px4_arch_gpiosetevent(_drdy_gpio, false, true, true, &DataReadyInterruptCallback, this) == 0; +} + +bool IIM42652::DataReadyInterruptDisable() +{ + if (_drdy_gpio == 0) { + return false; + } + + return px4_arch_gpiosetevent(_drdy_gpio, false, false, false, nullptr, nullptr) == 0; +} + +template +bool IIM42652::RegisterCheck(const T ®_cfg) +{ + bool success = true; + + const uint8_t reg_value = RegisterRead(reg_cfg.reg); + + if (reg_cfg.set_bits && ((reg_value & reg_cfg.set_bits) != reg_cfg.set_bits)) { + PX4_DEBUG("0x%02hhX: 0x%02hhX (0x%02hhX not set)", (uint8_t)reg_cfg.reg, reg_value, reg_cfg.set_bits); + success = false; + } + + if (reg_cfg.clear_bits && ((reg_value & reg_cfg.clear_bits) != 0)) { + PX4_DEBUG("0x%02hhX: 0x%02hhX (0x%02hhX not cleared)", (uint8_t)reg_cfg.reg, reg_value, reg_cfg.clear_bits); + success = false; + } + + return success; +} + +template +uint8_t IIM42652::RegisterRead(T reg) +{ + uint8_t cmd[2] {}; + cmd[0] = static_cast(reg) | DIR_READ; + SelectRegisterBank(reg); + transfer(cmd, cmd, sizeof(cmd)); + return cmd[1]; +} + +template +void IIM42652::RegisterWrite(T reg, uint8_t value) +{ + uint8_t cmd[2] { (uint8_t)reg, value }; + SelectRegisterBank(reg); + transfer(cmd, cmd, sizeof(cmd)); +} + +template +void IIM42652::RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbits) +{ + const uint8_t orig_val = RegisterRead(reg); + + uint8_t val = (orig_val & ~clearbits) | setbits; + + if (orig_val != val) { + RegisterWrite(reg, val); + } +} + +uint16_t IIM42652::FIFOReadCount() +{ + // read FIFO count + uint8_t fifo_count_buf[3] {}; + fifo_count_buf[0] = static_cast(Register::BANK_0::FIFO_COUNTH) | DIR_READ; + SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0); + + if (transfer(fifo_count_buf, fifo_count_buf, sizeof(fifo_count_buf)) != PX4_OK) { + perf_count(_bad_transfer_perf); + return 0; + } + + return combine(fifo_count_buf[1], fifo_count_buf[2]); +} + +bool IIM42652::FIFORead(const hrt_abstime ×tamp_sample, uint8_t samples) +{ + FIFOTransferBuffer buffer{}; + const size_t transfer_size = math::min(samples * sizeof(FIFO::DATA) + 4, FIFO::SIZE); + SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0); + + if (transfer((uint8_t *)&buffer, (uint8_t *)&buffer, transfer_size) != PX4_OK) { + perf_count(_bad_transfer_perf); + return false; + } + + if (buffer.INT_STATUS & INT_STATUS_BIT::FIFO_FULL_INT) { + perf_count(_fifo_overflow_perf); + FIFOReset(); + return false; + } + + const uint16_t fifo_count_bytes = combine(buffer.FIFO_COUNTH, buffer.FIFO_COUNTL); + + if (fifo_count_bytes >= FIFO::SIZE) { + perf_count(_fifo_overflow_perf); + FIFOReset(); + return false; + } + + const uint8_t fifo_count_samples = fifo_count_bytes / sizeof(FIFO::DATA); + + if (fifo_count_samples == 0) { + perf_count(_fifo_empty_perf); + return false; + } + + // check FIFO header in every sample + uint8_t valid_samples = 0; + + for (int i = 0; i < math::min(samples, fifo_count_samples); i++) { + bool valid = true; + + // With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8’b_0110_10xx + const uint8_t FIFO_HEADER = buffer.f[i].FIFO_Header; + + if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_MSG) { + // FIFO sample empty if HEADER_MSG set + valid = false; + + } else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ACCEL)) { + // accel bit not set + valid = false; + + } else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_GYRO)) { + // gyro bit not set + valid = false; + + } else if (!(FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_20)) { + // Packet does not contain a new and valid extended 20-bit data + valid = false; + + } else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_ACCEL) { + // accel ODR changed + valid = false; + + } else if (FIFO_HEADER & FIFO::FIFO_HEADER_BIT::HEADER_ODR_GYRO) { + // gyro ODR changed + valid = false; + } + + if (valid) { + valid_samples++; + + } else { + perf_count(_bad_transfer_perf); + break; + } + } + + if (valid_samples > 0) { + if (ProcessTemperature(buffer.f, valid_samples)) { + ProcessGyro(timestamp_sample, buffer.f, valid_samples); + ProcessAccel(timestamp_sample, buffer.f, valid_samples); + return true; + } + } + + return false; +} + +void IIM42652::FIFOReset() +{ + perf_count(_fifo_reset_perf); + + // SIGNAL_PATH_RESET: FIFO flush + RegisterSetBits(Register::BANK_0::SIGNAL_PATH_RESET, SIGNAL_PATH_RESET_BIT::FIFO_FLUSH); + + // reset while FIFO is disabled + _drdy_timestamp_sample.store(0); +} + +static constexpr int32_t reassemble_20bit(const uint32_t a, const uint32_t b, const uint32_t c) +{ + // 0xXXXAABBC + uint32_t high = ((a << 12) & 0x000FF000); + uint32_t low = ((b << 4) & 0x00000FF0); + uint32_t lowest = (c & 0x0000000F); + + uint32_t x = high | low | lowest; + + if (a & Bit7) { + // sign extend + x |= 0xFFF00000u; + } + + return static_cast(x); +} + +void IIM42652::ProcessAccel(const hrt_abstime ×tamp_sample, const FIFO::DATA fifo[], const uint8_t samples) +{ + sensor_accel_fifo_s accel{}; + accel.timestamp_sample = timestamp_sample; + accel.samples = 0; + accel.dt = FIFO_SAMPLE_DT; + + // 18-bits of accelerometer data + bool scale_20bit = false; + + // first pass + for (int i = 0; i < samples; i++) { + // 20 bit hires mode + // Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte) + // Accel data is 18 bit () + int32_t accel_x = reassemble_20bit(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0, + fifo[i].Ext_Accel_X_Gyro_X & 0xF0 >> 4); + int32_t accel_y = reassemble_20bit(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0, + fifo[i].Ext_Accel_Y_Gyro_Y & 0xF0 >> 4); + int32_t accel_z = reassemble_20bit(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0, + fifo[i].Ext_Accel_Z_Gyro_Z & 0xF0 >> 4); + + // sample invalid if -524288 + if (accel_x != -524288 && accel_y != -524288 && accel_z != -524288) { + // check if any values are going to exceed int16 limits + static constexpr int16_t max_accel = INT16_MAX; + static constexpr int16_t min_accel = INT16_MIN; + + if (accel_x >= max_accel || accel_x <= min_accel) { + scale_20bit = true; + } + + if (accel_y >= max_accel || accel_y <= min_accel) { + scale_20bit = true; + } + + if (accel_z >= max_accel || accel_z <= min_accel) { + scale_20bit = true; + } + + // shift by 2 (2 least significant bits are always 0) + accel.x[accel.samples] = accel_x / 4; + accel.y[accel.samples] = accel_y / 4; + accel.z[accel.samples] = accel_z / 4; + accel.samples++; + } + } + + if (!scale_20bit) { + // if highres enabled accel data is always 8192 LSB/g + _px4_accel.set_scale(CONSTANTS_ONE_G / 8192.f); + + } else { + // 20 bit data scaled to 16 bit (2^4) + for (int i = 0; i < samples; i++) { + // 20 bit hires mode + // Sign extension + Accel [19:12] + Accel [11:4] + Accel [3:2] (20 bit extension byte) + // Accel data is 18 bit () + int16_t accel_x = combine(fifo[i].ACCEL_DATA_X1, fifo[i].ACCEL_DATA_X0); + int16_t accel_y = combine(fifo[i].ACCEL_DATA_Y1, fifo[i].ACCEL_DATA_Y0); + int16_t accel_z = combine(fifo[i].ACCEL_DATA_Z1, fifo[i].ACCEL_DATA_Z0); + + accel.x[i] = accel_x; + accel.y[i] = accel_y; + accel.z[i] = accel_z; + } + + _px4_accel.set_scale(CONSTANTS_ONE_G / 2048.f); + } + + // correct frame for publication + for (int i = 0; i < accel.samples; i++) { + // sensor's frame is +x forward, +y left, +z up + // flip y & z to publish right handed with z down (x forward, y right, z down) + accel.x[i] = accel.x[i]; + accel.y[i] = (accel.y[i] == INT16_MIN) ? INT16_MAX : -accel.y[i]; + accel.z[i] = (accel.z[i] == INT16_MIN) ? INT16_MAX : -accel.z[i]; + } + + _px4_accel.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) + + perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf)); + + if (accel.samples > 0) { + _px4_accel.updateFIFO(accel); + } +} + +void IIM42652::ProcessGyro(const hrt_abstime ×tamp_sample, const FIFO::DATA fifo[], const uint8_t samples) +{ + sensor_gyro_fifo_s gyro{}; + gyro.timestamp_sample = timestamp_sample; + gyro.samples = 0; + gyro.dt = FIFO_SAMPLE_DT; + + // 20-bits of gyroscope data + bool scale_20bit = false; + + // first pass + for (int i = 0; i < samples; i++) { + // 20 bit hires mode + // Gyro [19:12] + Gyro [11:4] + Gyro [3:0] (bottom 4 bits of 20 bit extension byte) + int32_t gyro_x = reassemble_20bit(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0, fifo[i].Ext_Accel_X_Gyro_X & 0x0F); + int32_t gyro_y = reassemble_20bit(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0, fifo[i].Ext_Accel_Y_Gyro_Y & 0x0F); + int32_t gyro_z = reassemble_20bit(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0, fifo[i].Ext_Accel_Z_Gyro_Z & 0x0F); + + // check if any values are going to exceed int16 limits + static constexpr int16_t max_gyro = INT16_MAX; + static constexpr int16_t min_gyro = INT16_MIN; + + if (gyro_x >= max_gyro || gyro_x <= min_gyro) { + scale_20bit = true; + } + + if (gyro_y >= max_gyro || gyro_y <= min_gyro) { + scale_20bit = true; + } + + if (gyro_z >= max_gyro || gyro_z <= min_gyro) { + scale_20bit = true; + } + + gyro.x[gyro.samples] = gyro_x / 2; + gyro.y[gyro.samples] = gyro_y / 2; + gyro.z[gyro.samples] = gyro_z / 2; + gyro.samples++; + } + + if (!scale_20bit) { + // if highres enabled gyro data is always 131 LSB/dps + _px4_gyro.set_scale(math::radians(1.f / 131.f)); + + } else { + // 20 bit data scaled to 16 bit (2^4) + for (int i = 0; i < samples; i++) { + gyro.x[i] = combine(fifo[i].GYRO_DATA_X1, fifo[i].GYRO_DATA_X0); + gyro.y[i] = combine(fifo[i].GYRO_DATA_Y1, fifo[i].GYRO_DATA_Y0); + gyro.z[i] = combine(fifo[i].GYRO_DATA_Z1, fifo[i].GYRO_DATA_Z0); + } + + _px4_gyro.set_scale(math::radians(2000.f / 32768.f)); + } + + // correct frame for publication + for (int i = 0; i < gyro.samples; i++) { + // sensor's frame is +x forward, +y left, +z up + // flip y & z to publish right handed with z down (x forward, y right, z down) + gyro.x[i] = gyro.x[i]; + gyro.y[i] = (gyro.y[i] == INT16_MIN) ? INT16_MAX : -gyro.y[i]; + gyro.z[i] = (gyro.z[i] == INT16_MIN) ? INT16_MAX : -gyro.z[i]; + } + + _px4_gyro.set_error_count(perf_event_count(_bad_register_perf) + perf_event_count(_bad_transfer_perf) + + perf_event_count(_fifo_empty_perf) + perf_event_count(_fifo_overflow_perf)); + + if (gyro.samples > 0) { + _px4_gyro.updateFIFO(gyro); + } +} + +bool IIM42652::ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples) +{ + int16_t temperature[FIFO_MAX_SAMPLES]; + float temperature_sum{0}; + + int valid_samples = 0; + + for (int i = 0; i < samples; i++) { + const int16_t t = combine(fifo[i].TEMP_DATA1, fifo[i].TEMP_DATA0); + + // sample invalid if -32768 + if (t != -32768) { + temperature_sum += t; + temperature[valid_samples] = t; + valid_samples++; + } + } + + if (valid_samples > 0) { + const float temperature_avg = temperature_sum / valid_samples; + + for (int i = 0; i < valid_samples; i++) { + // temperature changing wildly is an indication of a transfer error + if (fabsf(temperature[i] - temperature_avg) > 1000) { + perf_count(_bad_transfer_perf); + return false; + } + } + + // use average temperature reading + const float TEMP_degC = (temperature_avg / TEMPERATURE_SENSITIVITY) + TEMPERATURE_OFFSET; + + if (PX4_ISFINITE(TEMP_degC)) { + _px4_accel.set_temperature(TEMP_degC); + _px4_gyro.set_temperature(TEMP_degC); + return true; + + } else { + perf_count(_bad_transfer_perf); + } + } + + return false; +} diff --git a/src/drivers/imu/invensense/iim42652/IIM42652.hpp b/src/drivers/imu/invensense/iim42652/IIM42652.hpp new file mode 100644 index 0000000000..9ba671d093 --- /dev/null +++ b/src/drivers/imu/invensense/iim42652/IIM42652.hpp @@ -0,0 +1,206 @@ +/**************************************************************************** + * + * Copyright (c) 2022 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file IIM42652.hpp + * + * Driver for the Invensense IIM42652 connected via SPI. + * + */ + +#pragma once + +#include "InvenSense_IIM42652_registers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace InvenSense_IIM42652; + +class IIM42652 : public device::SPI, public I2CSPIDriver +{ +public: + IIM42652(const I2CSPIDriverConfig &config); + ~IIM42652() override; + + static void print_usage(); + + void RunImpl(); + + int init() override; + void print_status() override; + +private: + void exit_and_cleanup() override; + + // Sensor Configuration + static constexpr float FIFO_SAMPLE_DT{1e6f / 8000.f}; // 8000 Hz accel & gyro ODR configured + static constexpr float GYRO_RATE{1e6f / FIFO_SAMPLE_DT}; + static constexpr float ACCEL_RATE{1e6f / FIFO_SAMPLE_DT}; + + // maximum FIFO samples per transfer is limited to the size of sensor_accel_fifo/sensor_gyro_fifo + static constexpr int32_t FIFO_MAX_SAMPLES{math::min(FIFO::SIZE / sizeof(FIFO::DATA), sizeof(sensor_gyro_fifo_s::x) / sizeof(sensor_gyro_fifo_s::x[0]), sizeof(sensor_accel_fifo_s::x) / sizeof(sensor_accel_fifo_s::x[0]) * (int)(GYRO_RATE / ACCEL_RATE))}; + + // Transfer data + struct FIFOTransferBuffer { + uint8_t cmd{static_cast(Register::BANK_0::INT_STATUS) | DIR_READ}; + uint8_t INT_STATUS{0}; + uint8_t FIFO_COUNTH{0}; + uint8_t FIFO_COUNTL{0}; + FIFO::DATA f[FIFO_MAX_SAMPLES] {}; + }; + // ensure no struct padding + static_assert(sizeof(FIFOTransferBuffer) == (4 + FIFO_MAX_SAMPLES *sizeof(FIFO::DATA))); + + struct register_bank0_config_t { + Register::BANK_0 reg; + uint8_t set_bits{0}; + uint8_t clear_bits{0}; + }; + + struct register_bank1_config_t { + Register::BANK_1 reg; + uint8_t set_bits{0}; + uint8_t clear_bits{0}; + }; + + struct register_bank2_config_t { + Register::BANK_2 reg; + uint8_t set_bits{0}; + uint8_t clear_bits{0}; + }; + + int probe() override; + + bool Reset(); + + bool Configure(); + void ConfigureSampleRate(int sample_rate); + void ConfigureFIFOWatermark(uint8_t samples); + + void SelectRegisterBank(enum REG_BANK_SEL_BIT bank, bool force = false); + void SelectRegisterBank(Register::BANK_0 reg) { SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_0); } + void SelectRegisterBank(Register::BANK_1 reg) { SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_1); } + void SelectRegisterBank(Register::BANK_2 reg) { SelectRegisterBank(REG_BANK_SEL_BIT::USER_BANK_2); } + + static int DataReadyInterruptCallback(int irq, void *context, void *arg); + void DataReady(); + bool DataReadyInterruptConfigure(); + bool DataReadyInterruptDisable(); + + template bool RegisterCheck(const T ®_cfg); + template uint8_t RegisterRead(T reg); + template void RegisterWrite(T reg, uint8_t value); + template void RegisterSetAndClearBits(T reg, uint8_t setbits, uint8_t clearbits); + template void RegisterSetBits(T reg, uint8_t setbits) { RegisterSetAndClearBits(reg, setbits, 0); } + template void RegisterClearBits(T reg, uint8_t clearbits) { RegisterSetAndClearBits(reg, 0, clearbits); } + + uint16_t FIFOReadCount(); + bool FIFORead(const hrt_abstime ×tamp_sample, uint8_t samples); + void FIFOReset(); + + void ProcessAccel(const hrt_abstime ×tamp_sample, const FIFO::DATA fifo[], const uint8_t samples); + void ProcessGyro(const hrt_abstime ×tamp_sample, const FIFO::DATA fifo[], const uint8_t samples); + bool ProcessTemperature(const FIFO::DATA fifo[], const uint8_t samples); + + const spi_drdy_gpio_t _drdy_gpio; + + PX4Accelerometer _px4_accel; + PX4Gyroscope _px4_gyro; + + perf_counter_t _bad_register_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad register")}; + perf_counter_t _bad_transfer_perf{perf_alloc(PC_COUNT, MODULE_NAME": bad transfer")}; + perf_counter_t _fifo_empty_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO empty")}; + perf_counter_t _fifo_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO overflow")}; + perf_counter_t _fifo_reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": FIFO reset")}; + perf_counter_t _drdy_missed_perf{nullptr}; + + hrt_abstime _reset_timestamp{0}; + hrt_abstime _last_config_check_timestamp{0}; + hrt_abstime _temperature_update_timestamp{0}; + int _failure_count{0}; + + enum REG_BANK_SEL_BIT _last_register_bank {REG_BANK_SEL_BIT::USER_BANK_0}; + + px4::atomic _drdy_timestamp_sample{0}; + bool _data_ready_interrupt_enabled{false}; + + enum class STATE : uint8_t { + RESET, + WAIT_FOR_RESET, + CONFIGURE, + FIFO_READ, + } _state{STATE::RESET}; + + uint16_t _fifo_empty_interval_us{1250}; // default 1250 us / 800 Hz transfer interval + int32_t _fifo_gyro_samples{static_cast(_fifo_empty_interval_us / (1000000 / GYRO_RATE))}; + + uint8_t _checked_register_bank0{0}; + static constexpr uint8_t size_register_bank0_cfg{13}; + register_bank0_config_t _register_bank0_cfg[size_register_bank0_cfg] { + // Register | Set bits, Clear bits + { Register::BANK_0::INT_CONFIG, INT_CONFIG_BIT::INT1_MODE | INT_CONFIG_BIT::INT1_DRIVE_CIRCUIT, INT_CONFIG_BIT::INT1_POLARITY }, + { Register::BANK_0::FIFO_CONFIG, FIFO_CONFIG_BIT::FIFO_MODE_STOP_ON_FULL, 0 }, + { Register::BANK_0::PWR_MGMT0, PWR_MGMT0_BIT::GYRO_MODE_LOW_NOISE | PWR_MGMT0_BIT::ACCEL_MODE_LOW_NOISE, 0 }, + { Register::BANK_0::GYRO_CONFIG0, GYRO_CONFIG0_BIT::GYRO_FS_SEL_2000_DPS | GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_SET, GYRO_CONFIG0_BIT::GYRO_ODR_8KHZ_CLEAR }, + { Register::BANK_0::ACCEL_CONFIG0, ACCEL_CONFIG0_BIT::ACCEL_FS_SEL_16G | ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_SET, ACCEL_CONFIG0_BIT::ACCEL_ODR_8KHZ_CLEAR }, + { Register::BANK_0::GYRO_CONFIG1, 0, GYRO_CONFIG1_BIT::GYRO_UI_FILT_ORD }, + { Register::BANK_0::GYRO_ACCEL_CONFIG0, 0, GYRO_ACCEL_CONFIG0_BIT::ACCEL_UI_FILT_BW | GYRO_ACCEL_CONFIG0_BIT::GYRO_UI_FILT_BW }, + { Register::BANK_0::ACCEL_CONFIG1, 0, ACCEL_CONFIG1_BIT::ACCEL_UI_FILT_ORD }, + { Register::BANK_0::FIFO_CONFIG1, FIFO_CONFIG1_BIT::FIFO_WM_GT_TH | FIFO_CONFIG1_BIT::FIFO_HIRES_EN | FIFO_CONFIG1_BIT::FIFO_TEMP_EN | FIFO_CONFIG1_BIT::FIFO_GYRO_EN | FIFO_CONFIG1_BIT::FIFO_ACCEL_EN, 0 }, + { Register::BANK_0::FIFO_CONFIG2, 0, 0 }, // FIFO_WM[7:0] set at runtime + { Register::BANK_0::FIFO_CONFIG3, 0, 0 }, // FIFO_WM[11:8] set at runtime + { Register::BANK_0::INT_CONFIG0, INT_CONFIG0_BIT::CLEAR_ON_FIFO_READ, 0 }, + { Register::BANK_0::INT_SOURCE0, INT_SOURCE0_BIT::FIFO_THS_INT1_EN, 0 }, + }; + + uint8_t _checked_register_bank1{0}; + static constexpr uint8_t size_register_bank1_cfg{1}; + register_bank1_config_t _register_bank1_cfg[size_register_bank1_cfg] { + // Register | Set bits, Clear bits + { Register::BANK_1::GYRO_CONFIG_STATIC2, GYRO_CONFIG_STATIC2_BIT::GYRO_AAF_DIS | GYRO_CONFIG_STATIC2_BIT::GYRO_NF_DIS, 0 }, + }; + + uint8_t _checked_register_bank2{0}; + static constexpr uint8_t size_register_bank2_cfg{1}; + register_bank2_config_t _register_bank2_cfg[size_register_bank2_cfg] { + // Register | Set bits, Clear bits + { Register::BANK_2::ACCEL_CONFIG_STATIC2, ACCEL_CONFIG_STATIC2_BIT::ACCEL_AAF_DIS, 0 }, + }; +}; diff --git a/src/drivers/imu/invensense/iim42652/InvenSense_IIM42652_registers.hpp b/src/drivers/imu/invensense/iim42652/InvenSense_IIM42652_registers.hpp new file mode 100644 index 0000000000..7f59099e2f --- /dev/null +++ b/src/drivers/imu/invensense/iim42652/InvenSense_IIM42652_registers.hpp @@ -0,0 +1,322 @@ +/**************************************************************************** + * + * Copyright (c) 2022 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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. + * + ****************************************************************************/ + +/** + * @file InvenSense_IIM42652_registers.hpp + * + * Invensense IIM-42652 registers. + * + */ + +#pragma once + +#include +#include + +namespace InvenSense_IIM42652 +{ +// TODO: move to a central header +static constexpr uint8_t Bit0 = (1 << 0); +static constexpr uint8_t Bit1 = (1 << 1); +static constexpr uint8_t Bit2 = (1 << 2); +static constexpr uint8_t Bit3 = (1 << 3); +static constexpr uint8_t Bit4 = (1 << 4); +static constexpr uint8_t Bit5 = (1 << 5); +static constexpr uint8_t Bit6 = (1 << 6); +static constexpr uint8_t Bit7 = (1 << 7); + +static constexpr uint32_t SPI_SPEED = 24 * 1000 * 1000; // 24 MHz SPI +static constexpr uint8_t DIR_READ = 0x80; + +static constexpr uint8_t WHOAMI = 0x6F; + +static constexpr float TEMPERATURE_SENSITIVITY = 132.48f; // LSB/C +static constexpr float TEMPERATURE_OFFSET = 25.f; // C + +namespace Register +{ + +enum class BANK_0 : uint8_t { + DEVICE_CONFIG = 0x11, + + INT_CONFIG = 0x14, + + FIFO_CONFIG = 0x16, + + TEMP_DATA1 = 0x1D, + TEMP_DATA0 = 0x1E, + + INT_STATUS = 0x2D, + FIFO_COUNTH = 0x2E, + FIFO_COUNTL = 0x2F, + FIFO_DATA = 0x30, + + SIGNAL_PATH_RESET = 0x4B, + INTF_CONFIG0 = 0x4C, + INTF_CONFIG1 = 0x4D, + PWR_MGMT0 = 0x4E, + GYRO_CONFIG0 = 0x4F, + ACCEL_CONFIG0 = 0x50, + GYRO_CONFIG1 = 0x51, + GYRO_ACCEL_CONFIG0 = 0x52, + ACCEL_CONFIG1 = 0x53, + + FIFO_CONFIG1 = 0x5F, + FIFO_CONFIG2 = 0x60, + FIFO_CONFIG3 = 0x61, + + INT_CONFIG0 = 0x63, + + INT_SOURCE0 = 0x65, + + SELF_TEST_CONFIG = 0x70, + + WHO_AM_I = 0x75, + REG_BANK_SEL = 0x76, +}; + +enum class BANK_1 : uint8_t { + GYRO_CONFIG_STATIC2 = 0x0B, + + INTF_CONFIG5 = 0x7B, +}; + +enum class BANK_2 : uint8_t { + ACCEL_CONFIG_STATIC2 = 0x03, +}; + +}; + +//---------------- BANK0 Register bits + +// DEVICE_CONFIG +enum DEVICE_CONFIG_BIT : uint8_t { + SOFT_RESET_CONFIG = Bit0, // +}; + +// INT_CONFIG +enum INT_CONFIG_BIT : uint8_t { + INT1_MODE = Bit2, + INT1_DRIVE_CIRCUIT = Bit1, + INT1_POLARITY = Bit0, +}; + +// FIFO_CONFIG +enum FIFO_CONFIG_BIT : uint8_t { + // 7:6 FIFO_MODE + FIFO_MODE_STOP_ON_FULL = Bit7 | Bit6, // 11: STOP-on-FULL Mode +}; + +// INT_STATUS +enum INT_STATUS_BIT : uint8_t { + RESET_DONE_INT = Bit4, + DATA_RDY_INT = Bit3, + FIFO_THS_INT = Bit2, + FIFO_FULL_INT = Bit1, +}; + +// SIGNAL_PATH_RESET +enum SIGNAL_PATH_RESET_BIT : uint8_t { + ABORT_AND_RESET = Bit3, + FIFO_FLUSH = Bit1, +}; + +// PWR_MGMT0 +enum PWR_MGMT0_BIT : uint8_t { + GYRO_MODE_LOW_NOISE = Bit3 | Bit2, // 11: Places gyroscope in Low Noise (LN) Mode + ACCEL_MODE_LOW_NOISE = Bit1 | Bit0, // 11: Places accelerometer in Low Noise (LN) Mode +}; + +// GYRO_CONFIG0 +enum GYRO_CONFIG0_BIT : uint8_t { + // 7:5 GYRO_FS_SEL + GYRO_FS_SEL_2000_DPS = 0, // 0b000 = ±2000dps (default) + GYRO_FS_SEL_1000_DPS = Bit5, + GYRO_FS_SEL_500_DPS = Bit6, + GYRO_FS_SEL_250_DPS = Bit6 | Bit5, + GYRO_FS_SEL_125_DPS = Bit7, + + + // 3:0 GYRO_ODR + // 0001: 32kHz + GYRO_ODR_32KHZ_SET = Bit0, + GYRO_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0, + // 0010: 16kHz + GYRO_ODR_16KHZ_SET = Bit1, + GYRO_ODR_16KHZ_CLEAR = Bit3 | Bit2 | Bit0, + // 0011: 8kHz + GYRO_ODR_8KHZ_SET = Bit1 | Bit0, + GYRO_ODR_8KHZ_CLEAR = Bit3 | Bit2, + // 0110: 1kHz (default) + GYRO_ODR_1KHZ_SET = Bit2 | Bit1, + GYRO_ODR_1KHZ_CLEAR = Bit3 | Bit0, +}; + +// ACCEL_CONFIG0 +enum ACCEL_CONFIG0_BIT : uint8_t { + // 7:5 ACCEL_FS_SEL + ACCEL_FS_SEL_16G = 0, // 000: ±16g (default) + ACCEL_FS_SEL_8G = Bit5, + ACCEL_FS_SEL_4G = Bit6, + ACCEL_FS_SEL_2G = Bit6 | Bit5, + + + // 3:0 ACCEL_ODR + // 0001: 32kHz + ACCEL_ODR_32KHZ_SET = Bit0, + ACCEL_ODR_32KHZ_CLEAR = Bit3 | Bit2 | Bit0, + // 0010: 16kHz + ACCEL_ODR_16KHZ_SET = Bit1, + ACCEL_ODR_16KHZ_CLEAR = Bit3 | Bit2 | Bit0, + // 0011: 8kHz + ACCEL_ODR_8KHZ_SET = Bit1 | Bit0, + ACCEL_ODR_8KHZ_CLEAR = Bit3 | Bit2, + // 0110: 1kHz (default) + ACCEL_ODR_1KHZ_SET = Bit2 | Bit1, + ACCEL_ODR_1KHZ_CLEAR = Bit3 | Bit0, +}; + +// GYRO_CONFIG1 +enum GYRO_CONFIG1_BIT : uint8_t { + GYRO_UI_FILT_ORD = Bit3 | Bit2, // 00: 1st Order +}; + +// GYRO_ACCEL_CONFIG0 +enum GYRO_ACCEL_CONFIG0_BIT : uint8_t { + // 7:4 ACCEL_UI_FILT_BW + ACCEL_UI_FILT_BW = Bit7 | Bit6 | Bit5 | Bit4, // 0: BW=ODR/2 + + // 3:0 GYRO_UI_FILT_BW + GYRO_UI_FILT_BW = Bit3 | Bit2 | Bit1 | Bit0, // 0: BW=ODR/2 +}; + +// ACCEL_CONFIG1 +enum ACCEL_CONFIG1_BIT : uint8_t { + ACCEL_UI_FILT_ORD = Bit4 | Bit3, // 00: 1st Order +}; + +// FIFO_CONFIG1 +enum FIFO_CONFIG1_BIT : uint8_t { + FIFO_RESUME_PARTIAL_RD = Bit6, + FIFO_WM_GT_TH = Bit5, + FIFO_HIRES_EN = Bit4, + FIFO_TEMP_EN = Bit2, + FIFO_GYRO_EN = Bit1, + FIFO_ACCEL_EN = Bit0, +}; + +// INT_CONFIG0 +enum INT_CONFIG0_BIT : uint8_t { + // 3:2 FIFO_THS_INT_CLEAR + CLEAR_ON_FIFO_READ = Bit3, +}; + +// INT_SOURCE0 +enum INT_SOURCE0_BIT : uint8_t { + UI_FSYNC_INT1_EN = Bit6, + PLL_RDY_INT1_EN = Bit5, + RESET_DONE_INT1_EN = Bit4, + UI_DRDY_INT1_EN = Bit3, + FIFO_THS_INT1_EN = Bit2, // FIFO threshold interrupt routed to INT1 + FIFO_FULL_INT1_EN = Bit1, + UI_AGC_RDY_INT1_EN = Bit0, +}; + +// REG_BANK_SEL +enum REG_BANK_SEL_BIT : uint8_t { + USER_BANK_0 = 0, // 0: Select USER BANK 0. + USER_BANK_1 = Bit4, // 1: Select USER BANK 1. + USER_BANK_2 = Bit5, // 2: Select USER BANK 2. + USER_BANK_3 = Bit5 | Bit4, // 3: Select USER BANK 3. +}; + + +//---------------- BANK1 Register bits + +// GYRO_CONFIG_STATIC2 +enum GYRO_CONFIG_STATIC2_BIT : uint8_t { + GYRO_AAF_DIS = Bit1, + GYRO_NF_DIS = Bit0, +}; + + +//---------------- BANK2 Register bits + +// ACCEL_CONFIG_STATIC2 +enum ACCEL_CONFIG_STATIC2_BIT : uint8_t { + ACCEL_AAF_DIS = Bit0, +}; + +namespace FIFO +{ +static constexpr size_t SIZE = 2048; + +// FIFO_DATA layout when FIFO_CONFIG1 has FIFO_GYRO_EN and FIFO_ACCEL_EN set + +// Packet 4 +struct DATA { + uint8_t FIFO_Header; + uint8_t ACCEL_DATA_X1; // Accel X [19:12] + uint8_t ACCEL_DATA_X0; // Accel X [11:4] + uint8_t ACCEL_DATA_Y1; // Accel Y [19:12] + uint8_t ACCEL_DATA_Y0; // Accel Y [11:4] + uint8_t ACCEL_DATA_Z1; // Accel Z [19:12] + uint8_t ACCEL_DATA_Z0; // Accel Z [11:4] + uint8_t GYRO_DATA_X1; // Gyro X [19:12] + uint8_t GYRO_DATA_X0; // Gyro X [11:4] + uint8_t GYRO_DATA_Y1; // Gyro Y [19:12] + uint8_t GYRO_DATA_Y0; // Gyro Y [11:4] + uint8_t GYRO_DATA_Z1; // Gyro Z [19:12] + uint8_t GYRO_DATA_Z0; // Gyro Z [11:4] + uint8_t TEMP_DATA1; // Temperature[15:8] + uint8_t TEMP_DATA0; // Temperature[7:0] + uint8_t TimeStamp_h; // TimeStamp[15:8] + uint8_t TimeStamp_l; // TimeStamp[7:0] + uint8_t Ext_Accel_X_Gyro_X; // Accel X [3:0] Gyro X [3:0] + uint8_t Ext_Accel_Y_Gyro_Y; // Accel Y [3:0] Gyro Y [3:0] + uint8_t Ext_Accel_Z_Gyro_Z; // Accel Z [3:0] Gyro Z [3:0] +}; + +// With FIFO_ACCEL_EN and FIFO_GYRO_EN header should be 8’b_0110_10xx +enum FIFO_HEADER_BIT : uint8_t { + HEADER_MSG = Bit7, // 1: FIFO is empty + HEADER_ACCEL = Bit6, // 1: Packet is sized so that accel data have location in the packet, FIFO_ACCEL_EN must be 1 + HEADER_GYRO = Bit5, // 1: Packet is sized so that gyro data have location in the packet, FIFO_GYRO_EN must be1 + HEADER_20 = Bit4, // 1: Packet has a new and valid sample of extended 20-bit data for gyro and/or accel + HEADER_TIMESTAMP_FSYNC = Bit3 | Bit2, + HEADER_ODR_ACCEL = Bit1, // 1: The ODR for accel is different for this accel data packet compared to the previous accel packet + HEADER_ODR_GYRO = Bit0, // 1: The ODR for gyro is different for this gyro data packet compared to the previous gyro packet +}; + +} +} // namespace InvenSense_IIM42652 diff --git a/src/drivers/imu/invensense/iim42652/Kconfig b/src/drivers/imu/invensense/iim42652/Kconfig new file mode 100644 index 0000000000..88031b3b6a --- /dev/null +++ b/src/drivers/imu/invensense/iim42652/Kconfig @@ -0,0 +1,5 @@ +menuconfig DRIVERS_IMU_INVENSENSE_IIM42652 + bool "iim42652" + default n + ---help--- + Enable support for iim42652 diff --git a/src/drivers/imu/invensense/iim42652/iim42652_main.cpp b/src/drivers/imu/invensense/iim42652/iim42652_main.cpp new file mode 100644 index 0000000000..ae88e7e709 --- /dev/null +++ b/src/drivers/imu/invensense/iim42652/iim42652_main.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * Copyright (c) 2022 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 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 "IIM42652.hpp" + +#include +#include + +void IIM42652::print_usage() +{ + PRINT_MODULE_USAGE_NAME("iim42652", "driver"); + PRINT_MODULE_USAGE_SUBCATEGORY("imu"); + PRINT_MODULE_USAGE_COMMAND("start"); + PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(false, true); + PRINT_MODULE_USAGE_PARAM_INT('R', 0, 0, 35, "Rotation", true); + PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); +} + +extern "C" int iim42652_main(int argc, char *argv[]) +{ + int ch; + using ThisDriver = IIM42652; + BusCLIArguments cli{false, true}; + cli.default_spi_frequency = SPI_SPEED; + + while ((ch = cli.getOpt(argc, argv, "R:")) != EOF) { + switch (ch) { + case 'R': + cli.rotation = (enum Rotation)atoi(cli.optArg()); + break; + } + } + + const char *verb = cli.optArg(); + + if (!verb) { + ThisDriver::print_usage(); + return -1; + } + + BusInstanceIterator iterator(MODULE_NAME, cli, DRV_IMU_DEVTYPE_IIM42652); + + if (!strcmp(verb, "start")) { + return ThisDriver::module_start(cli, iterator); + } + + if (!strcmp(verb, "stop")) { + return ThisDriver::module_stop(iterator); + } + + if (!strcmp(verb, "status")) { + return ThisDriver::module_status(iterator); + } + + ThisDriver::print_usage(); + return -1; +}