From c59f208cc7a2b110768e89ee7bebfeded57ff981 Mon Sep 17 00:00:00 2001 From: Alexis Guijarro Date: Tue, 19 May 2026 13:13:54 -0500 Subject: [PATCH] feat(boards): add support for 3DR Control N1 flight controller (#27395) * fix(srgbled_dma): stm32_dmasetup now works with H7 boards * feat(drivers/ak09940a): add AKM AK09940A magnetometer driver * feat(boards/3dr/ctrl-n1)!: add 3DR Control-N1 flight controller * fix(boards/3dr/ctrl-n1): macro replacement list is now enclosed in parentheses * fix(boards/3dr/ctrl-n1): applying copyright changes * fix(boards/3dr/ctrl-n1): removing serial console --- .vscode/cmake-variants.yaml | 5 + Makefile | 1 + boards/3dr/ctrl-n1/bootloader.px4board | 3 + boards/3dr/ctrl-n1/default.px4board | 95 +++++ .../ctrl-n1/extras/3dr_ctrl-n1_bootloader.bin | Bin 0 -> 43328 bytes boards/3dr/ctrl-n1/firmware.prototype | 13 + boards/3dr/ctrl-n1/init/rc.board_defaults | 10 + boards/3dr/ctrl-n1/init/rc.board_sensors | 16 + .../ctrl-n1/nuttx-config/bootloader/defconfig | 91 +++++ .../3dr/ctrl-n1/nuttx-config/include/board.h | 292 +++++++++++++++ .../nuttx-config/include/board_dma_map.h | 55 +++ boards/3dr/ctrl-n1/nuttx-config/nsh/defconfig | 272 ++++++++++++++ .../nuttx-config/scripts/bootloader_script.ld | 215 +++++++++++ .../ctrl-n1/nuttx-config/scripts/script.ld | 228 ++++++++++++ boards/3dr/ctrl-n1/src/CMakeLists.txt | 65 ++++ boards/3dr/ctrl-n1/src/board_config.h | 152 ++++++++ boards/3dr/ctrl-n1/src/bootloader_main.c | 75 ++++ boards/3dr/ctrl-n1/src/hw_config.h | 132 +++++++ boards/3dr/ctrl-n1/src/i2c.cpp | 39 ++ boards/3dr/ctrl-n1/src/init.c | 199 ++++++++++ boards/3dr/ctrl-n1/src/led.c | 109 ++++++ boards/3dr/ctrl-n1/src/spi.cpp | 54 +++ boards/3dr/ctrl-n1/src/timer_config.cpp | 58 +++ boards/3dr/ctrl-n1/src/usb.c | 78 ++++ .../stm32_common/srgbled_dma/srgbled_dma.cpp | 11 + .../nuttx/src/px4/stm/stm32h7/CMakeLists.txt | 1 + src/drivers/drv_sensor.h | 2 + src/drivers/magnetometer/akm/CMakeLists.txt | 1 + .../magnetometer/akm/ak09940a/AK09940A.cpp | 346 ++++++++++++++++++ .../magnetometer/akm/ak09940a/AK09940A.hpp | 131 +++++++ .../akm/ak09940a/AKM_AK09940A_registers.hpp | 132 +++++++ .../magnetometer/akm/ak09940a/CMakeLists.txt | 48 +++ src/drivers/magnetometer/akm/ak09940a/Kconfig | 5 + .../akm/ak09940a/ak09940a_main.cpp | 89 +++++ 34 files changed, 3023 insertions(+) create mode 100755 boards/3dr/ctrl-n1/bootloader.px4board create mode 100644 boards/3dr/ctrl-n1/default.px4board create mode 100755 boards/3dr/ctrl-n1/extras/3dr_ctrl-n1_bootloader.bin create mode 100755 boards/3dr/ctrl-n1/firmware.prototype create mode 100755 boards/3dr/ctrl-n1/init/rc.board_defaults create mode 100755 boards/3dr/ctrl-n1/init/rc.board_sensors create mode 100755 boards/3dr/ctrl-n1/nuttx-config/bootloader/defconfig create mode 100755 boards/3dr/ctrl-n1/nuttx-config/include/board.h create mode 100755 boards/3dr/ctrl-n1/nuttx-config/include/board_dma_map.h create mode 100644 boards/3dr/ctrl-n1/nuttx-config/nsh/defconfig create mode 100755 boards/3dr/ctrl-n1/nuttx-config/scripts/bootloader_script.ld create mode 100755 boards/3dr/ctrl-n1/nuttx-config/scripts/script.ld create mode 100755 boards/3dr/ctrl-n1/src/CMakeLists.txt create mode 100755 boards/3dr/ctrl-n1/src/board_config.h create mode 100755 boards/3dr/ctrl-n1/src/bootloader_main.c create mode 100755 boards/3dr/ctrl-n1/src/hw_config.h create mode 100755 boards/3dr/ctrl-n1/src/i2c.cpp create mode 100755 boards/3dr/ctrl-n1/src/init.c create mode 100755 boards/3dr/ctrl-n1/src/led.c create mode 100755 boards/3dr/ctrl-n1/src/spi.cpp create mode 100755 boards/3dr/ctrl-n1/src/timer_config.cpp create mode 100755 boards/3dr/ctrl-n1/src/usb.c create mode 100644 src/drivers/magnetometer/akm/ak09940a/AK09940A.cpp create mode 100644 src/drivers/magnetometer/akm/ak09940a/AK09940A.hpp create mode 100644 src/drivers/magnetometer/akm/ak09940a/AKM_AK09940A_registers.hpp create mode 100644 src/drivers/magnetometer/akm/ak09940a/CMakeLists.txt create mode 100644 src/drivers/magnetometer/akm/ak09940a/Kconfig create mode 100644 src/drivers/magnetometer/akm/ak09940a/ak09940a_main.cpp diff --git a/.vscode/cmake-variants.yaml b/.vscode/cmake-variants.yaml index f41c0af8cbf..377c19da98f 100644 --- a/.vscode/cmake-variants.yaml +++ b/.vscode/cmake-variants.yaml @@ -131,6 +131,11 @@ CONFIG: buildType: MinSizeRel settings: CONFIG: px4_fmu-v6xrt_bootloader + 3dr_ctrl-n1_default: + short: 3dr_ctrl-n1 + buildType: MinSizeRel + settings: + CONFIG: 3dr_ctrl-n1_default 3dr_ctrl-zero-h7-oem-revg_default: short: 3dr_ctrl-zero-h7-oem-revg buildType: MinSizeRel diff --git a/Makefile b/Makefile index a32a0996d08..2c92d9e2ff4 100644 --- a/Makefile +++ b/Makefile @@ -355,6 +355,7 @@ px4io_update: git status bootloaders_update: \ + 3dr_ctrl-n1_bootloader \ 3dr_ctrl-zero-h7-oem-revg_bootloader \ ark_fmu-v6x_bootloader \ ark_fpv_bootloader \ diff --git a/boards/3dr/ctrl-n1/bootloader.px4board b/boards/3dr/ctrl-n1/bootloader.px4board new file mode 100755 index 00000000000..19b6e662be6 --- /dev/null +++ b/boards/3dr/ctrl-n1/bootloader.px4board @@ -0,0 +1,3 @@ +CONFIG_BOARD_TOOLCHAIN="arm-none-eabi" +CONFIG_BOARD_ARCHITECTURE="cortex-m7" +CONFIG_BOARD_ROMFSROOT="" diff --git a/boards/3dr/ctrl-n1/default.px4board b/boards/3dr/ctrl-n1/default.px4board new file mode 100644 index 00000000000..0eabddbb2e9 --- /dev/null +++ b/boards/3dr/ctrl-n1/default.px4board @@ -0,0 +1,95 @@ +CONFIG_BOARD_TOOLCHAIN="arm-none-eabi" +CONFIG_BOARD_ARCHITECTURE="cortex-m7" +CONFIG_BOARD_COMPILE_DEFINITIONS="-DUSE_S_RGB_LED_DMA" +CONFIG_BOARD_SERIAL_GPS1="/dev/ttyS6" +CONFIG_BOARD_SERIAL_GPS2="/dev/ttyS2" +CONFIG_BOARD_SERIAL_TEL1="/dev/ttyS1" +CONFIG_BOARD_SERIAL_TEL2="/dev/ttyS3" +CONFIG_BOARD_SERIAL_RC="/dev/ttyS4" +CONFIG_BOARD_PARAM_FILE="/fs/microsd/mtd_params" +CONFIG_DRIVERS_ADC_BOARD_ADC=y +CONFIG_DRIVERS_BAROMETER_DPS310=y +CONFIG_DRIVERS_BATT_SMBUS=y +CONFIG_DRIVERS_CAMERA_CAPTURE=y +CONFIG_DRIVERS_CAMERA_TRIGGER=y +CONFIG_DRIVERS_CDCACM_AUTOSTART=y +CONFIG_COMMON_DIFFERENTIAL_PRESSURE=y +CONFIG_COMMON_DISTANCE_SENSOR=y +CONFIG_DRIVERS_DSHOT=y +CONFIG_DRIVERS_GPS=y +CONFIG_DRIVERS_IMU_INVENSENSE_IIM42653=y +CONFIG_DRIVERS_LIGHTS_NEOPIXEL=y +CONFIG_COMMON_MAGNETOMETER=y +CONFIG_DRIVERS_MAGNETOMETER_AKM_AK09940A=y +CONFIG_COMMON_OPTICAL_FLOW=y +CONFIG_DRIVERS_POWER_MONITOR_INA226=y +CONFIG_DRIVERS_PWM_OUT=y +CONFIG_COMMON_RC=y +CONFIG_DRIVERS_SMART_BATTERY_BATMON=y +CONFIG_COMMON_TELEMETRY=y +CONFIG_DRIVERS_TONE_ALARM=y +CONFIG_DRIVERS_UAVCAN=y +CONFIG_MODULES_AIRSPEED_SELECTOR=y +CONFIG_MODULES_BATTERY_STATUS=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_RATE_CONTROL=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_SENSORS=y +CONFIG_MODULES_SIMULATION_SIMULATOR_SIH=y +CONFIG_MODULES_TEMPERATURE_COMPENSATION=y +CONFIG_MODULES_UUV_ATT_CONTROL=y +CONFIG_MODULES_UUV_POS_CONTROL=y +CONFIG_MODULES_UXRCE_DDS_CLIENT=y +CONFIG_MODULES_VTOL_ATT_CONTROL=y +CONFIG_SYSTEMCMDS_ACTUATOR_TEST=y +CONFIG_SYSTEMCMDS_BSONDUMP=y +CONFIG_SYSTEMCMDS_DMESG=y +CONFIG_SYSTEMCMDS_DUMPFILE=y +CONFIG_SYSTEMCMDS_GPIO=y +CONFIG_SYSTEMCMDS_HARDFAULT_LOG=y +CONFIG_SYSTEMCMDS_I2CDETECT=y +CONFIG_SYSTEMCMDS_LED_CONTROL=y +CONFIG_SYSTEMCMDS_MFT=y +CONFIG_SYSTEMCMDS_MTD=y +CONFIG_SYSTEMCMDS_NSHTERM=y +CONFIG_SYSTEMCMDS_PARAM=y +CONFIG_SYSTEMCMDS_PERF=y +CONFIG_SYSTEMCMDS_REBOOT=y +CONFIG_SYSTEMCMDS_REFLECT=y +CONFIG_SYSTEMCMDS_SD_BENCH=y +CONFIG_SYSTEMCMDS_SD_STRESS=y +CONFIG_SYSTEMCMDS_SERIAL_TEST=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_USB_CONNECTED=y +CONFIG_SYSTEMCMDS_VER=y +CONFIG_SYSTEMCMDS_WORK_QUEUE=y +CONFIG_ARCH_CHIP_STM32H7=y diff --git a/boards/3dr/ctrl-n1/extras/3dr_ctrl-n1_bootloader.bin b/boards/3dr/ctrl-n1/extras/3dr_ctrl-n1_bootloader.bin new file mode 100755 index 0000000000000000000000000000000000000000..725e3899ccb976403e346863f6559a753115f33c GIT binary patch literal 43328 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 literal 0 HcmV?d00001 diff --git a/boards/3dr/ctrl-n1/firmware.prototype b/boards/3dr/ctrl-n1/firmware.prototype new file mode 100755 index 00000000000..810ccc82831 --- /dev/null +++ b/boards/3dr/ctrl-n1/firmware.prototype @@ -0,0 +1,13 @@ +{ + "board_id": 1186, + "magic": "3dr-ctrl-n1", + "description": "Firmware for the 3dr-ctrl-n1 board", + "image": "", + "build_time": 0, + "summary": "3dr-ctrl-n1", + "version": "0.1", + "image_size": 0, + "image_maxsize": 1966080, + "git_identity": "", + "board_revision": 0 +} diff --git a/boards/3dr/ctrl-n1/init/rc.board_defaults b/boards/3dr/ctrl-n1/init/rc.board_defaults new file mode 100755 index 00000000000..2696b843bad --- /dev/null +++ b/boards/3dr/ctrl-n1/init/rc.board_defaults @@ -0,0 +1,10 @@ +#!/bin/sh +# +# board specific defaults +#------------------------------------------------------------------------------ + +#param set-default BAT1_V_DIV 10.1 +#param set-default BAT1_A_PER_V 17 +# +#safety_button start +neopixel start diff --git a/boards/3dr/ctrl-n1/init/rc.board_sensors b/boards/3dr/ctrl-n1/init/rc.board_sensors new file mode 100755 index 00000000000..44d9d1dfd83 --- /dev/null +++ b/boards/3dr/ctrl-n1/init/rc.board_sensors @@ -0,0 +1,16 @@ +#!/bin/sh +# +# board specific sensors init +#------------------------------------------------------------------------------ + +board_adc start + +# Internal x2 ICM-20602 +iim42653 -R 14 -s -b 1 start +iim42653 -R 14 -s -b 2 start + +# Interal DPS310 (barometer) +dps310 -s -b 6 start + +# Internal AK09940A magnetometer +ak09940a -X -b 1 -R 4 start diff --git a/boards/3dr/ctrl-n1/nuttx-config/bootloader/defconfig b/boards/3dr/ctrl-n1/nuttx-config/bootloader/defconfig new file mode 100755 index 00000000000..4a87cabdc88 --- /dev/null +++ b/boards/3dr/ctrl-n1/nuttx-config/bootloader/defconfig @@ -0,0 +1,91 @@ +# +# 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/3dr/ctrl-n1/nuttx-config" +CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y +CONFIG_ARCH_BOARD_CUSTOM_NAME="px4" +CONFIG_ARCH_CHIP="stm32h7" +CONFIG_ARCH_CHIP_STM32H743II=y +CONFIG_ARCH_CHIP_STM32H7=y +CONFIG_ARCH_INTERRUPTSTACK=768 +CONFIG_ARMV7M_BASEPRI_WAR=y +CONFIG_ARMV7M_ICACHE=y +CONFIG_ARMV7M_MEMCPY=y +CONFIG_ARMV7M_USEBASEPRI=y +CONFIG_BOARDCTL=y +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_ASSERT_RESET_VALUE=0 +CONFIG_BOARD_INITTHREAD_PRIORITY=254 +CONFIG_BOARD_LATE_INITIALIZE=y +CONFIG_BOARD_LOOPSPERMSEC=95150 +CONFIG_BOARD_RESET_ON_ASSERT=2 +CONFIG_CDCACM=y +CONFIG_CDCACM_IFLOWCONTROL=y +CONFIG_CDCACM_PRODUCTID=0x1186 +CONFIG_CDCACM_PRODUCTSTR="PX4 BL 3DR ControlN1" +CONFIG_CDCACM_RXBUFSIZE=600 +CONFIG_CDCACM_TXBUFSIZE=12000 +CONFIG_CDCACM_VENDORID=0x26ac +CONFIG_CDCACM_VENDORSTR="3DR" +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEBUG_TCBINFO=y +CONFIG_DEFAULT_SMALL=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_INIT_ENTRYPOINT="bootloader_main" +CONFIG_INIT_STACKSIZE=3194 +CONFIG_LIBC_FLOATINGPOINT=y +CONFIG_LIBC_LONG_LONG=y +CONFIG_LIBC_STRERROR=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_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_OTGFS=y +CONFIG_STM32H7_PROGMEM=y +CONFIG_STM32H7_PWR_EXTERNAL_SOURCE_SUPPLY=y +CONFIG_STM32H7_SERIAL_DISABLE_REORDERING=y +CONFIG_STM32H7_TIM1=y +CONFIG_STM32H7_USART2=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_USART2_RXBUFSIZE=600 +CONFIG_USART2_TXBUFSIZE=300 +CONFIG_USBDEV=y +CONFIG_USBDEV_BUSPOWERED=y +CONFIG_USBDEV_MAXPOWER=500 +CONFIG_USEC_PER_TICK=1000 diff --git a/boards/3dr/ctrl-n1/nuttx-config/include/board.h b/boards/3dr/ctrl-n1/nuttx-config/include/board.h new file mode 100755 index 00000000000..618c33177dd --- /dev/null +++ b/boards/3dr/ctrl-n1/nuttx-config/include/board.h @@ -0,0 +1,292 @@ +/************************************************************************************ + * nuttx-config/include/board.h + * + * Copyright (C) 2026 Gregory Nutt. 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 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. + * + ************************************************************************************/ +#pragma once + +#include "board_dma_map.h" + +#include + +#ifndef __ASSEMBLY__ +# include +#endif + +#include "stm32_rcc.h" +#include "stm32_sdmmc.h" + +/* Clocking *************************************************************************/ +/* The board provides the following clock sources: + * + * X1: 24 MHz crystal for HSE + * + * So we have these clock source available within the STM32 + * + * HSI: 16 MHz RC factory-trimmed internal oscillator + * HSE: 24 MHz crystal for HSE + */ +#define STM32_BOARD_XTAL 24000000ul + +#define STM32_HSI_FREQUENCY 16000000ul +#define STM32_LSI_FREQUENCY 32000 +#define STM32_HSE_FREQUENCY STM32_BOARD_XTAL +#define STM32_LSE_FREQUENCY 0 + +#define STM32_HSEBYP_ENABLE 1 + +/* Main PLL Configuration. + * + * PLL source is HSE = 24,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_PLLCFG_PLLSRC RCC_PLLCKSELR_PLLSRC_HSE + +/* PLL1, wide 4 - 8 MHz input, enable DIVP, DIVQ, DIVR + * + * PLL1_VCO = (24,000,000 / 2) * 80 = 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(2) +#define STM32_PLLCFG_PLL1N RCC_PLL1DIVR_N1(80) +#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 / 2) * 80) +#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(32) +#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) * 32) +#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(32) +#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) * 32) +#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 + */ +#define STM32_RCC_D2CCIP2R_I2C123SRC RCC_D2CCIP2R_I2C123SEL_HSI /* I2C123 clock source */ +#define STM32_RCC_D3CCIPR_I2C4SRC RCC_D3CCIPR_I2C4SEL_HSI /* I2C4 clock source */ +#define STM32_RCC_D2CCIP1R_SPI123SRC RCC_D2CCIP1R_SPI123SEL_PLL2 /* SPI123 clock source */ +#define STM32_RCC_D2CCIP1R_SPI45SRC RCC_D2CCIP1R_SPI45SEL_PLL2 /* SPI45 clock source */ +#define STM32_RCC_D3CCIPR_SPI6SRC RCC_D3CCIPR_SPI6SEL_PLL2 /* SPI6 clock source */ +#define STM32_RCC_D2CCIP2R_USBSRC RCC_D2CCIP2R_USBSEL_PLL3 /* USB 1 and 2 clock source */ +#define STM32_RCC_D3CCIPR_ADCSRC RCC_D3CCIPR_ADCSEL_PLL2 /* ADC 1 2 3 clock source */ +#define STM32_RCC_D2CCIP1R_FDCANSEL RCC_D2CCIP1R_FDCANSEL_HSE /* FDCAN 1 2 clock source */ + +#define STM32_FDCANCLK STM32_HSE_FREQUENCY + +/* UART clock selection */ +/* reset to default to overwrite any changes done by any bootloader */ + +#define STM32_RCC_D2CCIP2R_USART234578_SEL RCC_D2CCIP2R_USART234578SEL_RCC +#define STM32_RCC_D2CCIP2R_USART16_SEL RCC_D2CCIP2R_USART16SEL_RCC + +/* FLASH wait states */ +#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 + + +/* UART/USART */ +#define GPIO_USART1_TX GPIO_USART1_TX_2 /* PA9 */ +#define GPIO_USART1_RX GPIO_USART1_RX_2 /* PA10 */ + +#define GPIO_USART2_TX GPIO_USART2_TX_2 /* PD5 */ +#define GPIO_USART2_RX GPIO_USART2_RX_2 /* PD6 */ +#define GPIO_USART2_CTS GPIO_USART2_CTS_NSS_2 /* PD3 */ +#define GPIO_USART2_RTS GPIO_USART2_RTS_2 /* PD4 */ + +#define GPIO_USART3_TX GPIO_USART3_TX_3 /* PD8 */ +#define GPIO_USART3_RX GPIO_USART3_RX_3 /* PD9 */ + +#define GPIO_UART4_TX GPIO_UART4_TX_5 /* PD1 */ +#define GPIO_UART4_RX GPIO_UART4_RX_5 /* PD0 */ +#define GPIO_UART4_CTS GPIO_UART4_CTS_1 /* PB0 */ +#define GPIO_UART4_RTS GPIO_UART4_RTS_1 /* PA15 */ + +#define GPIO_USART6_TX GPIO_USART6_TX_1 /* PC6 */ +#define GPIO_USART6_RX GPIO_USART6_RX_1 /* PC7 */ + +#define GPIO_UART7_TX GPIO_UART7_TX_3 /* PE8 */ +#define GPIO_UART7_RX GPIO_UART7_RX_3 /* PE7 */ +#define GPIO_UART7_CTS GPIO_UART7_CTS_1 /* PE10 */ +#define GPIO_UART7_RTS GPIO_UART7_RTS_1 /* PE9 */ + +#define GPIO_UART8_TX GPIO_UART8_TX_1 /* PE1 */ +#define GPIO_UART8_RX GPIO_UART8_RX_1 /* PE0 */ + + +/* CAN */ +#define GPIO_CAN1_RX GPIO_CAN1_RX_1 /* PA11 */ +#define GPIO_CAN1_TX GPIO_CAN1_TX_1 /* PA12 */ + +#define GPIO_CAN2_RX GPIO_CAN2_RX_1 /* PB12 */ +#define GPIO_CAN2_TX GPIO_CAN2_TX_2 /* PB6 */ + + +/* SPI */ +#define ADJ_SLEW_RATE(p) (((p) & ~GPIO_SPEED_MASK) | (GPIO_SPEED_2MHz)) + +#define GPIO_SPI1_SCK ADJ_SLEW_RATE(GPIO_SPI1_SCK_1) /* PA5 */ +#define GPIO_SPI1_MISO GPIO_SPI1_MISO_1 /* PA6 */ +#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_3 /* PD7 */ + +#define GPIO_SPI2_SCK ADJ_SLEW_RATE(GPIO_SPI2_SCK_4) /* PB13 */ +#define GPIO_SPI2_MISO GPIO_SPI2_MISO_2 /* PC2 */ +#define GPIO_SPI2_MOSI GPIO_SPI2_MOSI_2 /* PC1 */ + +#define GPIO_SPI4_SCK ADJ_SLEW_RATE(GPIO_SPI4_SCK_2) /* PE2 */ +#define GPIO_SPI4_MISO GPIO_SPI4_MISO_2 /* PE5 */ +#define GPIO_SPI4_MOSI GPIO_SPI4_MOSI_2 /* PE6 */ + +#define GPIO_SPI6_SCK ADJ_SLEW_RATE(GPIO_SPI6_SCK_3) /* PB3 */ +#define GPIO_SPI6_MISO GPIO_SPI6_MISO_3 /* PB4 */ +#define GPIO_SPI6_MOSI GPIO_SPI6_MOSI_3 /* PB5 */ + +/* I2C */ +#define GPIO_I2C1_SCL GPIO_I2C1_SCL_2 /* PB8 */ +#define GPIO_I2C1_SDA GPIO_I2C1_SDA_1 /* PB7 */ + +#define GPIO_I2C2_SCL GPIO_I2C2_SCL_1 /* PB10 */ +#define GPIO_I2C2_SDA GPIO_I2C2_SDA_1 /* PB11 */ diff --git a/boards/3dr/ctrl-n1/nuttx-config/include/board_dma_map.h b/boards/3dr/ctrl-n1/nuttx-config/include/board_dma_map.h new file mode 100755 index 00000000000..8aae1df261f --- /dev/null +++ b/boards/3dr/ctrl-n1/nuttx-config/include/board_dma_map.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * + * Copyright (c) 2026 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 +#define DMAMAP_SPI1_RX DMAMAP_DMA12_SPI1RX_0 /* DMA1:37 */ +#define DMAMAP_SPI1_TX DMAMAP_DMA12_SPI1TX_0 /* DMA1:38 */ + +#define DMAMAP_SPI2_RX DMAMAP_DMA12_SPI2RX_0 /* DMA1:39 */ +#define DMAMAP_SPI2_TX DMAMAP_DMA12_SPI2TX_0 /* DMA1:40 */ + +#define DMAMAP_SPI6_RX DMAMAP_BDMA_SPI6_RX /* DMA2:11 */ +#define DMAMAP_SPI6_TX DMAMAP_BDMA_SPI6_TX /* DMA2:12 */ + +#define DMAMAP_TIM3_CH4 DMAMAP_DMA12_TIM3CH4_0 /* DMA1:26 */ + +#define DMAMAP_USART2_RX DMAMAP_DMA12_USART2RX_1 /* DMA2:43 */ +#define DMAMAP_USART2_TX DMAMAP_DMA12_USART2TX_1 /* DMA2:44 */ + +#define DMAMAP_USART3_RX DMAMAP_DMA12_USART3RX_0 /* DMA1:45 */ +#define DMAMAP_USART3_TX DMAMAP_DMA12_USART3TX_0 /* DMA1:46 */ + +#define DMAMAP_USART6_RX DMAMAP_DMA12_USART6RX_1 /* DMA2:71 */ +#define DMAMAP_USART6_TX DMAMAP_DMA12_USART6TX_1 /* DMA2:72 */ diff --git a/boards/3dr/ctrl-n1/nuttx-config/nsh/defconfig b/boards/3dr/ctrl-n1/nuttx-config/nsh/defconfig new file mode 100644 index 00000000000..7004a93c212 --- /dev/null +++ b/boards/3dr/ctrl-n1/nuttx-config/nsh/defconfig @@ -0,0 +1,272 @@ +# +# 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_DISABLE_PTHREAD 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_CAT is not set +# CONFIG_NSH_DISABLE_CD is not set +# CONFIG_NSH_DISABLE_CP is not set +# CONFIG_NSH_DISABLE_DATE is not set +# CONFIG_NSH_DISABLE_DF is not set +# CONFIG_NSH_DISABLE_ECHO is not set +# CONFIG_NSH_DISABLE_ENV is not set +# CONFIG_NSH_DISABLE_EXEC is not set +# CONFIG_NSH_DISABLE_EXPORT is not set +# CONFIG_NSH_DISABLE_FREE is not set +# CONFIG_NSH_DISABLE_GET is not set +# CONFIG_NSH_DISABLE_HELP is not set +# CONFIG_NSH_DISABLE_ITEF is not set +# CONFIG_NSH_DISABLE_KILL is not set +# CONFIG_NSH_DISABLE_LOOPS is not set +# CONFIG_NSH_DISABLE_LS is not set +# CONFIG_NSH_DISABLE_MKDIR is not set +# CONFIG_NSH_DISABLE_MKFATFS is not set +# CONFIG_NSH_DISABLE_MOUNT is not set +# CONFIG_NSH_DISABLE_MV is not set +# CONFIG_NSH_DISABLE_PS is not set +# CONFIG_NSH_DISABLE_PSSTACKUSAGE is not set +# CONFIG_NSH_DISABLE_PWD is not set +# CONFIG_NSH_DISABLE_RM is not set +# CONFIG_NSH_DISABLE_RMDIR is not set +# CONFIG_NSH_DISABLE_SEMICOLON is not set +# CONFIG_NSH_DISABLE_SET is not set +# CONFIG_NSH_DISABLE_SLEEP is not set +# CONFIG_NSH_DISABLE_SOURCE is not set +# CONFIG_NSH_DISABLE_TEST is not set +# CONFIG_NSH_DISABLE_TIME is not set +# CONFIG_NSH_DISABLE_UMOUNT is not set +# CONFIG_NSH_DISABLE_UNSET is not set +# CONFIG_NSH_DISABLE_USLEEP is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD_CUSTOM=y +CONFIG_ARCH_BOARD_CUSTOM_DIR="../../../../boards/3dr/ctrl-n1/nuttx-config" +CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y +CONFIG_ARCH_BOARD_CUSTOM_NAME="px4" +CONFIG_ARCH_CHIP="stm32h7" +CONFIG_ARCH_CHIP_STM32H743VI=y +CONFIG_ARCH_CHIP_STM32H7=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +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_ASSERT_RESET_VALUE=0 +CONFIG_BOARD_CRASHDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=95150 +CONFIG_BOARD_RESET_ON_ASSERT=2 +CONFIG_BUILTIN=y +CONFIG_CDCACM=y +CONFIG_CDCACM_IFLOWCONTROL=y +CONFIG_CDCACM_PRODUCTID=0x1186 +CONFIG_CDCACM_PRODUCTSTR="3DRControlN1" +CONFIG_CDCACM_RXBUFSIZE=600 +CONFIG_CDCACM_TXBUFSIZE=12000 +CONFIG_CDCACM_VENDORID=0x26ac +CONFIG_CDCACM_VENDORSTR="3DR" +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_HARDFAULT_ALERT=y +CONFIG_DEBUG_MEMFAULT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEBUG_TCBINFO=y +CONFIG_DEFAULT_SMALL=y +CONFIG_DEV_FIFO_SIZE=0 +CONFIG_DEV_PIPE_MAXSIZE=1024 +CONFIG_DEV_PIPE_SIZE=70 +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_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_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=3194 +CONFIG_LIBC_FLOATINGPOINT=y +CONFIG_LIBC_LONG_LONG=y +CONFIG_LIBC_MAX_EXITFUNS=1 +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_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_VARS=y +CONFIG_PIPES=y +CONFIG_PREALLOC_TIMERS=50 +CONFIG_PRIORITY_INHERITANCE=y +CONFIG_PTHREAD_MUTEX_ROBUST=y +CONFIG_PTHREAD_STACK_MIN=512 +CONFIG_RAMTRON_EMULATE_PAGE_SHIFT=5 +CONFIG_RAMTRON_EMULATE_SECTOR_SHIFT=5 +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_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=249 +CONFIG_SCHED_HPWORKSTACKSIZE=1280 +CONFIG_SCHED_INSTRUMENTATION=y +CONFIG_SCHED_INSTRUMENTATION_EXTERNAL=y +CONFIG_SCHED_INSTRUMENTATION_SWITCH=y +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_LPWORKPRIORITY=50 +CONFIG_SCHED_LPWORKSTACKSIZE=1632 +CONFIG_SCHED_WAITPID=y +CONFIG_SDMMC1_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_FLOWCONTROL_BROKEN=y +CONFIG_STM32H7_I2C1=y +CONFIG_STM32H7_I2C2=y +CONFIG_STM32H7_I2C_DYNTIMEO=y +CONFIG_STM32H7_I2C_DYNTIMEO_STARTSTOP=10 +CONFIG_STM32H7_OTGHS=y +CONFIG_STM32H7_OTGHS_FS=y +CONFIG_STM32H7_PROGMEM=y +CONFIG_STM32H7_PWR_EXTERNAL_SOURCE_SUPPLY=y +CONFIG_STM32H7_RTC=y +CONFIG_STM32H7_RTC_HSECLOCK=y +CONFIG_STM32H7_RTC_MAGIC_REG=1 +CONFIG_STM32H7_SAVE_CRASHDUMP=y +CONFIG_STM32H7_SDMMC1=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=1024 +CONFIG_STM32H7_SPI4=y +CONFIG_STM32H7_SPI6=y +CONFIG_STM32H7_SPI6_DMA=y +CONFIG_STM32H7_SPI6_DMA_BUFFER=1024 +CONFIG_STM32H7_TIM12=y +CONFIG_STM32H7_TIM17=y +CONFIG_STM32H7_TIM1=y +CONFIG_STM32H7_TIM2=y +CONFIG_STM32H7_TIM3=y +CONFIG_STM32H7_TIM4=y +CONFIG_STM32H7_UART4=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_NSH=y +CONFIG_TASK_NAME_SIZE=24 +CONFIG_TTY_SIGINT=y +CONFIG_TTY_SIGTSTP=y +CONFIG_UART4_BAUD=57600 +CONFIG_UART4_RXBUFSIZE=600 +CONFIG_UART4_TXBUFSIZE=1500 +CONFIG_UART7_BAUD=57600 +CONFIG_UART7_IFLOWCONTROL=y +CONFIG_UART7_OFLOWCONTROL=y +CONFIG_UART7_RXBUFSIZE=600 +CONFIG_UART7_TXBUFSIZE=1500 +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_RXDMA=y +CONFIG_USART2_TXBUFSIZE=1500 +CONFIG_USART2_TXDMA=y +CONFIG_USART3_BAUD=57600 +CONFIG_USART3_RXBUFSIZE=600 +CONFIG_USART3_RXDMA=y +CONFIG_USART3_TXBUFSIZE=3000 +CONFIG_USART3_TXDMA=y +CONFIG_USART6_BAUD=57600 +CONFIG_USART6_RXBUFSIZE=600 +CONFIG_USART6_RXDMA=y +CONFIG_USART6_TXBUFSIZE=1500 +CONFIG_USART6_TXDMA=y +CONFIG_USBDEV=y +CONFIG_USBDEV_BUSPOWERED=y +CONFIG_USBDEV_MAXPOWER=500 +CONFIG_USEC_PER_TICK=1000 diff --git a/boards/3dr/ctrl-n1/nuttx-config/scripts/bootloader_script.ld b/boards/3dr/ctrl-n1/nuttx-config/scripts/bootloader_script.ld new file mode 100755 index 00000000000..dd41df29a55 --- /dev/null +++ b/boards/3dr/ctrl-n1/nuttx-config/scripts/bootloader_script.ld @@ -0,0 +1,215 @@ +/**************************************************************************** + * scripts/script.ld + * + * Copyright (C) 2026 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 3DR Control-N1 uses an STM32H743VI 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 STM32H743VI, 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 3DR Control-N1 has a switch on board, the BOOT0 pin is at ground so by default, + * the STM32 will boot to address 0x0800:0000 in FLASH unless the switch is + * depressed, then the boot will be from 0x1FF0:0000 + * + * The STM32H743VI 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/3dr/ctrl-n1/nuttx-config/scripts/script.ld b/boards/3dr/ctrl-n1/nuttx-config/scripts/script.ld new file mode 100755 index 00000000000..c62f374e7a3 --- /dev/null +++ b/boards/3dr/ctrl-n1/nuttx-config/scripts/script.ld @@ -0,0 +1,228 @@ +/**************************************************************************** + * scripts/script.ld + * + * Copyright (C) 2026 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 3DR Control-N1 uses an STM32H743VI 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 STM32H743VI, 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 3DR Control-N1 has a switch on board, the BOOT0 pin is at ground so by default, + * the STM32 will boot to address 0x0800:0000 in FLASH unless the switch is + * depressed, then the boot will be from 0x1FF0:0000 + * + * The STM32H743VI 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 /* params in last sector */ + + 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) + +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/3dr/ctrl-n1/src/CMakeLists.txt b/boards/3dr/ctrl-n1/src/CMakeLists.txt new file mode 100755 index 00000000000..d1c95483d93 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/CMakeLists.txt @@ -0,0 +1,65 @@ +############################################################################ +# +# Copyright (c) 2026 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 + nuttx_drivers + bootloader + ) + target_include_directories(drivers_board PRIVATE ${PX4_SOURCE_DIR}/platforms/nuttx/src/bootloader/common) + +else() + add_library(drivers_board + i2c.cpp + init.c + led.c + spi.cpp + timer_config.cpp + usb.c + ) + + target_link_libraries(drivers_board + PRIVATE + arch_io_pins + arch_spi + drivers__led + nuttx_arch + nuttx_drivers + px4_layer + ) +endif() diff --git a/boards/3dr/ctrl-n1/src/board_config.h b/boards/3dr/ctrl-n1/src/board_config.h new file mode 100755 index 00000000000..d1d22ac2119 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/board_config.h @@ -0,0 +1,152 @@ +/**************************************************************************** + * + * Copyright (c) 2026 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 + * + * Board internal definitions + */ + +#pragma once + +#include +#include +#include +#include + +/* LEDs */ +#define GPIO_nLED_BL /* PD10 */ (GPIO_OUTPUT|GPIO_OPENDRAIN|GPIO_SPEED_50MHz|GPIO_OUTPUT_SET|GPIO_PORTD|GPIO_PIN10) +#define GPIO_RGB_S /* PB1 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN1) +#define BOARD_SRGBLED_PORT STM32_GPIOB_ODR +#define BOARD_SRGBLED_BIT 1 + +//#define BOARD_HAS_CONTROL_STATUS_LEDS 1 +//#define BOARD_OVERLOAD_LED LED_RED +//#define BOARD_ARMED_STATE_LED LED_BLUE + +/* ADC channels */ +#define PX4_ADC_GPIO \ + /* PA4 */ GPIO_ADC12_INP18, \ + /* PC0 */ GPIO_ADC123_INP10, \ + /* PC5 */ GPIO_ADC12_INP8 + +/* Define Channel numbers must match above GPIO pins */ +#define ADC_BATTERY_VOLTAGE_CHANNEL 18 /* PA2 BATT_VOLT_SENS */ +#define ADC_BATTERY_CURRENT_CHANNEL 10 /* PC0 BATT_CURRENT_SENS */ +#define ADC_SCALED_V5_CHANNEL 8 /* PC5 VDD_5V_SENS */ + +#define ADC_CHANNELS \ + ((1 << ADC_BATTERY_VOLTAGE_CHANNEL) | \ + (1 << ADC_BATTERY_CURRENT_CHANNEL) | \ + (1 << ADC_SCALED_V5_CHANNEL)) + + +/* HW has to large of R termination on ADC todo:change when HW value is chosen */ +#define BOARD_ADC_OPEN_CIRCUIT_V (5.6f) + +/* PWM */ +#define DIRECT_PWM_OUTPUT_CHANNELS 12 + +/* Tone alarm output */ +#define TONE_ALARM_TIMER 17 /* timer 17 */ +#define TONE_ALARM_CHANNEL 1 /* PB9 TIM17_CH1 */ + +#define GPIO_BUZZER_1 /* PB9 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN9) + +#define GPIO_TONE_ALARM_IDLE GPIO_BUZZER_1 +#define GPIO_TONE_ALARM GPIO_TIM17_CH1OUT_1 + +/* NO USB OTG FS VBUS */ +#define BOARD_USB_VBUS_SENSE_DISABLED 1 +#define GPIO_OTGFS_VBUS 0 + +/* High-resolution timer */ +#define HRT_TIMER 12 /* use timer3 for the HRT */ +#define HRT_TIMER_CHANNEL 1 /* use capture/compare channel 1 */ + +/* RC Serial port */ +//Uncomment to accept "legacy" protocols +//#define RC_SERIAL_PORT "/dev/ttyS5" + +/* 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(ADC_SCALED_V5_CHANNEL)) +#define BOARD_ADC_USB_VALID BOARD_ADC_USB_CONNECTED +#define BOARD_ADC_SERVO_VALID (1) /* never powers off the Servo rail */ + +/* This board provides a DMA pool and APIs */ +#define BOARD_DMA_ALLOC_POOL_SIZE 5120 + +/* This board has 6 DMA channels available for bidirectional dshot */ +#define BOARD_DMA_NUM_DSHOT_CHANNELS 6 + +/* This board provides the board_on_reset interface */ +#define BOARD_HAS_ON_RESET 1 + +#define BOARD_HAS_STATIC_MANIFEST 1 + +#define BOARD_NUM_IO_TIMERS 3 + +#define BOARD_ENABLE_CONSOLE_BUFFER + +#define PX4_GPIO_INIT_LIST { \ + PX4_ADC_GPIO, \ + GPIO_CAN1_TX, \ + GPIO_CAN1_RX, \ + GPIO_CAN2_TX, \ + GPIO_CAN2_RX, \ + GPIO_TONE_ALARM_IDLE, \ + } + +__BEGIN_DECLS + + +#if defined(USE_S_RGB_LED_DMA) +#define BOARD_HAS_N_S_RGB_LED 1 +#define BOARD_MAX_LEDS BOARD_HAS_N_S_RGB_LED +#define S_RGB_LED_DMA DMAMAP_DMA12_TIM3CH4_0 +#define S_RGB_LED_TIMER 3 /* timer 3 */ +#define S_RGB_LED_CHANNEL 4 /* channel 4 */ +#define S_RGB_LED_TIM_GPIO GPIO_TIM3_CH4OUT_1 +#endif + +#ifndef __ASSEMBLY__ + +extern void stm32_spiinitialize(void); +extern void board_peripheral_reset(int ms); + +#include +#endif /* __ASSEMBLY__ */ +__END_DECLS diff --git a/boards/3dr/ctrl-n1/src/bootloader_main.c b/boards/3dr/ctrl-n1/src/bootloader_main.c new file mode 100755 index 00000000000..2487678aba6 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/bootloader_main.c @@ -0,0 +1,75 @@ +/**************************************************************************** + * + * Copyright (c) 2026 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_configgpio(GPIO_OTGFS_VBUS); +} + +__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/3dr/ctrl-n1/src/hw_config.h b/boards/3dr/ctrl-n1/src/hw_config.h new file mode 100755 index 00000000000..f00aebea63d --- /dev/null +++ b/boards/3dr/ctrl-n1/src/hw_config.h @@ -0,0 +1,132 @@ +/**************************************************************************** + * + * Copyright (C) 2026 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 + +/**************************************************************************** + * 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 APP_LOAD_ADDRESS 0x08020000 +#define BOOTLOADER_DELAY 5000 +#define INTERFACE_USB 1 +#define INTERFACE_USB_CONFIG "/dev/ttyACM0" + +#define INTERFACE_USART 1 +#define INTERFACE_USART_CONFIG "/dev/ttyS4,115200" +#define BOOT_DELAY_ADDRESS 0x000001a0 +#define BOARD_TYPE 1186 +#define _FLASH_KBYTES (*(uint32_t *)0x1FF1E880) +#define BOARD_FLASH_SECTORS (15) +#define BOARD_FLASH_SIZE (_FLASH_KBYTES * 1024) + +#define OSC_FREQ 24 + +#define BOARD_PIN_LED_ACTIVITY GPIO_nLED_BL +#define BOARD_PIN_LED_BOOTLOADER GPIO_nLED_BL +#define BOARD_LED_ON 0 +#define BOARD_LED_OFF 1 + +#define SERIAL_BREAK_DETECT_DISABLED 1 + +#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) +#endif + +#ifndef BOOT_DEVICES_FILTER_ONUSB +# define BOOT_DEVICES_FILTER_ONUSB (USB0_DEV|SERIAL0_DEV) +#endif diff --git a/boards/3dr/ctrl-n1/src/i2c.cpp b/boards/3dr/ctrl-n1/src/i2c.cpp new file mode 100755 index 00000000000..9ee5b2e4bf5 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/i2c.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** + * + * Copyright (C) 2026 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), +}; diff --git a/boards/3dr/ctrl-n1/src/init.c b/boards/3dr/ctrl-n1/src/init.c new file mode 100755 index 00000000000..ed3609d99fb --- /dev/null +++ b/boards/3dr/ctrl-n1/src/init.c @@ -0,0 +1,199 @@ +/**************************************************************************** + * + * Copyright (c) 2026 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 + * + * board-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 initialisation. + */ + +#include "board_config.h" + +#include + +#include +#include +#include +#include +#include +#include "arm_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +__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) +{ + /* wait for the peripheral rail to reach GND */ + usleep(ms * 1000); + syslog(LOG_DEBUG, "reset done, %d ms\n", ms); +} + +/************************************************************************************ + * 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))); + } + + /* + * On resets invoked from system (not boot) ensure we establish a low + * output state on PWM pins to disarm the ESC and prevent the reset from potentially + * spinning up the motors. + */ + if (status >= 0) { + up_mdelay(100); + } +} + +/************************************************************************************ + * 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) +{ + /* Reset PWM first thing */ + board_on_reset(-1); + + /* configure pins */ + const uint32_t gpio[] = PX4_GPIO_INIT_LIST; + px4_gpio_init(gpio, arraySize(gpio)); + board_control_spi_sensors_power_configgpio(); + + /* configure LEDs */ + board_autoled_initialize(); +} + +/**************************************************************************** + * 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; + * + * 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 */ + board_control_spi_sensors_power(true, 0xffff); + + px4_platform_init(); + + stm32_spiinitialize(); + + /* configure the DMA allocator */ + if (board_dma_alloc_init() < 0) { + syslog(LOG_ERR, "[boot] DMA alloc FAILED\n"); + } + + + /* 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); + } + +#ifdef CONFIG_MMCSD + /* Mount the SDIO-based MMC/SD block driver */ + /* First, get an instance of the SDIO interface */ + struct sdio_dev_s *sdio_dev = sdio_initialize(0); // SDIO_SLOTNO 0 Only one slot + + if (!sdio_dev) { + syslog(LOG_ERR, "[boot] Failed to initialize SDIO slot %d\n", 0); + } + + if (mmcsd_slotinitialize(0, sdio_dev) != OK) { + syslog(LOG_ERR, "[boot] Failed to bind SDIO to the MMC/SD driver\n"); + } + + /* Assume that the SD card is inserted. What choice do we have? */ + sdio_mediachange(sdio_dev, true); +#endif /* CONFIG_MMCSD */ + + /* Configure the HW based on the manifest */ + + px4_platform_configure(); + + return OK; +} diff --git a/boards/3dr/ctrl-n1/src/led.c b/boards/3dr/ctrl-n1/src/led.c new file mode 100755 index 00000000000..4e7e455ecc3 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/led.c @@ -0,0 +1,109 @@ +/**************************************************************************** + * + * Copyright (c) 2026 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 led.c + * + * 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 + +static uint32_t g_ledmap[] = { + GPIO_nLED_BL // TODO: Finish remapping the LEDs +}; + +__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(led, true); +} + +__EXPORT void led_off(int led) +{ + phy_set_led(led, false); +} + +__EXPORT void led_toggle(int led) +{ + phy_set_led(led, !phy_get_led(led)); +} diff --git a/boards/3dr/ctrl-n1/src/spi.cpp b/boards/3dr/ctrl-n1/src/spi.cpp new file mode 100755 index 00000000000..052fcc1f228 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/spi.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** + * + * Copyright (C) 2026 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_t px4_spi_buses[SPI_BUS_MAX_BUS_ITEMS] = { + initSPIBus(SPI::Bus::SPI1, { + initSPIDevice(DRV_IMU_DEVTYPE_IIM42653, SPI::CS{GPIO::PortE, GPIO::Pin12}), + }), + initSPIBus(SPI::Bus::SPI2, { + initSPIDevice(DRV_IMU_DEVTYPE_IIM42653, SPI::CS{GPIO::PortE, GPIO::Pin15}), + }), + initSPIBusExternal(SPI::Bus::SPI4, { + initSPIConfigExternal(SPI::CS{GPIO::PortB, GPIO::Pin2}), + }), + initSPIBus(SPI::Bus::SPI6, { + initSPIDevice(SPIDEV_FLASH(0), SPI::CS{GPIO::PortC, GPIO::Pin13}), + initSPIDevice(DRV_BARO_DEVTYPE_DPS310, SPI::CS{GPIO::PortE, GPIO::Pin3}), + }), +}; + +static constexpr bool unused = validateSPIConfig(px4_spi_buses); diff --git a/boards/3dr/ctrl-n1/src/timer_config.cpp b/boards/3dr/ctrl-n1/src/timer_config.cpp new file mode 100755 index 00000000000..96855d6ffa5 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/timer_config.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** + * + * Copyright (C) 2026 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 io_timers_t io_timers[MAX_IO_TIMERS] = { + initIOTimer(Timer::Timer1, DMA{DMA::Index1}), + initIOTimer(Timer::Timer2, DMA{DMA::Index1}), + initIOTimer(Timer::Timer4, DMA{DMA::Index1}), +}; + +constexpr timer_io_channels_t timer_io_channels[MAX_TIMER_IO_CHANNELS] = { + initIOTimerChannel(io_timers, {Timer::Timer1, Timer::Channel1}, {GPIO::PortA, GPIO::Pin8}), + initIOTimerChannel(io_timers, {Timer::Timer1, Timer::Channel2}, {GPIO::PortE, GPIO::Pin11}), + initIOTimerChannel(io_timers, {Timer::Timer1, Timer::Channel3}, {GPIO::PortE, GPIO::Pin13}), + initIOTimerChannel(io_timers, {Timer::Timer1, Timer::Channel4}, {GPIO::PortE, GPIO::Pin14}), + initIOTimerChannel(io_timers, {Timer::Timer2, Timer::Channel4}, {GPIO::PortA, GPIO::Pin3}), + initIOTimerChannel(io_timers, {Timer::Timer2, Timer::Channel3}, {GPIO::PortA, GPIO::Pin2}), + initIOTimerChannel(io_timers, {Timer::Timer2, Timer::Channel2}, {GPIO::PortA, GPIO::Pin1}), + initIOTimerChannel(io_timers, {Timer::Timer2, Timer::Channel1}, {GPIO::PortA, GPIO::Pin0}), + initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel1}, {GPIO::PortD, GPIO::Pin12}), + 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::Timer4, Timer::Channel4}, {GPIO::PortD, GPIO::Pin15}), +}; + +constexpr io_timers_channel_mapping_t io_timers_channel_mapping = + initIOTimerChannelMapping(io_timers, timer_io_channels); diff --git a/boards/3dr/ctrl-n1/src/usb.c b/boards/3dr/ctrl-n1/src/usb.c new file mode 100755 index 00000000000..a262d693125 --- /dev/null +++ b/boards/3dr/ctrl-n1/src/usb.c @@ -0,0 +1,78 @@ +/**************************************************************************** + * + * Copyright (C) 2026 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 usb.c + * + * Board-specific USB functions. + */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "board_config.h" + +/************************************************************************************ + * 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_usbinitialize(void) +{ +} + +__EXPORT void stm32_usbsuspend(FAR struct usbdev_s *dev, bool resume) +{ + uinfo("resume: %d\n", resume); +} + +__EXPORT int board_read_VBUS_state(void) +{ + return 0; +} diff --git a/platforms/nuttx/src/px4/stm/stm32_common/srgbled_dma/srgbled_dma.cpp b/platforms/nuttx/src/px4/stm/stm32_common/srgbled_dma/srgbled_dma.cpp index 7f6b6a20d5c..100ff067bd1 100644 --- a/platforms/nuttx/src/px4/stm/stm32_common/srgbled_dma/srgbled_dma.cpp +++ b/platforms/nuttx/src/px4/stm/stm32_common/srgbled_dma/srgbled_dma.cpp @@ -374,11 +374,22 @@ extern int neopixel_write(neopixel::NeoLEDData *led_data, int number_of_packages // Set up the DMA Operations +#if defined(CONFIG_ARCH_CHIP_STM32H7) + struct stm32_dma_config_s dma_cfg; + dma_cfg.paddr = _TIM_REG(STM32_GTIM_DMAR_OFFSET); + dma_cfg.maddr = (uint32_t) bits; + dma_cfg.cfg1 = SLED_DMA_SCR; + dma_cfg.cfg2 = 0; + dma_cfg.ndata = arraySize(bits); + stm32_dmasetup(dma_handle, &dma_cfg); +#else stm32_dmasetup(dma_handle, _TIM_REG(STM32_GTIM_DMAR_OFFSET), (uint32_t) bits, arraySize(bits), SLED_DMA_SCR); +#endif + // atomic operations irqstate_t flags = px4_enter_critical_section(); diff --git a/platforms/nuttx/src/px4/stm/stm32h7/CMakeLists.txt b/platforms/nuttx/src/px4/stm/stm32h7/CMakeLists.txt index 1205a6eda24..ed3e9ba0049 100644 --- a/platforms/nuttx/src/px4/stm/stm32h7/CMakeLists.txt +++ b/platforms/nuttx/src/px4/stm/stm32h7/CMakeLists.txt @@ -39,6 +39,7 @@ add_subdirectory(../stm32_common/board_reset board_reset) add_subdirectory(../stm32_common/dshot dshot) add_subdirectory(../stm32_common/hrt hrt) add_subdirectory(../stm32_common/led_pwm led_pwm) +add_subdirectory(../stm32_common/srgbled_dma srgbled_dma) add_subdirectory(../stm32_common/io_pins io_pins) add_subdirectory(../stm32_common/spi spi) add_subdirectory(../stm32_common/tone_alarm tone_alarm) diff --git a/src/drivers/drv_sensor.h b/src/drivers/drv_sensor.h index 8947720f9b3..43bc00a953b 100644 --- a/src/drivers/drv_sensor.h +++ b/src/drivers/drv_sensor.h @@ -277,6 +277,8 @@ #define DRV_MOTOR_DEVTYPE_HIWONDER_EMM 0xED +#define DRV_MAG_DEVTYPE_AK09940A 0xF1 + #define DRV_DEVTYPE_UNUSED 0xff #endif /* _DRV_SENSOR_H */ diff --git a/src/drivers/magnetometer/akm/CMakeLists.txt b/src/drivers/magnetometer/akm/CMakeLists.txt index 6b3977f977b..cff6b7fb5e1 100644 --- a/src/drivers/magnetometer/akm/CMakeLists.txt +++ b/src/drivers/magnetometer/akm/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory(ak8963) add_subdirectory(ak09916) +add_subdirectory(ak09940a) diff --git a/src/drivers/magnetometer/akm/ak09940a/AK09940A.cpp b/src/drivers/magnetometer/akm/ak09940a/AK09940A.cpp new file mode 100644 index 00000000000..ecf1c09ccc7 --- /dev/null +++ b/src/drivers/magnetometer/akm/ak09940a/AK09940A.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** + * + * Copyright (c) 2019-2026 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 "AK09940A.hpp" + +using namespace time_literals; + +static constexpr int16_t combine(uint8_t msb, uint8_t lsb) +{ + return (msb << 8u) | lsb; +} + +static constexpr int32_t combine(uint8_t hh, uint8_t hm, uint8_t hl) +{ + // Combine into 24-bit word + int32_t raw = (hh << 16) | (hm << 8) | hl; + + // Overflow marker check (datasheet says 0x1FFFF means overflow) + if (raw == 0x1FFFF) { + return raw; + } + + // Keep only 18 bits + raw &= 0x3FFFF; + + // Sign-extend from 18 bits + if (raw & (1 << 17)) { + raw -= (1 << 18); + } + + return raw; +} + +AK09940A::AK09940A(const I2CSPIDriverConfig &config) : + I2C(config), + I2CSPIDriver(config), + _px4_mag(get_device_id(), config.rotation) +{ +} + +AK09940A::~AK09940A() +{ + perf_free(_reset_perf); + perf_free(_bad_register_perf); + perf_free(_bad_transfer_perf); + perf_free(_magnetic_sensor_overflow_perf); +} + +int AK09940A::init() +{ + int ret = I2C::init(); + + if (ret != PX4_OK) { + DEVICE_DEBUG("I2C::init failed (%i)", ret); + return ret; + } + + return Reset() ? 0 : -1; +} + +bool AK09940A::Reset() +{ + _state = STATE::RESET; + ScheduleClear(); + ScheduleNow(); + return true; +} + +void AK09940A::print_status() +{ + I2CSPIDriverBase::print_status(); + PX4_INFO("Variant: %s", device_name()); + + perf_print_counter(_reset_perf); + perf_print_counter(_bad_register_perf); + perf_print_counter(_bad_transfer_perf); + perf_print_counter(_magnetic_sensor_overflow_perf); +} + +int AK09940A::probe() +{ + // 3 retries + for (int i = 0; i < 3; i++) { + + const uint8_t WIA1 = RegisterRead(Register::WIA1); + const uint8_t WIA2 = RegisterRead(Register::WIA2); + + if ((WIA1 != Company_ID)) { + PX4_DEBUG("unexpected WIA1 0x%02x", WIA1); + return PX4_ERROR; + } + + switch (static_cast(WIA2)) { + case AKTYPE::AK09940A: + _device = AKTYPE::AK09940A; + return PX4_OK; + + default: + PX4_DEBUG("unexpected WIA2 0x%02x", WIA2); + }; + } + + return PX4_ERROR; +} + +void AK09940A::RunImpl() +{ + const hrt_abstime now = hrt_absolute_time(); + + switch (_state) { + case STATE::RESET: + perf_count(_reset_perf); + // CNTL4 SRST: Soft reset + RegisterWrite(Register::CNTL4, static_cast(CNTL4_BIT::SRST)); + _reset_timestamp = now; + _failure_count = 0; + _state = STATE::WAIT_FOR_RESET; + ScheduleDelayed(100_ms); + break; + + case STATE::WAIT_FOR_RESET: { + if ((RegisterRead(Register::WIA1) == Company_ID) && (static_cast(RegisterRead(Register::WIA2)) == _device)) { + // if reset succeeded then configure + _state = STATE::CONFIGURE; + ScheduleDelayed(100_ms); + + } else { + // RESET not complete + if (hrt_elapsed_time(&_reset_timestamp) > 30_s) { + PX4_ERR("Reset failed, retrying"); + Reset(); + + } else { + PX4_DEBUG("Reset not complete, check again in 100 ms"); + ScheduleDelayed(100_ms); + } + } + + break; + } + + case STATE::CONFIGURE: + if (Configure()) { + // if configure succeeded then start reading + _state = STATE::READ; + ScheduleOnInterval(20_ms, 20_ms); // 50 Hz + + } else { + // CONFIGURE not complete + if (hrt_elapsed_time(&_reset_timestamp) > 30_s) { + PX4_ERR("Configure failed, resetting"); + Reset(); + + } else { + PX4_DEBUG("Configure failed, retrying"); + ScheduleDelayed(100_ms); + } + } + + break; + + case STATE::READ: { + + struct TransferBuffer { + uint8_t ST1; + uint8_t HXL; + uint8_t HXM; + uint8_t HXH; + uint8_t HYL; + uint8_t HYM; + uint8_t HYH; + uint8_t HZL; + uint8_t HZM; + uint8_t HZH; + uint8_t TMPS; + uint8_t ST2; + } buffer{}; + + uint8_t cmd = static_cast(Register::ST1); + int ret = transfer(&cmd, 1, (uint8_t *)&buffer, sizeof(TransferBuffer)); + + bool success = false; + + if (ret == PX4_OK) { + if (buffer.ST1 & static_cast(ST1_BIT::DRDY)) { + + const int32_t x = combine(buffer.HXH, buffer.HXM, buffer.HXL); + const int32_t y = combine(buffer.HYH, buffer.HYM, buffer.HYL); + const int32_t z = combine(buffer.HZH, buffer.HZM, buffer.HZL); + + if (buffer.ST2 & static_cast(ST2_BIT::INV)) { + // invalid + perf_count(_bad_transfer_perf); + + } else { + if (x == 0x1FFFF || y == 0x1FFFF || z == 0x1FFFF) { + // Magnetic Overflow is present + perf_count(_magnetic_sensor_overflow_perf); + + } else { + + // sensor's frame is +X forward (X), +Y right (Y), +Z down (Z) + _px4_mag.update(now, x, y, z); + _px4_mag.set_temperature((float)(30 - ((int8_t)buffer.TMPS) / 1.7)); + } + + // Submit errors + _px4_mag.set_error_count(perf_event_count(_bad_register_perf) + + perf_event_count(_bad_transfer_perf) + + perf_event_count(_magnetic_sensor_overflow_perf)); + + 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_cfg[_checked_register])) { + _last_config_check_timestamp = now; + _checked_register = (_checked_register + 1) % size_register_cfg; + + } else { + // register check failed, force reset + perf_count(_bad_register_perf); + Reset(); + } + } + } + + break; + } +} + +bool AK09940A::Configure() +{ + _retries = 2; + + // first set and clear all configured register bits + for (const auto ®_cfg : _register_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_cfg) { + if (!RegisterCheck(reg_cfg)) { + success = false; + } + } + + // mag resolution is 1.0e-4 Gauss per bit (10 nT/LSB) + _px4_mag.set_scale(1.0e-4f); + + return success; +} + +bool AK09940A::RegisterCheck(const register_config_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; +} + +uint8_t AK09940A::RegisterRead(Register reg) +{ + const uint8_t cmd = static_cast(reg); + uint8_t buffer{}; + transfer(&cmd, 1, &buffer, 1); + return buffer; +} + +void AK09940A::RegisterWrite(Register reg, uint8_t value) +{ + uint8_t buffer[2] { (uint8_t)reg, value }; + transfer(buffer, sizeof(buffer), nullptr, 0); +} + +void AK09940A::RegisterSetAndClearBits(Register 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); + } +} diff --git a/src/drivers/magnetometer/akm/ak09940a/AK09940A.hpp b/src/drivers/magnetometer/akm/ak09940a/AK09940A.hpp new file mode 100644 index 00000000000..60739496172 --- /dev/null +++ b/src/drivers/magnetometer/akm/ak09940a/AK09940A.hpp @@ -0,0 +1,131 @@ +/**************************************************************************** + * + * Copyright (c) 2019-2026 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 AK09940A.hpp + * + * Driver for the AKM AK09940A connected via I2C. + * + */ + +#pragma once + +#include "AKM_AK09940A_registers.hpp" + +#include +#include +#include +#include +#include + +using namespace AKM_AK09940A; + +class AK09940A : public device::I2C, public I2CSPIDriver +{ +public: + AK09940A(const I2CSPIDriverConfig &config); + ~AK09940A() override; + + static void print_usage(); + + void RunImpl(); + + int init() override; + void print_status() override; + +private: + enum class AKTYPE : uint8_t { + AK09940A = 0xA3, + }; + + constexpr char const *device_name() + { + switch (_device) { + case AKTYPE::AK09940A: + return "AK09940A"; + + default: + return "Unknown"; + + }; + } + + struct register_config_t { + Register reg; + uint8_t set_bits{0}; + uint8_t clear_bits{0}; + }; + + int probe() override; + + bool Reset(); + + bool Configure(); + + bool RegisterCheck(const register_config_t ®_cfg); + + uint8_t RegisterRead(Register reg); + void RegisterWrite(Register reg, uint8_t value); + void RegisterSetAndClearBits(Register reg, uint8_t setbits, uint8_t clearbits); + + PX4Magnetometer _px4_mag; + + perf_counter_t _reset_perf{perf_alloc(PC_COUNT, MODULE_NAME": reset")}; + 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 _magnetic_sensor_overflow_perf{perf_alloc(PC_COUNT, MODULE_NAME": magnetic sensor overflow")}; + + hrt_abstime _reset_timestamp{0}; + hrt_abstime _last_config_check_timestamp{0}; + int _failure_count{0}; + + enum class STATE : uint8_t { + RESET, + WAIT_FOR_RESET, + CONFIGURE, + READ, + } _state{STATE::RESET}; + + uint8_t _checked_register{0}; + static constexpr uint8_t size_register_cfg{1}; + // TODO: Add the self test configuration + register_config_t _register_cfg[size_register_cfg] { + // Register | Set bits, Clear bits + { + Register::CNTL3, static_cast(CNTL3_BIT::MODE3_SET), + static_cast(CNTL3_BIT::MODE_PWR_DN) + } + }; + + AKTYPE _device; +}; diff --git a/src/drivers/magnetometer/akm/ak09940a/AKM_AK09940A_registers.hpp b/src/drivers/magnetometer/akm/ak09940a/AKM_AK09940A_registers.hpp new file mode 100644 index 00000000000..937f2e5a09c --- /dev/null +++ b/src/drivers/magnetometer/akm/ak09940a/AKM_AK09940A_registers.hpp @@ -0,0 +1,132 @@ +/**************************************************************************** + * + * Copyright (c) 2019-2026 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 AKM_AK09940A_registers.hpp + * + * Asahi Kasei Microdevices (AKM) AK09940A registers. + * + */ + +#pragma once + +#include + +namespace AKM_AK09940A +{ + +// 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 I2C_SPEED = 400 * 1000; // 400 kHz I2C serial interface +static constexpr uint8_t I2C_ADDRESS_DEFAULT = 0b0001100; + +static constexpr uint8_t Company_ID = 0x48; + +enum class Register : uint8_t { + WIA1 = 0x00, // Company ID of AKM + WIA2 = 0x01, // Device ID of AK09940A + + ST = 0x0F, // Status (for Polling) + ST1 = 0x10, // Status 1 + HXL = 0x11, // Measurements for X-axis data + HXM = 0x12, + HXH = 0x13, + HYL = 0x14, // Measurements for Y-axis data + HYM = 0x15, + HYH = 0x16, + HZL = 0x17, // Measurements for Z-axis data + HZM = 0x18, + HZH = 0x19, + TMPS = 0x1A, // Measurement Temperature data + ST2 = 0x1B, // Status 2 + + SXL = 0x20, // Self test data: X-axis data + SXH = 0x21, + SYL = 0x22, // Self test data: Y-axis data + SYH = 0x23, + SZL = 0x24, // Self test data: Z-axis data + SZH = 0x25, + + CNTL1 = 0x30, // Control 1 + CNTL2 = 0x31, // Control 2 + CNTL3 = 0x32, // Control 3 + CNTL4 = 0x33, // Control 4 +}; + +// ST (for polling) +enum ST_BIT : uint8_t { + DOR = Bit1, // Data overrun + DRDY = Bit0, // Data is ready +}; + +// ST1 +enum class ST1_BIT : uint8_t { + // FNUM[4:1] bits correspond to how maany data sets are into FIFO buffer, up to 0b1000 (8 sets) + FNUM = Bit4 | Bit3 | Bit2 | Bit1, + DRDY = Bit0, // Data is Ready +}; + +// ST2 +enum class ST2_BIT : uint8_t { + INV = Bit1, // Data is invalid + DOR = Bit0, // Data overrun +}; + +// CNTL2 +enum class CNTL2_BIT : uint8_t { + TEM = Bit6, // Temperature measurement, enable/disable +}; + +// CNTL3 +enum class CNTL3_BIT : uint8_t { + // MODE[4:0] bits + MODE3_SET = Bit2 | Bit1, // “00110”: Continuous measurement mode 3 (50Hz) + MODE_SELF_TEST = Bit4, // "10000": Self-test mode + MT = Bit6 | Bit5, // Power Drive setting + MODE_PWR_DN = Bit4 | Bit3 | Bit0, // "00000": Power-down mode +}; + +// CNTL4 +enum class CNTL4_BIT : uint8_t { + SRST = Bit0, +}; + +} // namespace AKM_AK09940A diff --git a/src/drivers/magnetometer/akm/ak09940a/CMakeLists.txt b/src/drivers/magnetometer/akm/ak09940a/CMakeLists.txt new file mode 100644 index 00000000000..c01de5a1a72 --- /dev/null +++ b/src/drivers/magnetometer/akm/ak09940a/CMakeLists.txt @@ -0,0 +1,48 @@ +############################################################################ +# +# Copyright (c) 2019-2026 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__magnetometer__akm__ak09940a + MAIN ak09940a + COMPILE_FLAGS + #-DDEBUG_BUILD # uncomment for PX4_DEBUG output + #-O0 # uncomment when debugging + SRCS + AKM_AK09940A_registers.hpp + AK09940A.cpp + AK09940A.hpp + ak09940a_main.cpp + DEPENDS + drivers_magnetometer + px4_work_queue + ) diff --git a/src/drivers/magnetometer/akm/ak09940a/Kconfig b/src/drivers/magnetometer/akm/ak09940a/Kconfig new file mode 100644 index 00000000000..828fce60434 --- /dev/null +++ b/src/drivers/magnetometer/akm/ak09940a/Kconfig @@ -0,0 +1,5 @@ +menuconfig DRIVERS_MAGNETOMETER_AKM_AK09940A + bool "ak09940a" + default n + ---help--- + Enable support for ak09940a diff --git a/src/drivers/magnetometer/akm/ak09940a/ak09940a_main.cpp b/src/drivers/magnetometer/akm/ak09940a/ak09940a_main.cpp new file mode 100644 index 00000000000..a7e0c33bc50 --- /dev/null +++ b/src/drivers/magnetometer/akm/ak09940a/ak09940a_main.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** + * + * Copyright (c) 2019-2026 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 "AK09940A.hpp" + +#include +#include + +void AK09940A::print_usage() +{ + PRINT_MODULE_USAGE_NAME("ak09940a", "driver"); + PRINT_MODULE_USAGE_SUBCATEGORY("magnetometer"); + PRINT_MODULE_USAGE_COMMAND("start"); + PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(true, false); + PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS(0x0c); + PRINT_MODULE_USAGE_PARAM_INT('R', 0, 0, 35, "Rotation", true); + PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); +} + +extern "C" __EXPORT int ak09940a_main(int argc, char *argv[]) +{ + int ch; + using ThisDriver = AK09940A; + BusCLIArguments cli{true, false}; + cli.default_i2c_frequency = I2C_SPEED; + cli.i2c_address = I2C_ADDRESS_DEFAULT; + + 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_MAG_DEVTYPE_AK09940A); + + 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; +}