From 8462ead986378a33d47224a045815b114367e072 Mon Sep 17 00:00:00 2001 From: Liu1 <108582329+cuav-liu1@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:54:33 +0800 Subject: [PATCH] boards: add new cuav x25-evo (#25176) --- .vscode/cmake-variants.yaml | 5 + Makefile | 1 + boards/cuav/x25-evo/CMakeLists.txt | 35 ++ boards/cuav/x25-evo/bootloader.px4board | 3 + boards/cuav/x25-evo/cmake/upload.cmake | 49 ++ .../cuav/x25-evo/core_heater/CMakeLists.txt | 39 ++ .../cuav/x25-evo/core_heater/core_heater.cpp | 261 ++++++++ boards/cuav/x25-evo/core_heater/core_heater.h | 159 +++++ .../x25-evo/core_heater/core_heater_params.c | 94 +++ boards/cuav/x25-evo/default.px4board | 92 +++ .../extras/cuav_x25-evo_bootloader.bin | Bin 0 -> 47100 bytes boards/cuav/x25-evo/firmware.prototype | 13 + boards/cuav/x25-evo/init/rc.board_defaults | 40 ++ boards/cuav/x25-evo/init/rc.board_sensors | 63 ++ boards/cuav/x25-evo/nuttx-config/Kconfig | 17 + .../x25-evo/nuttx-config/bootloader/defconfig | 95 +++ .../cuav/x25-evo/nuttx-config/include/board.h | 569 ++++++++++++++++++ .../nuttx-config/include/board_dma_map.h | 90 +++ .../cuav/x25-evo/nuttx-config/nsh/defconfig | 332 ++++++++++ .../nuttx-config/scripts/bootloader_script.ld | 215 +++++++ .../x25-evo/nuttx-config/scripts/script.ld | 229 +++++++ .../cuav/x25-evo/pwm_voltage/CMakeLists.txt | 42 ++ boards/cuav/x25-evo/pwm_voltage/parameters.c | 43 ++ .../cuav/x25-evo/pwm_voltage/pwm_voltage.cpp | 56 ++ boards/cuav/x25-evo/src/CMakeLists.txt | 75 +++ boards/cuav/x25-evo/src/board_config.h | 501 +++++++++++++++ boards/cuav/x25-evo/src/bootloader_main.c | 62 ++ boards/cuav/x25-evo/src/can.c | 123 ++++ boards/cuav/x25-evo/src/hw_config.h | 128 ++++ boards/cuav/x25-evo/src/i2c.cpp | 41 ++ boards/cuav/x25-evo/src/init.c | 277 +++++++++ boards/cuav/x25-evo/src/led.c | 236 ++++++++ boards/cuav/x25-evo/src/mtd.cpp | 102 ++++ boards/cuav/x25-evo/src/sdio.c | 177 ++++++ boards/cuav/x25-evo/src/spi.cpp | 58 ++ boards/cuav/x25-evo/src/timer_config.cpp | 90 +++ boards/cuav/x25-evo/src/usb.c | 105 ++++ .../cuav_x25-evo/X25-EVO.jpg | Bin 0 -> 66957 bytes docs/en/SUMMARY.md | 1 + .../autopilot_manufacturer_supported.md | 1 + docs/en/flight_controller/cuav_x25-evo.md | 161 +++++ 41 files changed, 4680 insertions(+) create mode 100644 boards/cuav/x25-evo/CMakeLists.txt create mode 100644 boards/cuav/x25-evo/bootloader.px4board create mode 100644 boards/cuav/x25-evo/cmake/upload.cmake create mode 100644 boards/cuav/x25-evo/core_heater/CMakeLists.txt create mode 100644 boards/cuav/x25-evo/core_heater/core_heater.cpp create mode 100644 boards/cuav/x25-evo/core_heater/core_heater.h create mode 100644 boards/cuav/x25-evo/core_heater/core_heater_params.c create mode 100644 boards/cuav/x25-evo/default.px4board create mode 100755 boards/cuav/x25-evo/extras/cuav_x25-evo_bootloader.bin create mode 100644 boards/cuav/x25-evo/firmware.prototype create mode 100644 boards/cuav/x25-evo/init/rc.board_defaults create mode 100644 boards/cuav/x25-evo/init/rc.board_sensors create mode 100644 boards/cuav/x25-evo/nuttx-config/Kconfig create mode 100644 boards/cuav/x25-evo/nuttx-config/bootloader/defconfig create mode 100644 boards/cuav/x25-evo/nuttx-config/include/board.h create mode 100644 boards/cuav/x25-evo/nuttx-config/include/board_dma_map.h create mode 100644 boards/cuav/x25-evo/nuttx-config/nsh/defconfig create mode 100644 boards/cuav/x25-evo/nuttx-config/scripts/bootloader_script.ld create mode 100644 boards/cuav/x25-evo/nuttx-config/scripts/script.ld create mode 100644 boards/cuav/x25-evo/pwm_voltage/CMakeLists.txt create mode 100644 boards/cuav/x25-evo/pwm_voltage/parameters.c create mode 100644 boards/cuav/x25-evo/pwm_voltage/pwm_voltage.cpp create mode 100644 boards/cuav/x25-evo/src/CMakeLists.txt create mode 100644 boards/cuav/x25-evo/src/board_config.h create mode 100644 boards/cuav/x25-evo/src/bootloader_main.c create mode 100644 boards/cuav/x25-evo/src/can.c create mode 100644 boards/cuav/x25-evo/src/hw_config.h create mode 100644 boards/cuav/x25-evo/src/i2c.cpp create mode 100644 boards/cuav/x25-evo/src/init.c create mode 100644 boards/cuav/x25-evo/src/led.c create mode 100644 boards/cuav/x25-evo/src/mtd.cpp create mode 100644 boards/cuav/x25-evo/src/sdio.c create mode 100644 boards/cuav/x25-evo/src/spi.cpp create mode 100644 boards/cuav/x25-evo/src/timer_config.cpp create mode 100644 boards/cuav/x25-evo/src/usb.c create mode 100644 docs/assets/flight_controller/cuav_x25-evo/X25-EVO.jpg create mode 100644 docs/en/flight_controller/cuav_x25-evo.md diff --git a/.vscode/cmake-variants.yaml b/.vscode/cmake-variants.yaml index a938bde2fa0..c75ef38326d 100644 --- a/.vscode/cmake-variants.yaml +++ b/.vscode/cmake-variants.yaml @@ -276,6 +276,11 @@ CONFIG: buildType: MinSizeRel settings: CONFIG: cuav_7-nano_default + cuav_x25_default: + short: cuav_x25-evo + buildType: MinSizeRel + settings: + CONFIG: cuav_x25-evo_default cubepilot_cubeorange_test: short: cubepilot_cubeorange buildType: MinSizeRel diff --git a/Makefile b/Makefile index 7e45de7b921..ca8392b43f6 100644 --- a/Makefile +++ b/Makefile @@ -328,6 +328,7 @@ bootloaders_update: \ cuav_nora_bootloader \ cuav_x7pro_bootloader \ cuav_7-nano_bootloader \ + cuav_x25-evo_bootloader \ cubepilot_cubeorange_bootloader \ cubepilot_cubeorangeplus_bootloader \ hkust_nxt-dual_bootloader \ diff --git a/boards/cuav/x25-evo/CMakeLists.txt b/boards/cuav/x25-evo/CMakeLists.txt new file mode 100644 index 00000000000..421ff0e988c --- /dev/null +++ b/boards/cuav/x25-evo/CMakeLists.txt @@ -0,0 +1,35 @@ +############################################################################ +# +# Copyright (c) 2025 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. +# +############################################################################ + +add_subdirectory(core_heater) +add_subdirectory(pwm_voltage) diff --git a/boards/cuav/x25-evo/bootloader.px4board b/boards/cuav/x25-evo/bootloader.px4board new file mode 100644 index 00000000000..19b6e662be6 --- /dev/null +++ b/boards/cuav/x25-evo/bootloader.px4board @@ -0,0 +1,3 @@ +CONFIG_BOARD_TOOLCHAIN="arm-none-eabi" +CONFIG_BOARD_ARCHITECTURE="cortex-m7" +CONFIG_BOARD_ROMFSROOT="" diff --git a/boards/cuav/x25-evo/cmake/upload.cmake b/boards/cuav/x25-evo/cmake/upload.cmake new file mode 100644 index 00000000000..71d0a625417 --- /dev/null +++ b/boards/cuav/x25-evo/cmake/upload.cmake @@ -0,0 +1,49 @@ +############################################################################ +# +# Copyright (c) 2025 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. +# +############################################################################ + + +set(PX4_FW_NAME ${PX4_BINARY_DIR}/${PX4_BOARD_VENDOR}_${PX4_BOARD_MODEL}_${PX4_BOARD_LABEL}.px4) + +add_custom_target(upload_skynode_usb + COMMAND ${PX4_SOURCE_DIR}/Tools/auterion/upload_skynode.sh --file=${PX4_FW_NAME} + DEPENDS ${PX4_FW_NAME} + COMMENT "Uploading PX4" + USES_TERMINAL +) + +add_custom_target(upload_skynode_wifi + COMMAND ${PX4_SOURCE_DIR}/Tools/auterion/upload_skynode.sh --file=${PX4_FW_NAME} --wifi + DEPENDS ${PX4_FW_NAME} + COMMENT "Uploading PX4" + USES_TERMINAL +) diff --git a/boards/cuav/x25-evo/core_heater/CMakeLists.txt b/boards/cuav/x25-evo/core_heater/CMakeLists.txt new file mode 100644 index 00000000000..7fdaa6d89ea --- /dev/null +++ b/boards/cuav/x25-evo/core_heater/CMakeLists.txt @@ -0,0 +1,39 @@ +############################################################################ +# +# Copyright (c) 2025 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__core_heater + MAIN core_heater + COMPILE_FLAGS + SRCS + core_heater.cpp + ) diff --git a/boards/cuav/x25-evo/core_heater/core_heater.cpp b/boards/cuav/x25-evo/core_heater/core_heater.cpp new file mode 100644 index 00000000000..0656504cba1 --- /dev/null +++ b/boards/cuav/x25-evo/core_heater/core_heater.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** + * + * Copyright (c) 2025 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 core_heater.cpp + * + */ + +#include "core_heater.h" + +#include +#include +#include +#include + +# ifndef GPIO_CORE_HEATER_OUTPUT +# error "To use the heater driver, the board_config.h must define and initialize GPIO_CORE_HEATER_OUTPUT" +# endif + +Core_Heater::Core_Heater() : + ModuleParams(nullptr), + ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::lp_default) +{ + _heater_status_pub.advertise(); +} + +Core_Heater::~Core_Heater() +{ + disable_core_heater(); +} + +int Core_Heater::custom_command(int argc, char *argv[]) +{ + // Check if the driver is running. + if (!is_running()) { + PX4_INFO("not running"); + return PX4_ERROR; + } + + return print_usage("Unrecognized command."); +} + +void Core_Heater::disable_core_heater() +{ + // Reset heater to off state. + px4_arch_unconfiggpio(GPIO_CORE_HEATER_OUTPUT); +} + +void Core_Heater::initialize_core_heater_io() +{ + // Initialize heater to off state. + px4_arch_configgpio(GPIO_CORE_HEATER_OUTPUT); +} + +void Core_Heater::core_heater_off() +{ + CORE_HEATER_OUTPUT_EN(false); +} + +void Core_Heater::core_heater_on() +{ + CORE_HEATER_OUTPUT_EN(true); +} + +bool Core_Heater::initialize_topics() +{ + for (uint8_t i = 0; i < ORB_MULTI_MAX_INSTANCES; i++) { + uORB::SubscriptionData sensor_accel_sub{ORB_ID(sensor_accel), i}; + + if (sensor_accel_sub.get().timestamp != 0 && + sensor_accel_sub.get().device_id != 0 && + PX4_ISFINITE(sensor_accel_sub.get().temperature)) { + + // If the correct ID is found, exit the for-loop with _sensor_accel_sub pointing to the correct instance. + if (sensor_accel_sub.get().device_id == (uint32_t)_param_core_temp_id.get()) { + _sensor_accel_sub.ChangeInstance(i); + _sensor_device_id = sensor_accel_sub.get().device_id; + initialize_core_heater_io(); + return true; + } + } + } + + return false; +} + +void Core_Heater::Run() +{ + if (should_exit()) { + exit_and_cleanup(); + return; + } + + update_params(); + + if (_sensor_device_id == 0) { + if (!initialize_topics()) { + // if sensor still not found try again in 1 second + ScheduleDelayed(1_s); + return; + } + } + + sensor_accel_s sensor_accel; + float temperature_delta {0.f}; + + if (_core_heater_on) { + // Turn the heater off. + _core_heater_on = false; + core_heater_off(); + ScheduleDelayed(_controller_period_usec - _controller_time_on_usec); + + } else if (_sensor_accel_sub.update(&sensor_accel)) { + // Update the current IMU sensor temperature if valid. + if (PX4_ISFINITE(sensor_accel.temperature)) { + temperature_delta = _param_core_imu_temp.get() - sensor_accel.temperature; + _temperature_last = sensor_accel.temperature; + } + + _proportional_value = temperature_delta * _param_core_imu_temp_p.get(); + _integrator_value += temperature_delta * _param_core_imu_temp_i.get(); + + _integrator_value = math::constrain(_integrator_value, -0.25f, 0.25f); + + _controller_time_on_usec = static_cast((_param_core_imu_temp_ff.get() + _proportional_value + + _integrator_value) * static_cast(_controller_period_usec)); + + _controller_time_on_usec = math::constrain(_controller_time_on_usec, 0, _controller_period_usec); + + if (fabsf(temperature_delta) < TEMPERATURE_TARGET_THRESHOLD) { + _temperature_target_met = true; + + } else { + + _temperature_target_met = false; + } + + _core_heater_on = true; + core_heater_on(); + ScheduleDelayed(_controller_time_on_usec); + } + + publish_status(); +} + +void Core_Heater::publish_status() +{ + heater_status_s status{}; + status.device_id = _sensor_device_id; + status.heater_on = _core_heater_on; + status.temperature_sensor = _temperature_last; + status.temperature_target = _param_core_imu_temp.get(); + status.temperature_target_met = _temperature_target_met; + status.controller_period_usec = _controller_period_usec; + status.controller_time_on_usec = _controller_time_on_usec; + status.proportional_value = _proportional_value; + status.integrator_value = _integrator_value; + status.feed_forward_value = _param_core_imu_temp_ff.get(); + + status.mode = heater_status_s::MODE_GPIO; + + status.timestamp = hrt_absolute_time(); + _heater_status_pub.publish(status); +} + +int Core_Heater::start() +{ + // Exit the driver if the sensor ID does not match the desired sensor. + if (_param_core_temp_id.get() == 0) { + PX4_ERR("Valid CORE_TEMP_ID required"); + request_stop(); + return PX4_ERROR; + } + + update_params(true); + ScheduleNow(); + return PX4_OK; +} + +int Core_Heater::task_spawn(int argc, char *argv[]) +{ + Core_Heater *core_heater = new Core_Heater(); + + if (!core_heater) { + PX4_ERR("driver allocation failed"); + return PX4_ERROR; + } + + _object.store(core_heater); + _task_id = task_id_is_work_queue; + + core_heater->start(); + return 0; +} + +void Core_Heater::update_params(const bool force) +{ + if (_parameter_update_sub.updated() || force) { + // clear update + parameter_update_s param_update; + _parameter_update_sub.copy(¶m_update); + + // update parameters from storage + ModuleParams::updateParams(); + } +} + +int Core_Heater::print_usage(const char *reason) +{ + if (reason) { + printf("%s\n\n", reason); + } + + PRINT_MODULE_DESCRIPTION( + R"DESCR_STR( +### Description +Background process running periodically on the LP work queue to regulate IMU temperature at a setpoint. + +)DESCR_STR"); + + PRINT_MODULE_USAGE_NAME("core_heater", "system"); + PRINT_MODULE_USAGE_COMMAND("start"); + PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); + + return 0; +} + +extern "C" __EXPORT int core_heater_main(int argc, char *argv[]) +{ + return Core_Heater::main(argc, argv); +} diff --git a/boards/cuav/x25-evo/core_heater/core_heater.h b/boards/cuav/x25-evo/core_heater/core_heater.h new file mode 100644 index 00000000000..6ac34ad53a0 --- /dev/null +++ b/boards/cuav/x25-evo/core_heater/core_heater.h @@ -0,0 +1,159 @@ +/**************************************************************************** + * + * Copyright (c) 2025 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 core_heater.h + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace time_literals; + +#define CONTROLLER_PERIOD_DEFAULT 10000 +#define TEMPERATURE_TARGET_THRESHOLD 2.5f + +class Core_Heater : public ModuleBase, public ModuleParams, public px4::ScheduledWorkItem +{ +public: + Core_Heater(); + + virtual ~Core_Heater(); + + /** + * @see ModuleBase::custom_command(). + * @brief main Main entry point to the module that should be + * called directly from the module's main method. + * @param argc The input argument count. + * @param argv Pointer to the input argument array. + * @return Returns 0 iff successful, -1 otherwise. + */ + static int custom_command(int argc, char *argv[]); + + /** + * @see ModuleBase::print_usage(). + * @brief Prints the module usage to the nuttshell console. + * @param reason The requested reason for printing to console. + */ + static int print_usage(const char *reason = nullptr); + + /** + * @see ModuleBase::task_spawn(). + * @brief Initializes the class in the same context as the work queue + * and starts the background listener. + * @param argv Pointer to the input argument array. + * @return Returns 0 iff successful, -1 otherwise. + */ + static int task_spawn(int argc, char *argv[]); + + /** + * @brief Initiates the heater driver work queue, starts a new background task, + * and fails if it is already running. + * @return Returns 1 iff start was successful. + */ + int start(); + +private: + + /** Disables the heater (either by GPIO). */ + void disable_core_heater(); + + /** Turns the heater on (either by GPIO). */ + void core_heater_on(); + + /** Turns the heater off (either by GPIO). */ + void core_heater_off(); + + void initialize(); + + /** Enables / configures the heater (either by GPIO). */ + void initialize_core_heater_io(); + + /** @brief Called once to initialize uORB topics. */ + bool initialize_topics(); + + void publish_status(); + + /** @brief Calculates the heater element on/off time and schedules the next cycle. */ + void Run() override; + + /** + * @brief Updates and checks for updated uORB parameters. + * @param force Boolean to determine if an update check should be forced. + */ + void update_params(const bool force = false); + + /** Work queue struct for the scheduler. */ + static struct work_s _work; + + bool _core_heater_initialized = false; + bool _core_heater_on = false; + bool _temperature_target_met = false; + + int _controller_period_usec = CONTROLLER_PERIOD_DEFAULT; + int _controller_time_on_usec = 0; + + float _integrator_value = 0.0f; + float _proportional_value = 0.0f; + + uORB::Publication _heater_status_pub{ORB_ID(heater_status)}; + + uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s}; + + uORB::Subscription _sensor_accel_sub{ORB_ID(sensor_accel)}; + + uint32_t _sensor_device_id{0}; + + float _temperature_last{NAN}; + + DEFINE_PARAMETERS( + (ParamFloat) _param_core_imu_temp_ff, + (ParamFloat) _param_core_imu_temp_i, + (ParamFloat) _param_core_imu_temp_p, + (ParamFloat) _param_core_imu_temp, + (ParamInt) _param_core_temp_id + ) +}; diff --git a/boards/cuav/x25-evo/core_heater/core_heater_params.c b/boards/cuav/x25-evo/core_heater/core_heater_params.c new file mode 100644 index 00000000000..a19eb6d0161 --- /dev/null +++ b/boards/cuav/x25-evo/core_heater/core_heater_params.c @@ -0,0 +1,94 @@ +/**************************************************************************** + * + * Copyright (c) 2025 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 core_heater_params.c + * Core Heater parameters. + * + */ + +/** + * Target IMU device ID to regulate temperature. + * + * @category system + * @group Sensors + */ +PARAM_DEFINE_INT32(CORE_TEMP_ID, 0); + +/** + * Target IMU temperature. + * + * @category system + * @group Sensors + * @unit celcius + * @min 0 + * @max 85.0 + * @decimal 3 + */ +PARAM_DEFINE_FLOAT(CORE_IMU_TEMP, 55.0f); + +/** + * IMU heater controller feedforward value. + * + * @category system + * @group Sensors + * @unit % + * @min 0 + * @max 1.0 + * @decimal 3 + */ +PARAM_DEFINE_FLOAT(CORE_IMU_TEMP_FF, 0.05f); + +/** + * IMU heater controller integrator gain value. + * + * @category system + * @group Sensors + * @unit us/C + * @min 0 + * @max 1.0 + * @decimal 3 + */ +PARAM_DEFINE_FLOAT(CORE_IMU_TEMP_I, 0.025f); + +/** + * IMU heater controller proportional gain value. + * + * @category system + * @group Sensors + * @unit us/C + * @min 0 + * @max 2.0 + * @decimal 3 + */ +PARAM_DEFINE_FLOAT(CORE_IMU_TEMP_P, 1.0f); diff --git a/boards/cuav/x25-evo/default.px4board b/boards/cuav/x25-evo/default.px4board new file mode 100644 index 00000000000..a5cd2829cad --- /dev/null +++ b/boards/cuav/x25-evo/default.px4board @@ -0,0 +1,92 @@ +CONFIG_BOARD_TOOLCHAIN="arm-none-eabi" +CONFIG_BOARD_ARCHITECTURE="cortex-m7" +CONFIG_BOARD_ETHERNET=y +CONFIG_BOARD_SERIAL_GPS1="/dev/ttyS0" +CONFIG_BOARD_SERIAL_GPS2="/dev/ttyS1" +CONFIG_BOARD_SERIAL_TEL1="/dev/ttyS6" +CONFIG_BOARD_SERIAL_TEL2="/dev/ttyS4" +CONFIG_BOARD_SERIAL_EXT2="/dev/ttyS3" +CONFIG_DRIVERS_ADC_ADS1115=y +CONFIG_DRIVERS_ADC_BOARD_ADC=y +CONFIG_DRIVERS_BAROMETER_BMP581=y +CONFIG_DRIVERS_BAROMETER_INVENSENSE_ICP201XX=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_HEATER=y +CONFIG_DRIVERS_IMU_INVENSENSE_ICM20948=y +CONFIG_DRIVERS_IMU_INVENSENSE_IIM42652=y +CONFIG_DRIVERS_IMU_INVENSENSE_IIM42653=y +CONFIG_COMMON_INS=y +CONFIG_COMMON_LIGHT=y +CONFIG_COMMON_MAGNETOMETER=y +CONFIG_DRIVERS_OSD_MSP_OSD=y +CONFIG_DRIVERS_POWER_MONITOR_INA226=y +CONFIG_DRIVERS_POWER_MONITOR_INA228=y +CONFIG_DRIVERS_POWER_MONITOR_INA238=y +CONFIG_DRIVERS_POWER_MONITOR_PM_SELECTOR_AUTERION=y +CONFIG_DRIVERS_PWM_OUT=y +CONFIG_DRIVERS_RC_INPUT=y +CONFIG_DRIVERS_SAFETY_BUTTON=y +CONFIG_DRIVERS_TONE_ALARM=y +CONFIG_DRIVERS_UAVCAN=y +CONFIG_BOARD_UAVCAN_TIMER_OVERRIDE=2 +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_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_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_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_NETMAN=y +CONFIG_SYSTEMCMDS_NSHTERM=y +CONFIG_SYSTEMCMDS_PARAM=y +CONFIG_SYSTEMCMDS_PERF=y +CONFIG_SYSTEMCMDS_REBOOT=y +CONFIG_SYSTEMCMDS_SYSTEM_TIME=y +CONFIG_SYSTEMCMDS_TOP=y +CONFIG_SYSTEMCMDS_TOPIC_LISTENER=y +CONFIG_SYSTEMCMDS_TUNE_CONTROL=y +CONFIG_SYSTEMCMDS_UORB=y +CONFIG_SYSTEMCMDS_VER=y +CONFIG_SYSTEMCMDS_WORK_QUEUE=y diff --git a/boards/cuav/x25-evo/extras/cuav_x25-evo_bootloader.bin b/boards/cuav/x25-evo/extras/cuav_x25-evo_bootloader.bin new file mode 100755 index 0000000000000000000000000000000000000000..f6b0adcacccc0cae5439b66e1713ef3d5778f267 GIT binary patch literal 47100 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 BZf*bo literal 0 HcmV?d00001 diff --git a/boards/cuav/x25-evo/firmware.prototype b/boards/cuav/x25-evo/firmware.prototype new file mode 100644 index 00000000000..98e18e27be2 --- /dev/null +++ b/boards/cuav/x25-evo/firmware.prototype @@ -0,0 +1,13 @@ +{ + "board_id": 7002, + "magic": "PX4FWv1", + "description": "Firmware for the CUAVX25EVO board", + "image": "", + "build_time": 0, + "summary": "CUAVX25EVO", + "version": "0.1", + "image_size": 0, + "image_maxsize": 1966080, + "git_identity": "", + "board_revision": 0 +} diff --git a/boards/cuav/x25-evo/init/rc.board_defaults b/boards/cuav/x25-evo/init/rc.board_defaults new file mode 100644 index 00000000000..8ef1ad0b404 --- /dev/null +++ b/boards/cuav/x25-evo/init/rc.board_defaults @@ -0,0 +1,40 @@ +#!/bin/sh +# +# board specific defaults +#------------------------------------------------------------------------------ + +Mavlink ethernet (CFG 1000) +param set-default MAV_2_CONFIG 1000 +param set-default MAV_2_BROADCAST 1 +param set-default MAV_2_MODE 0 +param set-default MAV_2_RADIO_CTL 0 +param set-default MAV_2_RATE 100000 +param set-default MAV_2_REMOTE_PRT 14550 +param set-default MAV_2_UDP_PRT 14550 + +param set-default BAT1_V_DIV 18 +param set-default BAT1_A_PER_V 24 + +param set-default SENS_EN_INA238 0 +param set-default SENS_EN_INA228 0 +param set-default SENS_EN_INA226 0 + +param set-default USB_MAV_MODE 5 + +param set-default UAVCAN_SUB_GPS 1 +param set-default UAVCAN_SUB_BAT 1 + +# Enable IMU thermal control +param set-default SENS_EN_THERMAL 1 +param set-default SENS_IMU_TEMP 45 +param set-default SENS_TEMP_ID 2818058 + +# CUAV core board IMU thermal control +param set-default CORE_IMU_TEMP 45 +param set-default CORE_TEMP_ID 3014698 +core_heater start + +# CUAV pwm voltage 3.3V/5V switch +pwm_voltage_apply start + +safety_button start diff --git a/boards/cuav/x25-evo/init/rc.board_sensors b/boards/cuav/x25-evo/init/rc.board_sensors new file mode 100644 index 00000000000..cc42ea6f01c --- /dev/null +++ b/boards/cuav/x25-evo/init/rc.board_sensors @@ -0,0 +1,63 @@ +#!/bin/sh +# +# CUAV X25-EVO specific board sensors init +#------------------------------------------------------------------------------ +set HAVE_PM2 yes + +if mft query -q -k MFT -s MFT_PM2 -v 0 +then + set HAVE_PM2 no +fi + +if param compare -s ADC_ADS1115_EN 1 +then + ads1115 start -X + board_adc start -n +else + board_adc start +fi + +if param compare SENS_EN_INA226 1 +then + # Start Digital power monitors + ina226 -X -b 1 -t 1 -k start + + if [ $HAVE_PM2 = yes ] + then + ina226 -X -b 2 -t 2 -k start + fi +fi + +if param compare SENS_EN_INA228 1 +then + # Start Digital power monitors + ina228 -X -b 1 -t 1 -k start + if [ $HAVE_PM2 = yes ] + then + ina228 -X -b 2 -t 2 -k start + fi +fi + +if param compare SENS_EN_INA238 1 +then + # Start Digital power monitors + ina238 -X -b 1 -t 1 -k start + if [ $HAVE_PM2 = yes ] + then + ina238 -X -b 2 -t 2 -k start + fi +fi + +iim42652 -s -b 1 -R 2 -C 32768 start +iim42653 -s -b 2 -R 27 start +iim42653 -s -b 5 -R 12 start + +rm3100 -s -b 4 start + +bmp581 -s -b 4 start +icp201xx -I -b 4 start + +# External compass on GPS1/I2C1 (the 3rd external bus): standard Holybro Pixhawk 4 or CUAV V5 GPS/compass puck (with lights, safety button, and buzzer) +ist8310 -X -b 1 -R 10 start + +unset HAVE_PM2 diff --git a/boards/cuav/x25-evo/nuttx-config/Kconfig b/boards/cuav/x25-evo/nuttx-config/Kconfig new file mode 100644 index 00000000000..bb33d3cfda4 --- /dev/null +++ b/boards/cuav/x25-evo/nuttx-config/Kconfig @@ -0,0 +1,17 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# +config BOARD_HAS_PROBES + bool "Board provides GPIO or other Hardware for signaling to timing analyze." + default y + ---help--- + This board provides GPIO FMU-CH1-5, CAP1-6 as PROBE_1-11 to provide timing signals from selected drivers. + +config BOARD_USE_PROBES + bool "Enable the use the board provided FMU-CH1-5, CAP1-6 as PROBE_1-11" + default n + depends on BOARD_HAS_PROBES + + ---help--- + Select to use GPIO FMU-CH1-5, CAP1-6 to provide timing signals from selected drivers. diff --git a/boards/cuav/x25-evo/nuttx-config/bootloader/defconfig b/boards/cuav/x25-evo/nuttx-config/bootloader/defconfig new file mode 100644 index 00000000000..e2527d322b7 --- /dev/null +++ b/boards/cuav/x25-evo/nuttx-config/bootloader/defconfig @@ -0,0 +1,95 @@ +# +# 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_DISABLE_PTHREAD 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/cuav/x25-evo/nuttx-config" +CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y +CONFIG_ARCH_BOARD_CUSTOM_NAME="px4" +CONFIG_ARCH_CHIP="stm32h7" +CONFIG_ARCH_CHIP_STM32H743XI=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=0x004c +CONFIG_CDCACM_PRODUCTSTR="PX4 BL CUAV X25-EVO" +CONFIG_CDCACM_RXBUFSIZE=600 +CONFIG_CDCACM_TXBUFSIZE=12000 +CONFIG_CDCACM_VENDORID=0x3163 +CONFIG_CDCACM_VENDORSTR="CUAV" +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_MUTEX_ROBUST=y +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_DMA2=y +CONFIG_STM32H7_OTGFS=y +CONFIG_STM32H7_PROGMEM=y +CONFIG_STM32H7_SERIAL_DISABLE_REORDERING=y +CONFIG_STM32H7_TIM1=y +CONFIG_STM32H7_UART5=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_UART5_RXBUFSIZE=512 +CONFIG_UART5_RXDMA=y +CONFIG_UART5_TXBUFSIZE=512 +CONFIG_UART5_TXDMA=y +CONFIG_USBDEV=y +CONFIG_USBDEV_BUSPOWERED=y +CONFIG_USBDEV_MAXPOWER=500 +CONFIG_USEC_PER_TICK=1000 diff --git a/boards/cuav/x25-evo/nuttx-config/include/board.h b/boards/cuav/x25-evo/nuttx-config/include/board.h new file mode 100644 index 00000000000..4c544f60863 --- /dev/null +++ b/boards/cuav/x25-evo/nuttx-config/include/board.h @@ -0,0 +1,569 @@ +/************************************************************************************ + * nuttx-configs/cuav_x25-evo/include/board.h + * + * Copyright (C) 2016-2025 Gregory Nutt. All rights reserved. + * Authors: David Sidrane + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ +#ifndef __NUTTX_CONFIG_CUAV_X25_EVO_INCLUDE_BOARD_H +#define __NUTTX_CONFIG_CUAV_X25_EVO_INCLUDE_BOARD_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include "board_dma_map.h" + +#include + +#ifndef __ASSEMBLY__ +# include +#endif + +#include "stm32_rcc.h" +#include "stm32_sdmmc.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Clocking *************************************************************************/ +/* The CUAV X25-EVO board provides the following clock sources: + * + * X1: 16 MHz crystal for HSE + * + * So we have these clock source available within the STM32 + * + * HSI: 16 MHz RC factory-trimmed + * HSE: 16 MHz crystal for HSE + */ + +#define STM32_BOARD_XTAL 16000000ul + +#define STM32_HSI_FREQUENCY 16000000ul +#define STM32_LSI_FREQUENCY 32000 +#define STM32_HSE_FREQUENCY STM32_BOARD_XTAL +#define STM32_LSE_FREQUENCY 32768 + +/* Main PLL Configuration. + * + * PLL source is HSE = 16,000,000 + * + * PLL_VCOx = (STM32_HSE_FREQUENCY / PLLM) * PLLN + * Subject to: + * + * 1 <= PLLM <= 63 + * 4 <= PLLN <= 512 + * 150 MHz <= PLL_VCOL <= 420MHz + * 192 MHz <= PLL_VCOH <= 836MHz + * + * SYSCLK = PLL_VCO / PLLP + * CPUCLK = SYSCLK / D1CPRE + * Subject to + * + * PLLP1 = {2, 4, 6, 8, ..., 128} + * PLLP2,3 = {2, 3, 4, ..., 128} + * CPUCLK <= 480 MHz + */ + +// #define STM32_BOARD_USEHSE + +#define STM32_PLLCFG_PLLSRC RCC_PLLCKSELR_PLLSRC_HSE + +/* PLL1, wide 4 - 8 MHz input, enable DIVP, DIVQ, DIVR + * + * PLL1_VCO = (16,000,000 / 1) * 60 = 960 MHz + * + * PLL1P = PLL1_VCO/2 = 960 MHz / 2 = 480 MHz + * PLL1Q = PLL1_VCO/4 = 960 MHz / 4 = 240 MHz + * PLL1R = PLL1_VCO/8 = 960 MHz / 8 = 120 MHz + */ + +#define STM32_PLLCFG_PLL1CFG (RCC_PLLCFGR_PLL1VCOSEL_WIDE | \ + RCC_PLLCFGR_PLL1RGE_4_8_MHZ | \ + RCC_PLLCFGR_DIVP1EN | \ + RCC_PLLCFGR_DIVQ1EN | \ + RCC_PLLCFGR_DIVR1EN) +#define STM32_PLLCFG_PLL1M RCC_PLLCKSELR_DIVM1(1) +#define STM32_PLLCFG_PLL1N RCC_PLL1DIVR_N1(60) +#define STM32_PLLCFG_PLL1P RCC_PLL1DIVR_P1(2) +#define STM32_PLLCFG_PLL1Q RCC_PLL1DIVR_Q1(4) +#define STM32_PLLCFG_PLL1R RCC_PLL1DIVR_R1(8) + +#define STM32_VCO1_FREQUENCY ((STM32_HSE_FREQUENCY / 1) * 60) +#define STM32_PLL1P_FREQUENCY (STM32_VCO1_FREQUENCY / 2) +#define STM32_PLL1Q_FREQUENCY (STM32_VCO1_FREQUENCY / 4) +#define STM32_PLL1R_FREQUENCY (STM32_VCO1_FREQUENCY / 8) + +/* PLL2 */ + +#define STM32_PLLCFG_PLL2CFG (RCC_PLLCFGR_PLL2VCOSEL_WIDE | \ + RCC_PLLCFGR_PLL2RGE_4_8_MHZ | \ + RCC_PLLCFGR_DIVP2EN | \ + RCC_PLLCFGR_DIVQ2EN | \ + RCC_PLLCFGR_DIVR2EN) +#define STM32_PLLCFG_PLL2M RCC_PLLCKSELR_DIVM2(4) +#define STM32_PLLCFG_PLL2N RCC_PLL2DIVR_N2(48) +#define STM32_PLLCFG_PLL2P RCC_PLL2DIVR_P2(2) +#define STM32_PLLCFG_PLL2Q RCC_PLL2DIVR_Q2(2) +#define STM32_PLLCFG_PLL2R RCC_PLL2DIVR_R2(2) + +#define STM32_VCO2_FREQUENCY ((STM32_HSE_FREQUENCY / 4) * 48) +#define STM32_PLL2P_FREQUENCY (STM32_VCO2_FREQUENCY / 2) +#define STM32_PLL2Q_FREQUENCY (STM32_VCO2_FREQUENCY / 2) +#define STM32_PLL2R_FREQUENCY (STM32_VCO2_FREQUENCY / 2) + +/* PLL3 */ + +#define STM32_PLLCFG_PLL3CFG (RCC_PLLCFGR_PLL3VCOSEL_WIDE | \ + RCC_PLLCFGR_PLL3RGE_4_8_MHZ | \ + RCC_PLLCFGR_DIVQ3EN) +#define STM32_PLLCFG_PLL3M RCC_PLLCKSELR_DIVM3(4) +#define STM32_PLLCFG_PLL3N RCC_PLL3DIVR_N3(48) +#define STM32_PLLCFG_PLL3P RCC_PLL3DIVR_P3(2) +#define STM32_PLLCFG_PLL3Q RCC_PLL3DIVR_Q3(4) +#define STM32_PLLCFG_PLL3R RCC_PLL3DIVR_R3(2) + +#define STM32_VCO3_FREQUENCY ((STM32_HSE_FREQUENCY / 4) * 48) +#define STM32_PLL3P_FREQUENCY (STM32_VCO3_FREQUENCY / 2) +#define STM32_PLL3Q_FREQUENCY (STM32_VCO3_FREQUENCY / 4) +#define STM32_PLL3R_FREQUENCY (STM32_VCO3_FREQUENCY / 2) + +/* SYSCLK = PLL1P = 480MHz + * CPUCLK = SYSCLK / 1 = 480 MHz + */ + +#define STM32_RCC_D1CFGR_D1CPRE (RCC_D1CFGR_D1CPRE_SYSCLK) +#define STM32_SYSCLK_FREQUENCY (STM32_PLL1P_FREQUENCY) +#define STM32_CPUCLK_FREQUENCY (STM32_SYSCLK_FREQUENCY / 1) + +/* Configure Clock Assignments */ + +/* AHB clock (HCLK) is SYSCLK/2 (240 MHz max) + * HCLK1 = HCLK2 = HCLK3 = HCLK4 = 240 + */ + +#define STM32_RCC_D1CFGR_HPRE RCC_D1CFGR_HPRE_SYSCLKd2 /* HCLK = SYSCLK / 2 */ +#define STM32_ACLK_FREQUENCY (STM32_CPUCLK_FREQUENCY / 2) /* ACLK in D1, HCLK3 in D1 */ +#define STM32_HCLK_FREQUENCY (STM32_CPUCLK_FREQUENCY / 2) /* HCLK in D2, HCLK4 in D3 */ +#define STM32_BOARD_HCLK STM32_HCLK_FREQUENCY /* same as above, to satisfy compiler */ + +/* APB1 clock (PCLK1) is HCLK/2 (120 MHz) */ + +#define STM32_RCC_D2CFGR_D2PPRE1 RCC_D2CFGR_D2PPRE1_HCLKd2 /* PCLK1 = HCLK / 2 */ +#define STM32_PCLK1_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* APB2 clock (PCLK2) is HCLK/2 (120 MHz) */ + +#define STM32_RCC_D2CFGR_D2PPRE2 RCC_D2CFGR_D2PPRE2_HCLKd2 /* PCLK2 = HCLK / 2 */ +#define STM32_PCLK2_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* APB3 clock (PCLK3) is HCLK/2 (120 MHz) */ + +#define STM32_RCC_D1CFGR_D1PPRE RCC_D1CFGR_D1PPRE_HCLKd2 /* PCLK3 = HCLK / 2 */ +#define STM32_PCLK3_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* APB4 clock (PCLK4) is HCLK/4 (120 MHz) */ + +#define STM32_RCC_D3CFGR_D3PPRE RCC_D3CFGR_D3PPRE_HCLKd2 /* PCLK4 = HCLK / 2 */ +#define STM32_PCLK4_FREQUENCY (STM32_HCLK_FREQUENCY/2) + +/* Timer clock frequencies */ + +/* Timers driven from APB1 will be twice PCLK1 */ + +#define STM32_APB1_TIM2_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM3_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM4_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM5_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM6_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM7_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM12_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM13_CLKIN (2*STM32_PCLK1_FREQUENCY) +#define STM32_APB1_TIM14_CLKIN (2*STM32_PCLK1_FREQUENCY) + +/* Timers driven from APB2 will be twice PCLK2 */ + +#define STM32_APB2_TIM1_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM8_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM15_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM16_CLKIN (2*STM32_PCLK2_FREQUENCY) +#define STM32_APB2_TIM17_CLKIN (2*STM32_PCLK2_FREQUENCY) + +/* Kernel Clock Configuration + * + * Note: look at Table 54 in ST Manual + */ + +/* I2C123 clock source */ + +#define STM32_RCC_D2CCIP2R_I2C123SRC RCC_D2CCIP2R_I2C123SEL_HSI + +/* I2C4 clock source */ + +#define STM32_RCC_D3CCIPR_I2C4SRC RCC_D3CCIPR_I2C4SEL_HSI + +/* SPI123 clock source */ + +#define STM32_RCC_D2CCIP1R_SPI123SRC RCC_D2CCIP1R_SPI123SEL_PLL2 + +/* SPI45 clock source */ + +#define STM32_RCC_D2CCIP1R_SPI45SRC RCC_D2CCIP1R_SPI45SEL_PLL2 + +/* SPI6 clock source */ + +#define STM32_RCC_D3CCIPR_SPI6SRC RCC_D3CCIPR_SPI6SEL_PLL2 + +/* USB 1 and 2 clock source */ + +#define STM32_RCC_D2CCIP2R_USBSRC RCC_D2CCIP2R_USBSEL_PLL3 + +/* ADC 1 2 3 clock source */ + +#define STM32_RCC_D3CCIPR_ADCSRC RCC_D3CCIPR_ADCSEL_PLL2 + +/* 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 + +/* FDCAN 1 2 clock source */ + +#define STM32_RCC_D2CCIP1R_FDCANSEL RCC_D2CCIP1R_FDCANSEL_HSE /* FDCAN 1 2 clock source */ + +#define STM32_FDCANCLK STM32_HSE_FREQUENCY + +/* FLASH wait states + * + * ------------ ---------- ----------- + * Vcore MAX ACLK WAIT STATES + * ------------ ---------- ----------- + * 1.15-1.26 V 70 MHz 0 + * (VOS1 level) 140 MHz 1 + * 210 MHz 2 + * 1.05-1.15 V 55 MHz 0 + * (VOS2 level) 110 MHz 1 + * 165 MHz 2 + * 220 MHz 3 + * 0.95-1.05 V 45 MHz 0 + * (VOS3 level) 90 MHz 1 + * 135 MHz 2 + * 180 MHz 3 + * 225 MHz 4 + * ------------ ---------- ----------- + */ + +#define BOARD_FLASH_WAITSTATES 2 + +/* SDMMC definitions ********************************************************/ + +/* Init 400kHz, freq = PLL1Q/(2*div) div = PLL1Q/(2*freq) */ + +#define STM32_SDMMC_INIT_CLKDIV (300 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) + +/* 25 MHz Max for now, 25 mHZ = PLL1Q/(2*div), div = PLL1Q/(2*freq) + * div = 4.8 = 240 / 50, So round up to 5 for default speed 24 MB/s + */ + +#if defined(CONFIG_STM32H7_SDMMC_XDMA) || defined(CONFIG_STM32H7_SDMMC_IDMA) +# define STM32_SDMMC_MMCXFR_CLKDIV (5 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#else +# define STM32_SDMMC_MMCXFR_CLKDIV (100 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#endif +#if defined(CONFIG_STM32H7_SDMMC_XDMA) || defined(CONFIG_STM32H7_SDMMC_IDMA) +# define STM32_SDMMC_SDXFR_CLKDIV (5 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#else +# define STM32_SDMMC_SDXFR_CLKDIV (100 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#endif + +#define STM32_SDMMC_CLKCR_EDGE STM32_SDMMC_CLKCR_NEGEDGE + +/* LED definitions ******************************************************************/ +/* The PX4 FMUV6X board has three, LED_GREEN a Green LED, LED_BLUE a Blue LED and + * LED_RED a Red LED, that can be controlled by software. + * + * If CONFIG_ARCH_LEDS is not defined, then the user can control the LEDs in any way. + * The following definitions are used to access individual LEDs. + */ + +/* LED index values for use with board_userled() */ + +/* LED definitions ******************************************************************/ +/* The px4_fmu-v6x board has three, LED_GREEN a Green LED, LED_BLUE a Blue LED and + * LED_RED a Red LED, that can be controlled by software. + * + * If CONFIG_ARCH_LEDS is not defined, then the user can control the LEDs in any way. + * The following definitions are used to access individual LEDs. + */ + +/* LED index values for use with board_userled() */ + +#define BOARD_LED1 0 +#define BOARD_LED2 1 +#define BOARD_LED3 2 +#define BOARD_NLEDS 3 + +#define BOARD_LED_RED BOARD_LED1 +#define BOARD_LED_GREEN BOARD_LED2 +#define BOARD_LED_BLUE BOARD_LED3 + +/* LED bits for use with board_userled_all() */ + +#define BOARD_LED1_BIT (1 << BOARD_LED1) +#define BOARD_LED2_BIT (1 << BOARD_LED2) +#define BOARD_LED3_BIT (1 << BOARD_LED3) + +/* If CONFIG_ARCH_LEDS is defined, the usage by the board port is defined in + * include/board.h and src/stm32_leds.c. The LEDs are used to encode OS-related + * events as follows: + * + * + * SYMBOL Meaning LED state + * Red Green Blue + * ---------------------- -------------------------- ------ ------ ----*/ + +#define LED_STARTED 0 /* NuttX has been started OFF OFF OFF */ +#define LED_HEAPALLOCATE 1 /* Heap has been allocated OFF OFF ON */ +#define LED_IRQSENABLED 2 /* Interrupts enabled OFF ON OFF */ +#define LED_STACKCREATED 3 /* Idle stack created OFF ON ON */ +#define LED_INIRQ 4 /* In an interrupt N/C N/C GLOW */ +#define LED_SIGNAL 5 /* In a signal handler N/C GLOW N/C */ +#define LED_ASSERTION 6 /* An assertion failed GLOW N/C GLOW */ +#define LED_PANIC 7 /* The system has crashed Blink OFF N/C */ +#define LED_IDLE 8 /* MCU is is sleep mode ON OFF OFF */ + +/* Thus if the Green LED is statically on, NuttX has successfully booted and + * is, apparently, running normally. If the Red LED is flashing at + * approximately 2Hz, then a fatal error has been detected and the system + * has halted. + */ + +/* Alternate function pin selections ************************************************/ + +#define GPIO_USART1_RX GPIO_USART1_RX_3 /* PB7 */ +#define GPIO_USART1_TX GPIO_USART1_TX_3 /* PB6 */ + +#define GPIO_USART2_RX GPIO_USART2_RX_1 /* PA3 */ +#define GPIO_USART2_TX GPIO_USART2_TX_2 /* PD5 */ +#define GPIO_USART2_RTS GPIO_USART2_RTS_2 /* PD4 */ +#define GPIO_USART2_CTS GPIO_USART2_CTS_NSS_2 /* PD3 */ + +#define GPIO_USART3_RX GPIO_USART3_RX_3 /* PD9 */ +#define GPIO_USART3_TX GPIO_USART3_TX_3 /* PD8 */ + +#define GPIO_UART4_RX GPIO_UART4_RX_6 /* PH14 */ +#define GPIO_UART4_TX GPIO_UART4_TX_6 /* PH13 */ + +#define GPIO_UART5_RX GPIO_UART5_RX_3 /* PD2 */ +#define GPIO_UART5_TX GPIO_UART5_TX_3 /* PC12 */ +// GPIO_UART5_RTS no remap /* PC8 */ +#undef GPIO_UART5_CTS +#define GPIO_UART5_CTS ((GPIO_ALT|GPIO_AF8|GPIO_PORTC|GPIO_PIN9) | GPIO_PULLDOWN) + +#define GPIO_USART6_RX GPIO_USART6_RX_1 /* PC7 */ +#define GPIO_USART6_TX GPIO_USART6_TX_1 /* PC6 */ + +#define GPIO_UART7_RX GPIO_UART7_RX_4 /* PF6 */ +#define GPIO_UART7_TX GPIO_UART7_TX_3 /* PE8 */ +#define GPIO_UART7_RTS GPIO_UART7_RTS_2 /* PF8 */ +#define GPIO_UART7_CTS (GPIO_UART7_CTS_1 | GPIO_PULLDOWN) /* PE10 */ + +#define GPIO_UART8_RX GPIO_UART8_RX_1 /* PE0 */ +#define GPIO_UART8_TX GPIO_UART8_TX_1 /* PE1 */ + + +/* CAN + * + * CAN1 is routed to transceiver. + * CAN2 is routed to transceiver. + */ +#define GPIO_CAN1_RX GPIO_CAN1_RX_3 /* PD0 */ +#define GPIO_CAN1_TX GPIO_CAN1_TX_3 /* PD1 */ +#define GPIO_CAN2_RX GPIO_CAN2_RX_1 /* PB12 */ +#define GPIO_CAN2_TX GPIO_CAN2_TX_1 /* PB13 */ + +/* SPI + * SPI1 is sensors1 + * SPI2 is sensors2 + * SPI3 is sensors3 + * SPI4 is Not Used + * SPI5 is FRAM + * SPI6 is EXTERNAL1 + * + */ + +#define ADJ_SLEW_RATE(p) (((p) & ~GPIO_SPEED_MASK) | (GPIO_SPEED_50MHz)) + +#define GPIO_SPI1_MISO ADJ_SLEW_RATE(GPIO_SPI1_MISO_3) /* PG9 */ +#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_2 /* PB5 */ +#define GPIO_SPI1_SCK GPIO_SPI1_SCK_1 /* PA5 */ + +#define GPIO_SPI2_MISO ADJ_SLEW_RATE(GPIO_SPI2_MISO_3) /* PI2 */ +#define GPIO_SPI2_MOSI GPIO_SPI2_MOSI_4 /* PI3 */ +#define GPIO_SPI2_SCK GPIO_SPI2_SCK_6 /* PI1 */ + +// #define GPIO_SPI3_MISO (GPIO_SPI3_MISO_2) /* PC11 */ +// #define GPIO_SPI3_MOSI (GPIO_SPI3_MOSI_3) /* PB2 */ +// #define GPIO_SPI3_SCK (GPIO_SPI3_SCK_2) /* PC10 */ + +#define GPIO_SPI4_MISO ADJ_SLEW_RATE(GPIO_SPI4_MISO_1) /* PE13 */ +#define GPIO_SPI4_MOSI GPIO_SPI4_MOSI_1 /* PE14 */ +#define GPIO_SPI4_SCK GPIO_SPI4_SCK_1 /* PE12 */ + +#define GPIO_SPI5_MISO ADJ_SLEW_RATE(GPIO_SPI5_MISO_2) /* PH7 */ +#define GPIO_SPI5_MOSI GPIO_SPI5_MOSI_1 /* PF11 */ +#define GPIO_SPI5_SCK GPIO_SPI5_SCK_1 /* PF7 */ + +#define GPIO_SPI6_MISO ADJ_SLEW_RATE(GPIO_SPI6_MISO_2) /* PA6 */ +#define GPIO_SPI6_MOSI GPIO_SPI6_MOSI_1 /* PG14 */ +#define GPIO_SPI6_SCK GPIO_SPI6_SCK_3 /* PB3 */ + +/* I2C + * + * The optional _GPIO configurations allow the I2C driver to manually + * reset the bus to clear stuck slaves. They match the pin configuration, + * but are normally-high GPIOs. + * + */ + +#define GPIO_I2C1_SCL GPIO_I2C1_SCL_2 /* PB8 */ +#define GPIO_I2C1_SDA GPIO_I2C1_SDA_2 /* PB9 */ + +#define GPIO_I2C1_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTB | GPIO_PIN8) +#define GPIO_I2C1_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTB | GPIO_PIN9) + +#define GPIO_I2C2_SCL GPIO_I2C2_SCL_2 /* PF1 */ +#define GPIO_I2C2_SDA GPIO_I2C2_SDA_2 /* PF0 */ + +#define GPIO_I2C2_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN1) +#define GPIO_I2C2_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN0) + +#define GPIO_I2C3_SCL GPIO_I2C3_SCL_1 /* PA8 */ +#define GPIO_I2C3_SDA GPIO_I2C3_SDA_2 /* PH8 */ + +#define GPIO_I2C3_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTA | GPIO_PIN8) +#define GPIO_I2C3_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTH | GPIO_PIN8) + +#define GPIO_I2C4_SCL GPIO_I2C4_SCL_2 /* PF14 */ +#define GPIO_I2C4_SDA GPIO_I2C4_SDA_2 /* PF15 */ + +#define GPIO_I2C4_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN | GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN14) +#define GPIO_I2C4_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN | GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN15) + +/* SDMMC2 + * + * VDD 3.3 + * GND + * SDMMC2_CK PD6 + * SDMMC2_CMD PD7 + * SDMMC2_D0 PB14 + * SDMMC2_D1 PB15 + * SDMMC2_D2 PG11 + * SDMMC2_D3 PB4 + */ + +#define GPIO_SDMMC2_CK GPIO_SDMMC2_CK_1 /* PD6 */ +#define GPIO_SDMMC2_CMD GPIO_SDMMC2_CMD_1 /* PD7 */ +// GPIO_SDMMC2_D0 No Remap /* PB14 */ +// GPIO_SDMMC2_D1 No Remap /* PB15 */ +#define GPIO_SDMMC2_D2 GPIO_SDMMC2_D2_1 /* PG11 */ +// GPIO_SDMMC2_D3 No Remap /* PB4 */ + +/* The STM32 H7 connects to a TI DP83848TSQ/NOPB + * using RMII + * + * STM32 H7 BOARD DP83848TSQ/NOPB + * GPIO SIGNAL PIN NAME + * -------- ------------ ------------- + * PA7 ETH_CRS_DV CRS_DV + * PC1 ETH_MDC MDC + * PA2 ETH_MDIO MDIO + * PA1 ETH_REF_CL X1 + * PC4 ETH_RXD0 RX_D0 + * PC5 ETH_RXD1 RX_D1 + * PB11 ETH_TX_EN TX_EN + * PG13 ETH_TXD0 TX_D0 + * PG12 ETH_TXD1 TX_D1 + * + * The PHY address is 1, since COL/PHYAD0 features a pull up. + */ + +#define GPIO_ETH_RMII_TX_EN GPIO_ETH_RMII_TX_EN_1 /* PB11 */ +#define GPIO_ETH_RMII_TXD0 GPIO_ETH_RMII_TXD0_2 /* PG13 */ +#define GPIO_ETH_RMII_TXD1 GPIO_ETH_RMII_TXD1_2 /* PG12 */ + + +/* USB + * + * OTG_FS_DM PA11 + * OTG_FS_DP PA12 + * VBUS PA9 + */ + + +/* Board provides GPIO or other Hardware for signaling to timing analyzer */ + +#if defined(CONFIG_BOARD_USE_PROBES) +# include "stm32_gpio.h" +# define PROBE_N(n) (1<<((n)-1)) +# define PROBE_1 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTI|GPIO_PIN0) /* PI0 AUX1 */ +# define PROBE_2 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN12) /* PH12 AUX2 */ +# define PROBE_3 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN11) /* PH11 AUX3 */ +# define PROBE_4 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN10) /* PH10 AUX4 */ +# define PROBE_5 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN13) /* PD13 AUX5 */ +# define PROBE_6 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN14) /* PD14 AUX6 */ +# define PROBE_7 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN6) /* PH6 AUX7 */ +# define PROBE_8 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN9) /* PH9 AUX8 */ +# define PROBE_9 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN11) /* PE11 CAP1 */ + +# define PROBE_INIT(mask) \ + do { \ + if ((mask)& PROBE_N(1)) { stm32_configgpio(PROBE_1); } \ + if ((mask)& PROBE_N(2)) { stm32_configgpio(PROBE_2); } \ + if ((mask)& PROBE_N(3)) { stm32_configgpio(PROBE_3); } \ + if ((mask)& PROBE_N(4)) { stm32_configgpio(PROBE_4); } \ + if ((mask)& PROBE_N(5)) { stm32_configgpio(PROBE_5); } \ + if ((mask)& PROBE_N(6)) { stm32_configgpio(PROBE_6); } \ + if ((mask)& PROBE_N(7)) { stm32_configgpio(PROBE_7); } \ + if ((mask)& PROBE_N(8)) { stm32_configgpio(PROBE_8); } \ + if ((mask)& PROBE_N(9)) { stm32_configgpio(PROBE_9); } \ + } while(0) + +# define PROBE(n,s) do {stm32_gpiowrite(PROBE_##n,(s));}while(0) +# define PROBE_MARK(n) PROBE(n,false);PROBE(n,true) +#else +# define PROBE_INIT(mask) +# define PROBE(n,s) +# define PROBE_MARK(n) +#endif + +#endif /*__NUTTX_CONFIG_CUAV_X25_EVO_INCLUDE_BOARD_H */ diff --git a/boards/cuav/x25-evo/nuttx-config/include/board_dma_map.h b/boards/cuav/x25-evo/nuttx-config/include/board_dma_map.h new file mode 100644 index 00000000000..03d028fa14a --- /dev/null +++ b/boards/cuav/x25-evo/nuttx-config/include/board_dma_map.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * + * Copyright (c) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +// DMAMUX1 Using at most 8 Channels on DMA1 -------- Assigned +// V + +#define DMAMAP_SPI1_RX DMAMAP_DMA12_SPI1RX_0 /* 1 DMA1:37 IIM42652 */ +#define DMAMAP_SPI1_TX DMAMAP_DMA12_SPI1TX_0 /* 2 DMA1:38 IIM42652 */ + +#define DMAMAP_SPI2_RX DMAMAP_DMA12_SPI2RX_0 /* 3 DMA1:39 IIM42653 */ +#define DMAMAP_SPI2_TX DMAMAP_DMA12_SPI2TX_0 /* 4 DMA1:40 IIM42653 */ + +// #define DMAMAP_USART1_RX DMAMAP_DMA12_USART1RX_0 /* DMA1:41 GPS1 */ +// #define DMAMAP_USART1_TX DMAMAP_DMA12_USART1TX_0 /* DMA1:42 GPS1 */ + +#define DMAMAP_USART2_RX DMAMAP_DMA12_USART2RX_0 /* DMA1:43 Telem3 */ +#define DMAMAP_USART2_TX DMAMAP_DMA12_USART2TX_0 /* DMA1:44 Telem3 */ + +//#define DMAMAP_USART3_RX DMAMAP_DMA12_USART3RX_0 /* DMA1:45 DEBUG */ +//#define DMAMAP_USART3_TX DMAMAP_DMA12_USART3TX_0 /* DMA1:46 DEBUG */ + +//#define DMAMAP_UART4_RX DMAMAP_DMA12_UART4RX_0 /* DMA1:63 EXT2 */ +//#define DMAMAP_UART4_TX DMAMAP_DMA12_UART4TX_0 /* DMA1:64 EXT2 */ + +// #define DMAMAP_USART6_RX DMAMAP_DMA12_USART6RX_0 /* 5 DMA1:71 PX4IO */ +// #define DMAMAP_USART6_TX DMAMAP_DMA12_USART6TX_0 /* 6 DMA1:72 PX4IO */ + +// Assigned in timer_config.cpp + +// Timer 4 /* 7 DMA1:32 TIM4UP */ +// Timer 5 /* 8 DMA1:50 TIM5UP */ + +// DMAMUX2 Using at most 8 Channels on DMA2 -------- Assigned +// V + +#define DMAMAP_SPI4_RX DMAMAP_DMA12_SPI4RX_1 /* 1 DMA2:61 RM3100 */ +#define DMAMAP_SPI4_TX DMAMAP_DMA12_SPI4TX_1 /* 2 DMA2:62 RM3100 */ + +#define DMAMAP_SPI5_RX DMAMAP_DMA12_SPI5RX_1 /* 1 DMA2:61 IIM42653 */ +#define DMAMAP_SPI5_TX DMAMAP_DMA12_SPI5TX_1 /* 2 DMA2:62 IIM42653 */ + +// #define DMAMAP_USART3_RX DMAMAP_DMA12_USART3RX_1 /* 3 DMA2:45 DEBUG */ +// #define DMAMAP_USART3_TX DMAMAP_DMA12_USART3TX_1 /* 4 DMA2:46 DEBUG */ + +#define DMAMAP_UART5_RX DMAMAP_DMA12_UART5RX_1 /* 5 DMA2:65 TELEM2 */ +#define DMAMAP_UART5_TX DMAMAP_DMA12_UART5TX_1 /* 6 DMA2:66 TELEM2 */ + +#define DMAMAP_UART7_RX DMAMAP_DMA12_UART7RX_1 /* 7 DMA1:79 TELEM1 */ +#define DMAMAP_UART7_TX DMAMAP_DMA12_UART7TX_1 /* 8 DMA1:80 TELEM1 */ + +// #define DMAMAP_UART8_RX DMAMAP_DMA12_UART8RX_1 /* DMA1:81 GPS2 */ +// #define DMAMAP_UART8_TX DMAMAP_DMA12_UART8TX_1 /* DMA1:82 GPS2 */ + +// DMAMUX2 Using at most 8 Channels on BDMA -------- Assigned +// V + +#define DMAMAP_SPI6_RX DMAMAP_BDMA_SPI6_RX /* 1 BDMA:11 SPI J11 */ +#define DMAMAP_SPI6_TX DMAMAP_BDMA_SPI6_TX /* 2 BDMA:12 SPI J11 */ diff --git a/boards/cuav/x25-evo/nuttx-config/nsh/defconfig b/boards/cuav/x25-evo/nuttx-config/nsh/defconfig new file mode 100644 index 00000000000..6c576f1a9dc --- /dev/null +++ b/boards/cuav/x25-evo/nuttx-config/nsh/defconfig @@ -0,0 +1,332 @@ +# +# 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_ARP 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_IFCONFIG is not set +# CONFIG_NSH_DISABLE_IFUPDOWN 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_NSLOOKUP 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_TELNETD 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/cuav/x25-evo/nuttx-config" +CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y +CONFIG_ARCH_BOARD_CUSTOM_NAME="px4" +CONFIG_ARCH_CHIP="stm32h7" +CONFIG_ARCH_CHIP_STM32H743XI=y +CONFIG_ARCH_CHIP_STM32H7=y +CONFIG_ARCH_INTERRUPTSTACK=768 +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=0x004c +CONFIG_CDCACM_PRODUCTSTR="PX4 CUAV X25-EVO" +CONFIG_CDCACM_RXBUFSIZE=600 +CONFIG_CDCACM_TXBUFSIZE=12000 +CONFIG_CDCACM_VENDORID=0x3163 +CONFIG_CDCACM_VENDORSTR="CUAV" +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_ETH0_PHY_LAN8742A=y +CONFIG_EXPERIMENTAL=y +CONFIG_FAT_DMAMEMORY=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FAT_LFN_ALIAS_HASH=y +CONFIG_FDCLONE_STDIO=y +CONFIG_FSUTILS_IPCFG=y +CONFIG_FS_BINFS=y +CONFIG_FS_CROMFS=y +CONFIG_FS_FAT=y +CONFIG_FS_FATTIME=y +CONFIG_FS_PROCFS=y +CONFIG_FS_PROCFS_INCLUDE_PROGMEM=y +CONFIG_FS_PROCFS_MAX_TASKS=64 +CONFIG_FS_PROCFS_REGISTER=y +CONFIG_FS_ROMFS=y +CONFIG_GRAN=y +CONFIG_GRAN_INTR=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_I2C=y +CONFIG_I2C_RESET=y +CONFIG_IDLETHREAD_STACKSIZE=750 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=3194 +CONFIG_IOB_NBUFFERS=24 +CONFIG_IOB_THROTTLE=0 +CONFIG_IPCFG_BINARY=y +CONFIG_IPCFG_CHARDEV=y +CONFIG_IPCFG_PATH="/fs/mtd_net" +CONFIG_LIBC_FLOATINGPOINT=y +CONFIG_LIBC_LONG_LONG=y +CONFIG_LIBC_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_NET=y +CONFIG_NETDB_DNSCLIENT=y +CONFIG_NETDB_DNSCLIENT_ENTRIES=8 +CONFIG_NETDB_DNSSERVER_NOADDR=y +CONFIG_NETDEV_PHY_IOCTL=y +CONFIG_NETINIT_DHCPC=y +CONFIG_NETINIT_DNS=y +CONFIG_NETINIT_DNSIPADDR=0XC0A800FE +CONFIG_NETINIT_DRIPADDR=0XC0A800FE +CONFIG_NETINIT_MONITOR=y +CONFIG_NETINIT_THREAD=y +CONFIG_NETINIT_THREAD_PRIORITY=49 +CONFIG_NETUTILS_TELNETD=y +CONFIG_NET_ARP_IPIN=y +CONFIG_NET_ARP_SEND=y +CONFIG_NET_BROADCAST=y +CONFIG_NET_ETH_PKTSIZE=1518 +CONFIG_NET_ICMP=y +CONFIG_NET_ICMP_SOCKET=y +CONFIG_NET_NACTIVESOCKETS=16 +CONFIG_NET_SOLINGER=y +CONFIG_NET_TCP=y +CONFIG_NET_TCPBACKLOG=y +CONFIG_NET_TCP_DELAYED_ACK=y +CONFIG_NET_TCP_WRITE_BUFFERS=y +CONFIG_NET_UDP=y +CONFIG_NET_UDP_CHECKSUMS=y +CONFIG_NET_UDP_WRITE_BUFFERS=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_ARGCAT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_CMDPARMS=y +CONFIG_NSH_CROMFSETC=y +CONFIG_NSH_LINELEN=128 +CONFIG_NSH_MAXARGUMENTS=15 +CONFIG_NSH_NESTDEPTH=8 +CONFIG_NSH_QUOTE=y +CONFIG_NSH_ROMFSETC=y +CONFIG_NSH_ROMFSSECTSIZE=128 +CONFIG_NSH_STRERROR=y +CONFIG_NSH_TELNET=y +CONFIG_NSH_TELNET_LOGIN=y +CONFIG_NSH_VARS=y +CONFIG_OTG_ID_GPIO_DISABLE=y +CONFIG_PIPES=y +CONFIG_PREALLOC_TIMERS=50 +CONFIG_PRIORITY_INHERITANCE=y +CONFIG_PTHREAD_MUTEX_ROBUST=y +CONFIG_PTHREAD_STACK_MIN=512 +CONFIG_RAMTRON_EMULATE_PAGE_SHIFT=6 +CONFIG_RAMTRON_EMULATE_SECTOR_SHIFT=6 +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_SDMMC2_SDIO_PULLUP=y +CONFIG_SEM_PREALLOCHOLDERS=32 +CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS=y +CONFIG_SERIAL_TERMIOS=y +CONFIG_SIG_DEFAULT=y +CONFIG_SIG_SIGALRM_ACTION=y +CONFIG_SIG_SIGUSR1_ACTION=y +CONFIG_SIG_SIGUSR2_ACTION=y +CONFIG_SIG_SIGWORK=4 +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=30 +CONFIG_START_MONTH=11 +CONFIG_STDIO_BUFFER_SIZE=256 +CONFIG_STM32H7_ADC1=y +CONFIG_STM32H7_ADC3=y +CONFIG_STM32H7_BBSRAM=y +CONFIG_STM32H7_BBSRAM_FILES=5 +CONFIG_STM32H7_BDMA=y +CONFIG_STM32H7_BKPSRAM=y +CONFIG_STM32H7_DMA1=y +CONFIG_STM32H7_DMA2=y +CONFIG_STM32H7_DMACAPABLE=y +CONFIG_STM32H7_ETHMAC=y +CONFIG_STM32H7_FLASH_OVERRIDE_I=y +CONFIG_STM32H7_FLOWCONTROL_BROKEN=y +CONFIG_STM32H7_I2C1=y +CONFIG_STM32H7_I2C2=y +CONFIG_STM32H7_I2C3=y +CONFIG_STM32H7_I2C4=y +CONFIG_STM32H7_I2C_DYNTIMEO=y +CONFIG_STM32H7_I2C_DYNTIMEO_STARTSTOP=10 +CONFIG_STM32H7_OTGFS=y +CONFIG_STM32H7_PHYSR=31 +CONFIG_STM32H7_PHYSR_100MBPS=0x8 +CONFIG_STM32H7_PHYSR_FULLDUPLEX=0x10 +CONFIG_STM32H7_PHYSR_MODE=0x10 +CONFIG_STM32H7_PHYSR_SPEED=0x8 +CONFIG_STM32H7_PHY_POLLING=y +CONFIG_STM32H7_PROGMEM=y +CONFIG_STM32H7_RTC=y +CONFIG_STM32H7_RTC_AUTO_LSECLOCK_START_DRV_CAPABILITY=y +CONFIG_STM32H7_RTC_MAGIC_REG=1 +CONFIG_STM32H7_SAVE_CRASHDUMP=y +CONFIG_STM32H7_SDMMC2=y +CONFIG_STM32H7_SERIALBRK_BSDCOMPAT=y +CONFIG_STM32H7_SERIAL_DISABLE_REORDERING=y +CONFIG_STM32H7_SPI1=y +CONFIG_STM32H7_SPI1_DMA=y +CONFIG_STM32H7_SPI1_DMA_BUFFER=1024 +CONFIG_STM32H7_SPI2=y +CONFIG_STM32H7_SPI2_DMA=y +CONFIG_STM32H7_SPI2_DMA_BUFFER=1024 +CONFIG_STM32H7_SPI4=y +CONFIG_STM32H7_SPI4_DMA=y +CONFIG_STM32H7_SPI4_DMA_BUFFER=1024 +CONFIG_STM32H7_SPI5=y +CONFIG_STM32H7_SPI5_DMA=y +CONFIG_STM32H7_SPI5_DMA_BUFFER=1024 +CONFIG_STM32H7_SPI6=y +CONFIG_STM32H7_SPI6_DMA=y +CONFIG_STM32H7_SPI6_DMA_BUFFER=1024 +CONFIG_STM32H7_TIM12=y +CONFIG_STM32H7_TIM1=y +CONFIG_STM32H7_TIM4=y +CONFIG_STM32H7_TIM5=y +CONFIG_STM32H7_UART4=y +CONFIG_STM32H7_UART5=y +CONFIG_STM32H7_UART7=y +CONFIG_STM32H7_UART8=y +CONFIG_STM32H7_USART1=y +CONFIG_STM32H7_USART2=y +CONFIG_STM32H7_USART3=y +CONFIG_STM32H7_USART6=y +CONFIG_STM32H7_USART_BREAKS=y +CONFIG_STM32H7_USART_INVERT=y +CONFIG_STM32H7_USART_SINGLEWIRE=y +CONFIG_STM32H7_USART_SWAP=y +CONFIG_SYSTEM_CDCACM=y +CONFIG_SYSTEM_DHCPC_RENEW=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_PING=y +CONFIG_SYSTEM_SYSTEM=y +CONFIG_TASK_NAME_SIZE=24 +CONFIG_UART4_BAUD=57600 +CONFIG_UART4_RXBUFSIZE=600 +CONFIG_UART4_TXBUFSIZE=1500 +CONFIG_UART5_IFLOWCONTROL=y +CONFIG_UART5_OFLOWCONTROL=y +CONFIG_UART5_RXDMA=y +CONFIG_UART5_TXBUFSIZE=10000 +CONFIG_UART5_TXDMA=y +CONFIG_UART7_BAUD=57600 +CONFIG_UART7_IFLOWCONTROL=y +CONFIG_UART7_OFLOWCONTROL=y +CONFIG_UART7_RXBUFSIZE=600 +CONFIG_UART7_RXDMA=y +CONFIG_UART7_TXBUFSIZE=3000 +CONFIG_UART7_TXDMA=y +CONFIG_UART8_BAUD=57600 +CONFIG_UART8_RXBUFSIZE=600 +CONFIG_UART8_TXBUFSIZE=1500 +CONFIG_USART1_BAUD=57600 +CONFIG_USART1_RXBUFSIZE=600 +CONFIG_USART1_TXBUFSIZE=1500 +CONFIG_USART2_BAUD=57600 +CONFIG_USART2_IFLOWCONTROL=y +CONFIG_USART2_OFLOWCONTROL=y +CONFIG_USART2_RXBUFSIZE=600 +CONFIG_USART2_RXDMA=y +CONFIG_USART2_TXBUFSIZE=3000 +CONFIG_USART2_TXDMA=y +CONFIG_USART3_BAUD=57600 +CONFIG_USART3_RXBUFSIZE=180 +CONFIG_USART3_SERIAL_CONSOLE=y +CONFIG_USART3_TXBUFSIZE=1500 +CONFIG_USART6_BAUD=57600 +CONFIG_USART6_RXBUFSIZE=600 +CONFIG_USART6_TXBUFSIZE=1500 +CONFIG_USBDEV=y +CONFIG_USBDEV_BUSPOWERED=y +CONFIG_USBDEV_MAXPOWER=500 +CONFIG_USEC_PER_TICK=1000 +CONFIG_WATCHDOG=y diff --git a/boards/cuav/x25-evo/nuttx-config/scripts/bootloader_script.ld b/boards/cuav/x25-evo/nuttx-config/scripts/bootloader_script.ld new file mode 100644 index 00000000000..44844638602 --- /dev/null +++ b/boards/cuav/x25-evo/nuttx-config/scripts/bootloader_script.ld @@ -0,0 +1,215 @@ +/**************************************************************************** + * scripts/script.ld + * + * Copyright (C) 2016, 2025 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/* The PX4 FMUV6X uses an STM32H753II has 2048Kb of main FLASH memory. + * The flash memory is partitioned into a User Flash memory and a System + * Flash memory. Each of these memories has two banks: + * + * 1) User Flash memory: + * + * Bank 1: Start address 0x0800:0000 to 0x080F:FFFF with 8 sectors, 128Kb each + * Bank 2: Start address 0x0810:0000 to 0x081F:FFFF with 8 sectors, 128Kb each + * + * 2) System Flash memory: + * + * Bank 1: Start address 0x1FF0:0000 to 0x1FF1:FFFF with 1 x 128Kb sector + * Bank 1: Start address 0x1FF4:0000 to 0x1FF5:FFFF with 1 x 128Kb sector + * + * 3) User option bytes for user configuration, only in Bank 1. + * + * In the STM32H743II, two different boot spaces can be selected through + * the BOOT pin and the boot base address programmed in the BOOT_ADD0 and + * BOOT_ADD1 option bytes: + * + * 1) BOOT=0: Boot address defined by user option byte BOOT_ADD0[15:0]. + * ST programmed value: Flash memory at 0x0800:0000 + * 2) BOOT=1: Boot address defined by user option byte BOOT_ADD1[15:0]. + * ST programmed value: System bootloader at 0x1FF0:0000 + * + * The PX4 FMUV6X has a test point on board, the BOOT0 pin is at ground so by + * default, the STM32 will boot to address 0x0800:0000 in FLASH unless the test + * point is pulled to 3.3V.then the boot will be from 0x1FF0:0000 + * + * The STM32H743II also has 1024Kb of data SRAM. + * SRAM is split up into several blocks and into three power domains: + * + * 1) TCM SRAMs are dedicated to the Cortex-M7 and are accessible with + * 0 wait states by the Cortex-M7 and by MDMA through AHBS slave bus + * + * 1.1) 128Kb of DTCM-RAM beginning at address 0x2000:0000 + * + * The DTCM-RAM is organized as 2 x 64Kb DTCM-RAMs on 2 x 32 bit + * DTCM ports. The DTCM-RAM could be used for critical real-time + * data, such as interrupt service routines or stack / heap memory. + * Both DTCM-RAMs can be used in parallel (for load/store operations) + * thanks to the Cortex-M7 dual issue capability. + * + * 1.2) 64Kb of ITCM-RAM beginning at address 0x0000:0000 + * + * This RAM is connected to ITCM 64-bit interface designed for + * execution of critical real-times routines by the CPU. + * + * 2) AXI SRAM (D1 domain) accessible by all system masters except BDMA + * through D1 domain AXI bus matrix + * + * 2.1) 512Kb of SRAM beginning at address 0x2400:0000 + * + * 3) AHB SRAM (D2 domain) accessible by all system masters except BDMA + * through D2 domain AHB bus matrix + * + * 3.1) 128Kb of SRAM1 beginning at address 0x3000:0000 + * 3.2) 128Kb of SRAM2 beginning at address 0x3002:0000 + * 3.3) 32Kb of SRAM3 beginning at address 0x3004:0000 + * + * SRAM1 - SRAM3 are one contiguous block: 288Kb at address 0x3000:0000 + * + * 4) AHB SRAM (D3 domain) accessible by most of system masters + * through D3 domain AHB bus matrix + * + * 4.1) 64Kb of SRAM4 beginning at address 0x3800:0000 + * 4.1) 4Kb of backup RAM beginning at address 0x3880:0000 + * + * When booting from FLASH, FLASH memory is aliased to address 0x0000:0000 + * where the code expects to begin execution by jumping to the entry point in + * the 0x0800:0000 address range. + * + * The bootloader uses the first sector of the flash, which is 128K in length. + */ + +MEMORY +{ + itcm (rwx) : ORIGIN = 0x00000000, LENGTH = 64K + flash (rx) : ORIGIN = 0x08000000, LENGTH = 128K + dtcm1 (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + dtcm2 (rwx) : ORIGIN = 0x20010000, LENGTH = 64K + sram (rwx) : ORIGIN = 0x24000000, LENGTH = 512K + sram1 (rwx) : ORIGIN = 0x30000000, LENGTH = 128K + sram2 (rwx) : ORIGIN = 0x30020000, LENGTH = 128K + sram3 (rwx) : ORIGIN = 0x30040000, LENGTH = 32K + sram4 (rwx) : ORIGIN = 0x38000000, LENGTH = 64K + bbram (rwx) : ORIGIN = 0x38800000, LENGTH = 4K +} + +OUTPUT_ARCH(arm) +EXTERN(_vectors) +ENTRY(_stext) + +/* + * Ensure that abort() is present in the final object. The exception handling + * code pulled in by libgcc.a requires it (and that code cannot be easily avoided). + */ +EXTERN(abort) +EXTERN(_bootdelay_signature) + +SECTIONS +{ + .text : { + _stext = ABSOLUTE(.); + *(.vectors) + . = ALIGN(32); + /* + This signature provides the bootloader with a way to delay booting + */ + _bootdelay_signature = ABSOLUTE(.); + FILL(0xffecc2925d7d05c5) + . += 8; + *(.text .text.*) + *(.fixup) + *(.gnu.warning) + *(.rodata .rodata.*) + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + _etext = ABSOLUTE(.); + + } > flash + + /* + * Init functions (static constructors and the like) + */ + .init_section : { + _sinit = ABSOLUTE(.); + KEEP(*(.init_array .init_array.*)) + _einit = ABSOLUTE(.); + } > flash + + + .ARM.extab : { + *(.ARM.extab*) + } > flash + + __exidx_start = ABSOLUTE(.); + .ARM.exidx : { + *(.ARM.exidx*) + } > flash + __exidx_end = ABSOLUTE(.); + + _eronly = ABSOLUTE(.); + + .data : { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.gnu.linkonce.d.*) + CONSTRUCTORS + _edata = ABSOLUTE(.); + } > sram AT > flash + + .bss : { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = ABSOLUTE(.); + } > sram + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/boards/cuav/x25-evo/nuttx-config/scripts/script.ld b/boards/cuav/x25-evo/nuttx-config/scripts/script.ld new file mode 100644 index 00000000000..57909007089 --- /dev/null +++ b/boards/cuav/x25-evo/nuttx-config/scripts/script.ld @@ -0,0 +1,229 @@ +/**************************************************************************** + * scripts/script.ld + * + * Copyright (C) 2016, 2025 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/* The PX4 FMUV6X uses an STM32H753II has 2048Kb of main FLASH memory. + * The flash memory is partitioned into a User Flash memory and a System + * Flash memory. Each of these memories has two banks: + * + * 1) User Flash memory: + * + * Bank 1: Start address 0x0800:0000 to 0x080F:FFFF with 8 sectors, 128Kb each + * Bank 2: Start address 0x0810:0000 to 0x081F:FFFF with 8 sectors, 128Kb each + * + * 2) System Flash memory: + * + * Bank 1: Start address 0x1FF0:0000 to 0x1FF1:FFFF with 1 x 128Kb sector + * Bank 1: Start address 0x1FF4:0000 to 0x1FF5:FFFF with 1 x 128Kb sector + * + * 3) User option bytes for user configuration, only in Bank 1. + * + * In the STM32H743II, two different boot spaces can be selected through + * the BOOT pin and the boot base address programmed in the BOOT_ADD0 and + * BOOT_ADD1 option bytes: + * + * 1) BOOT=0: Boot address defined by user option byte BOOT_ADD0[15:0]. + * ST programmed value: Flash memory at 0x0800:0000 + * 2) BOOT=1: Boot address defined by user option byte BOOT_ADD1[15:0]. + * ST programmed value: System bootloader at 0x1FF0:0000 + * + * The PX4 FMUV6X has a test point on board, the BOOT0 pin is at ground so by + * default, the STM32 will boot to address 0x0800:0000 in FLASH unless the test + * point is pulled to 3.3V.then the boot will be from 0x1FF0:0000 + * + * The STM32H743II also has 1024Kb of data SRAM. + * SRAM is split up into several blocks and into three power domains: + * + * 1) TCM SRAMs are dedicated to the Cortex-M7 and are accessible with + * 0 wait states by the Cortex-M7 and by MDMA through AHBS slave bus + * + * 1.1) 128Kb of DTCM-RAM beginning at address 0x2000:0000 + * + * The DTCM-RAM is organized as 2 x 64Kb DTCM-RAMs on 2 x 32 bit + * DTCM ports. The DTCM-RAM could be used for critical real-time + * data, such as interrupt service routines or stack / heap memory. + * Both DTCM-RAMs can be used in parallel (for load/store operations) + * thanks to the Cortex-M7 dual issue capability. + * + * 1.2) 64Kb of ITCM-RAM beginning at address 0x0000:0000 + * + * This RAM is connected to ITCM 64-bit interface designed for + * execution of critical real-times routines by the CPU. + * + * 2) AXI SRAM (D1 domain) accessible by all system masters except BDMA + * through D1 domain AXI bus matrix + * + * 2.1) 512Kb of SRAM beginning at address 0x2400:0000 + * + * 3) AHB SRAM (D2 domain) accessible by all system masters except BDMA + * through D2 domain AHB bus matrix + * + * 3.1) 128Kb of SRAM1 beginning at address 0x3000:0000 + * 3.2) 128Kb of SRAM2 beginning at address 0x3002:0000 + * 3.3) 32Kb of SRAM3 beginning at address 0x3004:0000 + * + * SRAM1 - SRAM3 are one contiguous block: 288Kb at address 0x3000:0000 + * + * 4) AHB SRAM (D3 domain) accessible by most of system masters + * through D3 domain AHB bus matrix + * + * 4.1) 64Kb of SRAM4 beginning at address 0x3800:0000 + * 4.1) 4Kb of backup RAM beginning at address 0x3880:0000 + * + * When booting from FLASH, FLASH memory is aliased to address 0x0000:0000 + * where the code expects to begin execution by jumping to the entry point in + * the 0x0800:0000 address range. + */ + +MEMORY +{ + ITCM_RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 64K + FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 1920K + + DTCM1_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + DTCM2_RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 64K + AXI_SRAM (rwx) : ORIGIN = 0x24000000, LENGTH = 512K /* D1 domain AXI bus */ + SRAM1 (rwx) : ORIGIN = 0x30000000, LENGTH = 128K /* D2 domain AHB bus */ + SRAM2 (rwx) : ORIGIN = 0x30020000, LENGTH = 128K /* D2 domain AHB bus */ + SRAM3 (rwx) : ORIGIN = 0x30040000, LENGTH = 32K /* D2 domain AHB bus */ + SRAM4 (rwx) : ORIGIN = 0x38000000, LENGTH = 64K /* D3 domain */ + BKPRAM (rwx) : ORIGIN = 0x38800000, LENGTH = 4K +} + +OUTPUT_ARCH(arm) +EXTERN(_vectors) +ENTRY(_stext) + +/* + * Ensure that abort() is present in the final object. The exception handling + * code pulled in by libgcc.a requires it (and that code cannot be easily avoided). + */ +EXTERN(abort) +EXTERN(_bootdelay_signature) +EXTERN(board_get_manifest) + +SECTIONS +{ + .text : { + _stext = ABSOLUTE(.); + *(.vectors) + . = ALIGN(32); + /* + This signature provides the bootloader with a way to delay booting + */ + _bootdelay_signature = ABSOLUTE(.); + FILL(0xffecc2925d7d05c5) + . += 8; + *(.text .text.*) + *(.fixup) + *(.gnu.warning) + *(.rodata .rodata.*) + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + _etext = ABSOLUTE(.); + + } > FLASH + + /* + * Init functions (static constructors and the like) + */ + .init_section : { + _sinit = ABSOLUTE(.); + KEEP(*(.init_array .init_array.*)) + _einit = ABSOLUTE(.); + } > FLASH + + + .ARM.extab : { + *(.ARM.extab*) + } > FLASH + + __exidx_start = ABSOLUTE(.); + .ARM.exidx : { + *(.ARM.exidx*) + } > FLASH + __exidx_end = ABSOLUTE(.); + + _eronly = ABSOLUTE(.); + + .data : { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.gnu.linkonce.d.*) + CONSTRUCTORS + _edata = ABSOLUTE(.); + + /* Pad out last section as the STM32H7 Flash write size is 256 bits. 32 bytes */ + . = ALIGN(16); + FILL(0xffff) + . += 16; + } > AXI_SRAM AT > FLASH = 0xffff + + .bss : { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = ABSOLUTE(.); + } > AXI_SRAM + + /* Emit the the D3 power domain section for locating BDMA data */ + + .sram4_reserve (NOLOAD) : + { + *(.sram4) + . = ALIGN(4); + _sram4_heap_start = ABSOLUTE(.); + } > SRAM4 + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/boards/cuav/x25-evo/pwm_voltage/CMakeLists.txt b/boards/cuav/x25-evo/pwm_voltage/CMakeLists.txt new file mode 100644 index 00000000000..6db1696208e --- /dev/null +++ b/boards/cuav/x25-evo/pwm_voltage/CMakeLists.txt @@ -0,0 +1,42 @@ +############################################################################ +# +# Copyright (c) 2025 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 modules__pwm_voltage_apply + MAIN pwm_voltage_apply + + SRCS + pwm_voltage.cpp + DEPENDS + px4_work_queue + ) diff --git a/boards/cuav/x25-evo/pwm_voltage/parameters.c b/boards/cuav/x25-evo/pwm_voltage/parameters.c new file mode 100644 index 00000000000..728bcc7a555 --- /dev/null +++ b/boards/cuav/x25-evo/pwm_voltage/parameters.c @@ -0,0 +1,43 @@ +/**************************************************************************** + * + * Copyright (c) 2025 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. + * + ****************************************************************************/ + +/** + * Control PWM output voltage + * + * @value 0 3.3V + * @value 1 5.0V + * + * @reboot_required true + * @group PWM Outputs + */ +PARAM_DEFINE_INT32(PWM_VOLT_SEL, 0); diff --git a/boards/cuav/x25-evo/pwm_voltage/pwm_voltage.cpp b/boards/cuav/x25-evo/pwm_voltage/pwm_voltage.cpp new file mode 100644 index 00000000000..2bef9ae0f6a --- /dev/null +++ b/boards/cuav/x25-evo/pwm_voltage/pwm_voltage.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** + * + * Copyright (c) 2025 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 +#include + +#include "board_config.h" + +extern "C" int pwm_voltage_apply_main(int argc, char *argv[]) +{ + int32_t pwm_volt_sel = 0; + + param_get(param_find("PWM_VOLT_SEL"), &pwm_volt_sel); + + if (pwm_volt_sel != 0) { + PWM_5V_VOLT_SEL(true); + + } else { + PWM_5V_VOLT_SEL(false); + } + + return 0; +} diff --git a/boards/cuav/x25-evo/src/CMakeLists.txt b/boards/cuav/x25-evo/src/CMakeLists.txt new file mode 100644 index 00000000000..a55550c0e50 --- /dev/null +++ b/boards/cuav/x25-evo/src/CMakeLists.txt @@ -0,0 +1,75 @@ +############################################################################ +# +# Copyright (c) 2025 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_compile_definitions(BOOTLOADER) + add_library(drivers_board + bootloader_main.c + init.c + usb.c + timer_config.cpp + ) + target_link_libraries(drivers_board + PRIVATE + nuttx_arch # sdio + nuttx_drivers # sdio + px4_layer #gpio + arch_io_pins # iotimer + bootloader + ) + target_include_directories(drivers_board PRIVATE ${PX4_SOURCE_DIR}/platforms/nuttx/src/bootloader/common) + +else() + add_library(drivers_board + can.c + i2c.cpp + init.c + led.c + mtd.cpp + sdio.c + spi.cpp + timer_config.cpp + usb.c + ) + add_dependencies(drivers_board arch_board_hw_info) + + target_link_libraries(drivers_board + PRIVATE + arch_io_pins + arch_spi + arch_board_hw_info + drivers__led # drv_led_start + nuttx_arch # sdio + nuttx_drivers # sdio + px4_layer + ) +endif() diff --git a/boards/cuav/x25-evo/src/board_config.h b/boards/cuav/x25-evo/src/board_config.h new file mode 100644 index 00000000000..beb675064bd --- /dev/null +++ b/boards/cuav/x25-evo/src/board_config.h @@ -0,0 +1,501 @@ +/**************************************************************************** + * + * Copyright (c) 2016-2025 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 + * + * CUAV X25-EVO internal definitions + */ + +#pragma once + +/**************************************************************************************************** + * Included Files + ****************************************************************************************************/ + +#include +#include +#include + + +#include + +/**************************************************************************************************** + * Definitions + ****************************************************************************************************/ + +#undef TRACE_PINS + +/* Configuration ************************************************************************************/ + +#define BOARD_HAS_LTC44XX_VALIDS 2 // N Bricks +#define BOARD_HAS_USB_VALID 1 // LTC Has USB valid +#define BOARD_HAS_NBAT_V 2d // 2 Digital Voltage +#define BOARD_HAS_NBAT_I 2d // 2 Digital Current + +/* PX4FMU GPIOs ***********************************************************************************/ + +/* LEDs are driven with push open drain to support Anode to 5V or 3.3V or used as TRACE0-2 */ + +# define GPIO_nLED_RED /* PE3 */ (GPIO_OUTPUT|GPIO_OPENDRAIN|GPIO_SPEED_50MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN3) +# define GPIO_nLED_GREEN /* PE4 */ (GPIO_OUTPUT|GPIO_OPENDRAIN|GPIO_SPEED_50MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN4) +# define GPIO_nLED_BLUE /* PE5 */ (GPIO_OUTPUT|GPIO_OPENDRAIN|GPIO_SPEED_50MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN5) + +# define BOARD_HAS_CONTROL_STATUS_LEDS 1 +# define BOARD_OVERLOAD_LED LED_RED +# define BOARD_ARMED_STATE_LED LED_BLUE + +/* CAN Silence Silent mode control */ + +#define GPIO_CAN1_SILENT_S0 /* PE2 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN2) +#define GPIO_CAN2_SILENT_S1 /* PI8 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTI|GPIO_PIN8) + +/* SPI */ + +// #define SPI6_nRESET_EXTERNAL1 /* PF10 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTF|GPIO_PIN10) + +/* I2C busses */ + +/* Devices on the onboard buses. + * + * Note that these are unshifted addresses. + */ +#define BOARD_MTD_NUM_EEPROM 1 /* MTD: imu_eeprom*/ + +#define GPIO_I2C4_DRDY1_ICP20100 /* PG5 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTG|GPIO_PIN5) + +/* + * ADC channels + * + * These are the channel numbers of the ADCs of the microcontroller that + * can be used by the Px4 Firmware in the adc driver + */ + +/* ADC defines to be used in sensors.cpp to read from a particular channel */ + +#define ADC1_CH(n) (n) + +/* N.B. there is no offset mapping needed for ADC3 because */ +#define ADC3_CH(n) (n) + +/* We are only use ADC3 for REV/VER. + * ADC3_6V6 and ADC3_3V3 are mapped back to ADC1 + * To do this We are relying on PC2_C, PC3_C being connected to PC2, PC3 + * respectively by the SYSCFG_PMCR default of setting for PC3SO PC2SO PA1SO + * PA0SO of 0. + * + * 0 Analog switch closed (pads are connected through the analog switch) + * + * So ADC3_INP0 is GPIO_ADC123_INP12 + * ADC3_INP1 is GPIO_ADC12_INP13 + */ + +/* Define GPIO pins used as ADC N.B. Channel numbers must match below */ + +#define PX4_ADC_GPIO \ + /* PA0 */ GPIO_ADC1_INP16, \ + /* PA4 */ GPIO_ADC12_INP18, \ + /* PB0 */ GPIO_ADC12_INP9, \ + /* PB1 */ GPIO_ADC12_INP5, \ + /* PC0 */ GPIO_ADC123_INP10, \ + /* PC2 */ GPIO_ADC123_INP12, \ + /* PC3 */ GPIO_ADC12_INP13, \ + /* PF12 */ GPIO_ADC1_INP6, \ + /* PH3 */ GPIO_ADC3_INP14, \ + /* PH4 */ GPIO_ADC3_INP15 + +// TODO: PF3 ADC3 6V6 +/* Define Channel numbers must match above GPIO pin IN(n)*/ +#define ADC_SCALED_VDD_3V3_SENSORS1_CHANNEL /* PA0 */ ADC1_CH(16) +#define ADC_SCALED_VDD_3V3_SENSORS2_CHANNEL /* PA4 */ ADC1_CH(18) +#define ADC_BATTERY_VOLTAGE_CHANNEL /* PB0 */ ADC1_CH(9) +#define ADC_SCALED_V5_CHANNEL /* PB1 */ ADC1_CH(5) +#define ADC_RSSI_IN_CHANNEL /* PC0 */ ADC1_CH(10) +#define ADC_BATTERY_CURRENT_CHANNEL /* PC2 */ ADC3_CH(12) +#define ADC_ADC3_3V3_CHANNEL /* PC3 */ ADC3_CH(13) +#define ADC_SERVO_VDD_SENSORS_CHANNEL /* PF12 */ ADC1_CH(6) +#define ADC_HW_VER_SENSE_CHANNEL /* PH3 */ ADC3_CH(14) +#define ADC_HW_REV_SENSE_CHANNEL /* PH4 */ ADC3_CH(15) + +#define ADC_CHANNELS \ + ((1 << ADC_SCALED_VDD_3V3_SENSORS1_CHANNEL) | \ + (1 << ADC_SCALED_VDD_3V3_SENSORS2_CHANNEL) | \ + (1 << ADC_BATTERY_VOLTAGE_CHANNEL) | \ + (1 << ADC_SCALED_V5_CHANNEL) | \ + (1 << ADC_RSSI_IN_CHANNEL) | \ + (1 << ADC_BATTERY_CURRENT_CHANNEL) | \ + (1 << ADC_ADC3_3V3_CHANNEL) | \ + (1 << ADC_SERVO_VDD_SENSORS_CHANNEL)) + +/* HW has to large of R termination on ADC todo:change when HW value is chosen */ + +#define HW_REV_VER_ADC_BASE STM32_ADC3_BASE + +#define SYSTEM_ADC_BASE STM32_ADC1_BASE + +/* HW has to large of R termination on ADC todo:change when HW value is chosen */ +#define BOARD_ADC_OPEN_CIRCUIT_V (5.6f) + +#define BOARD_ADC_BRICK_VALID 1 + +/* HW Version and Revision drive signals Default to 1 to detect */ +// #define BOARD_HAS_HW_SPLIT_VERSIONING + +#define GPIO_HW_VER_REV_DRIVE /* PG0 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTG|GPIO_PIN0) +#define GPIO_HW_REV_SENSE /* PH4 */ GPIO_ADC3_INP15 +#define GPIO_HW_VER_SENSE /* PH3 */ GPIO_ADC3_INP14 + +/* HEATER + * PWM in future + */ +// IMU BOARD HEATER +#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10) +#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true)) +// CORE BOARD HEATER +#define GPIO_CORE_HEATER_OUTPUT /* PE6 T15CH2 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN6) +#define CORE_HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_CORE_HEATER_OUTPUT, (on_true)) + +/* PE7 is nARMED + * The GPIO will be set as input while not armed HW will have external HW Pull DOWN. + * While armed it shall be configured at a GPIO OUT set HIGH + */ +#if !defined(TRACE_PINS) +#define GPIO_nARMED_INIT /* PE7 */ (GPIO_INPUT|GPIO_PULLDOWN|GPIO_PORTE|GPIO_PIN7) +#define GPIO_nARMED /* PE7 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN7) +#define BOARD_INDICATE_EXTERNAL_LOCKOUT_STATE(enabled) px4_arch_configgpio((enabled) ? GPIO_nARMED : GPIO_nARMED_INIT) +#define BOARD_GET_EXTERNAL_LOCKOUT_STATE() (!(px4_arch_gpioread(GPIO_nARMED))) +#endif + + +/* PWM + */ +#define DIRECT_PWM_OUTPUT_CHANNELS 16 + +/* PWM Power */ +#define GPIO_PWM_VOLT_SEL /* PG8 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN8) +#define PWM_5V_VOLT_SEL(on_true) px4_arch_gpiowrite(GPIO_PWM_VOLT_SEL, (on_true)) + +/* Power supply control and monitoring GPIOs */ + +#define GPIO_nPOWER_IN_A /* PJ1 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN1) +#define GPIO_nPOWER_IN_B /* PJ2 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN2) +#define GPIO_nPOWER_IN_C /* PJ3 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN3) + +#define GPIO_nVDD_BRICK1_VALID GPIO_nPOWER_IN_A /* Brick 1 Is Chosen */ +#define GPIO_nVDD_BRICK2_VALID GPIO_nPOWER_IN_B /* Brick 2 Is Chosen */ +#define BOARD_NUMBER_BRICKS 1 +#define GPIO_nVDD_USB_VALID GPIO_nPOWER_IN_C /* USB Is Chosen */ + +#define GPIO_VDD_3V3_SD_CARD_EN /* PC13 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTC|GPIO_PIN13) +#define GPIO_VDD_5V_PERIPH_nOC /* PE15 */ (GPIO_INPUT |GPIO_FLOAT|GPIO_PORTE|GPIO_PIN15) +#define GPIO_VDD_5V_HIPOWER_nOC /* PF13 */ (GPIO_INPUT |GPIO_FLOAT|GPIO_PORTF|GPIO_PIN13) +#define GPIO_VDD_5V_PERIPH_EN /* PG4 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN4) +#define GPIO_VDD_5V_HIPOWER_EN /* PG10 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN10) +#define GPIO_VDD_PWM_POWER_EN /* PH2 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN2) +#define GPIO_VDD_3V3_SENSORS3_EN /* PI4 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTI|GPIO_PIN4) +#define GPIO_VDD_3V3_SENSORS4_EN /* PJ4 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTJ|GPIO_PIN4) +#define GPIO_VDD_5V_RC_EN /* PJ5 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTJ|GPIO_PIN5) + +#define GPIO_VDD_3V3_SENSORS1_EN /* PF4 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTF|GPIO_PIN4) +#define GPIO_VDD_3V3_SENSORS2_EN /* PI11 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTI|GPIO_PIN4) + +/* Spare GPIO */ + +#define GPIO_ONBOARD_MAG_IN /* PA15 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTA|GPIO_PIN15) +#define GPIO_ADC_6V6 /* PF3 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_PORTF|GPIO_PIN3) +#define GPIO_PG2 /* PG2 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTG|GPIO_PIN2) +#define GPIO_SPI3_nCS2 /* PI13 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTI|GPIO_PIN13) +#define GPIO_SPI3_DRDY2_INT1 /* PI14 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTI|GPIO_PIN14) +#define GPIO_SPI3_POWER_EN2 /* PI15 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTI|GPIO_PIN15) +#define GPIO_PJ8 /* PJ8 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN8) +#define GPIO_PJ10 /* PJ10 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN10) +#define GPIO_PJ11 /* PJ11 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN11) +#define GPIO_PJ12 /* PJ12 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN12) +#define GPIO_PJ13 /* PJ13 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN13) +#define GPIO_PJ14 /* PJ14 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN14) +#define GPIO_PK0 /* PK0 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTK|GPIO_PIN0) +#define GPIO_PK1 /* PK1 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTK|GPIO_PIN1) +#define GPIO_PK2 /* PK2 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTK|GPIO_PIN2) +#define GPIO_PK3 /* PK3 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTK|GPIO_PIN3) +#define GPIO_PK4 /* PK4 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTK|GPIO_PIN4) +#define GPIO_PK6 /* PK6 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTK|GPIO_PIN6) +#define GPIO_PK7 /* PK7 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTK|GPIO_PIN7) + +/* ETHERNET GPIO */ + +#define GPIO_ETH_POWER_EN /* PG15 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN15) + +/* Define True logic Power Control in arch agnostic form */ +#define VDD_5V_PERIPH_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_5V_PERIPH_EN, (on_true)) +#define VDD_5V_HIPOWER_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_5V_HIPOWER_EN, (on_true)) +#define VDD_PWM_POWER_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_PWM_POWER_EN, (on_true)) +#define VDD_3V3_SD_CARD_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SD_CARD_EN, (on_true)) +#define VDD_3V3_ETH_POWER_EN(on_true) px4_arch_gpiowrite(GPIO_ETH_POWER_EN, (on_true)) +#define VDD_3V3_SENSORS3_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SENSORS3_EN, (on_true)) +#define VDD_3V3_SENSORS4_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SENSORS4_EN, (on_true)) +#define VDD_5V_RC_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_5V_RC_EN, (on_true)) + +#define VDD_3V3_SENSORS1_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SENSORS1_EN, (on_true)) +#define VDD_3V3_SENSORS2_EN(on_true) px4_arch_gpiowrite(GPIO_VDD_3V3_SENSORS2_EN, (on_true)) +/* Tone alarm output */ + +#define TONE_ALARM_TIMER 14 /* Timer 14 */ +#define TONE_ALARM_CHANNEL 1 /* PF9 GPIO_TIM14_CH1OUT_2 */ + +#define GPIO_BUZZER_1 /* PF9 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTF|GPIO_PIN9) + +#define GPIO_TONE_ALARM_IDLE GPIO_BUZZER_1 +#define GPIO_TONE_ALARM GPIO_TIM14_CH1OUT_2 + +/* USB OTG FS + * + * PA9 OTG_FS_VBUS VBUS sensing + */ +#define GPIO_OTGFS_VBUS /* PA9 */ (GPIO_INPUT|GPIO_PULLDOWN|GPIO_SPEED_100MHz|GPIO_PORTA|GPIO_PIN9) + +/* High-resolution timer */ +#define HRT_TIMER 3 /* use timer8 for the HRT */ +#define HRT_TIMER_CHANNEL 2 /* use capture/compare channel 3 */ + +#define HRT_PPM_CHANNEL /* T8C1 */ 1 /* use capture/compare channel 1 */ +#define GPIO_PPM_IN /* PC6 T8C1 */ GPIO_TIM3_CH1IN_3 + +/* RC Serial port */ + +#define RC_SERIAL_PORT "/dev/ttyS5" +#define RC_SERIAL_SINGLEWIRE + +/* PWM input driver. Use FMU AUX5 pins attached to timer4 channel 2 */ +#define PWMIN_TIMER 4 +#define PWMIN_TIMER_CHANNEL /* T4C2 */ 2 +#define GPIO_PWM_IN /* PD13 */ GPIO_TIM4_CH2IN_2 + +/* Safety Switch is HW version dependent on having an PX4IO + * So we init to a benign state with the _INIT definition + * and provide the the non _INIT one for the driver to make a run time + * decision to use it. + */ +// #define GPIO_nSAFETY_SWITCH_LED_OUT_INIT /* PD10 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_PORTD|GPIO_PIN10) +#define GPIO_nSAFETY_SWITCH_LED_OUT /* PD10 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTD|GPIO_PIN10) + +/* Enable the FMU to control it if there is no px4io fixme:This should be BOARD_SAFETY_LED(__ontrue) */ +#define GPIO_LED_SAFETY GPIO_nSAFETY_SWITCH_LED_OUT + +#define GPIO_SAFETY_SWITCH_IN /* PF5 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTF|GPIO_PIN5) +/* Enable the FMU to use the switch it if there is no px4io fixme:This should be BOARD_SAFTY_BUTTON() */ +#define GPIO_BTN_SAFETY GPIO_SAFETY_SWITCH_IN /* Enable the FMU to control it if there is no px4io */ + +/* Power switch controls ******************************************************/ + +// #define SPEKTRUM_POWER(_on_true) VDD_3V3_SPEKTRUM_POWER_EN(_on_true) + +/* + * FMUv6X has a separate RC_IN + * + * GPIO PPM_IN on PI5 T8CH1 + * SPEKTRUM_RX (it's TX or RX in Bind) on UART6 PC7 + * Inversion is possible in the UART and can drive GPIO PPM_IN as an output + */ + +#define GPIO_PPM_IN_AS_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTC|GPIO_PIN6) +#define SPEKTRUM_RX_AS_GPIO_OUTPUT() px4_arch_configgpio(GPIO_PPM_IN_AS_OUT) +#define SPEKTRUM_RX_AS_UART() /* Can be left as uart */ +#define SPEKTRUM_OUT(_one_true) px4_arch_gpiowrite(GPIO_PPM_IN_AS_OUT, (_one_true)) + +// /* RSSI_IN */ +#define GPIO_RSSI_IN /* PC0 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTC|GPIO_PIN0) + +#define SDIO_SLOTNO 0 /* Only one slot */ +#define SDIO_MINOR 0 + +/* SD card bringup does not work if performed on the IDLE thread because it + * will cause waiting. Use either: + * + * CONFIG_BOARDCTL=y, OR + * CONFIG_BOARD_INITIALIZE=y && CONFIG_BOARD_INITTHREAD=y + */ + +#if defined(CONFIG_BOARD_INITIALIZE) && !defined(CONFIG_BOARDCTL) && \ + !defined(CONFIG_BOARD_INITTHREAD) +# warning SDIO initialization cannot be perfomed on the IDLE thread +#endif + +/* By Providing BOARD_ADC_USB_CONNECTED (using the px4_arch abstraction) + * this board support the ADC system_power interface, and therefore + * provides the true logic GPIO BOARD_ADC_xxxx macros. + */ +#define BOARD_ADC_USB_CONNECTED (px4_arch_gpioread(GPIO_OTGFS_VBUS)) +#define BOARD_ADC_USB_VALID (!px4_arch_gpioread(GPIO_nVDD_USB_VALID)) + +/* FMUv6X never powers off the Servo rail */ + +#define BOARD_ADC_SERVO_VALID (1) + +#if !defined(BOARD_HAS_LTC44XX_VALIDS) || BOARD_HAS_LTC44XX_VALIDS == 0 +# define BOARD_ADC_BRICK1_VALID (1) +# define BOARD_ADC_BRICK2_VALID (0) +#elif BOARD_HAS_LTC44XX_VALIDS == 1 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (0) +#elif BOARD_HAS_LTC44XX_VALIDS == 2 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK2_VALID)) +#elif BOARD_HAS_LTC44XX_VALIDS == 3 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK2_VALID)) +# define BOARD_ADC_BRICK3_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK3_VALID)) +#elif BOARD_HAS_LTC44XX_VALIDS == 4 +# define BOARD_ADC_BRICK1_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK1_VALID)) +# define BOARD_ADC_BRICK2_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK2_VALID)) +# define BOARD_ADC_BRICK3_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK3_VALID)) +# define BOARD_ADC_BRICK4_VALID (!px4_arch_gpioread(GPIO_nVDD_BRICK4_VALID)) +#else +# error Unsupported BOARD_HAS_LTC44XX_VALIDS value +#endif + +#define BOARD_ADC_PERIPH_5V_OC (!px4_arch_gpioread(GPIO_VDD_5V_PERIPH_nOC)) +#define BOARD_ADC_HIPOWER_5V_OC (!px4_arch_gpioread(GPIO_VDD_5V_HIPOWER_nOC)) + + +/* This board provides a DMA pool and APIs */ +#define BOARD_DMA_ALLOC_POOL_SIZE 5120 + +/* This board provides the board_on_reset interface */ + +#define BOARD_HAS_ON_RESET 1 + +#define PX4_GPIO_INIT_LIST { \ + PX4_ADC_GPIO, \ + GPIO_HW_VER_REV_DRIVE, \ + GPIO_CAN1_TX, \ + GPIO_CAN1_RX, \ + GPIO_CAN2_TX, \ + GPIO_CAN2_RX, \ + GPIO_CAN1_SILENT_S0, \ + GPIO_CAN2_SILENT_S1, \ + GPIO_HEATER_OUTPUT, \ + GPIO_CORE_HEATER_OUTPUT, \ + GPIO_nPOWER_IN_A, \ + GPIO_nPOWER_IN_B, \ + GPIO_nPOWER_IN_C, \ + GPIO_VDD_5V_PERIPH_EN, \ + GPIO_VDD_5V_PERIPH_nOC, \ + GPIO_VDD_5V_HIPOWER_EN, \ + GPIO_VDD_5V_HIPOWER_nOC, \ + GPIO_VDD_PWM_POWER_EN, \ + GPIO_VDD_3V3_SD_CARD_EN, \ + GPIO_VDD_3V3_SENSORS3_EN, \ + GPIO_VDD_3V3_SENSORS4_EN, \ + GPIO_VDD_3V3_SENSORS1_EN, \ + GPIO_VDD_3V3_SENSORS2_EN, \ + GPIO_VDD_5V_RC_EN, \ + GPIO_ETH_POWER_EN, \ + GPIO_TONE_ALARM_IDLE, \ + GPIO_nSAFETY_SWITCH_LED_OUT, \ + GPIO_SAFETY_SWITCH_IN, \ + GPIO_nARMED_INIT, \ + GPIO_ONBOARD_MAG_IN, \ + GPIO_PWM_VOLT_SEL, \ + GPIO_ADC_6V6, \ + GPIO_PG2, \ + GPIO_SPI3_nCS2, \ + GPIO_SPI3_DRDY2_INT1, \ + GPIO_SPI3_POWER_EN2, \ + GPIO_PJ8, \ + GPIO_PJ10, \ + GPIO_PJ11, \ + GPIO_PJ12, \ + GPIO_PJ13, \ + GPIO_PJ14, \ + GPIO_PK0, \ + GPIO_PK1, \ + GPIO_PK2, \ + GPIO_PK3, \ + GPIO_PK4, \ + GPIO_PK6, \ + GPIO_PK7, \ + } + +#define BOARD_ENABLE_CONSOLE_BUFFER + +#define PX4_I2C_BUS_MTD 4,5 + + +#define BOARD_NUM_IO_TIMERS 5 +#define BOARD_SPI_BUS_MAX_BUS_ITEMS 5 + +__BEGIN_DECLS + +/**************************************************************************************************** + * Public Types + ****************************************************************************************************/ + +/**************************************************************************************************** + * Public data + ****************************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************************************** + * Public Functions + ****************************************************************************************************/ + +/**************************************************************************** + * Name: stm32_sdio_initialize + * + * Description: + * Initialize SDIO-based MMC/SD card support + * + ****************************************************************************/ + +int stm32_sdio_initialize(void); + +/**************************************************************************************************** + * Name: stm32_spiinitialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the PX4FMU board. + * + ****************************************************************************************************/ + +extern void stm32_spiinitialize(void); + +extern void stm32_usbinitialize(void); + +extern void board_peripheral_reset(int ms); + +#include + +#endif /* __ASSEMBLY__ */ + +__END_DECLS diff --git a/boards/cuav/x25-evo/src/bootloader_main.c b/boards/cuav/x25-evo/src/bootloader_main.c new file mode 100644 index 00000000000..34cf9f6e8b2 --- /dev/null +++ b/boards/cuav/x25-evo/src/bootloader_main.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * + * Copyright (c) 2019-2025 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); + +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/cuav/x25-evo/src/can.c b/boards/cuav/x25-evo/src/can.c new file mode 100644 index 00000000000..4cdbcbc2f35 --- /dev/null +++ b/boards/cuav/x25-evo/src/can.c @@ -0,0 +1,123 @@ +/**************************************************************************** + * + * Copyright (C) 2025 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 x25-evo_can.c + * + * Board-specific CAN functions. + */ + +#ifdef CONFIG_CAN + +#include +#include + +#include +#include + +#include "chip.h" +#include "arm_internal.h" + +#include "chip.h" +#include "stm32_can.h" +#include "board_config.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +#if defined(CONFIG_STM32_CAN1) && defined(CONFIG_STM32_CAN2) +# warning "Both CAN1 and CAN2 are enabled. Assuming only CAN1." +# undef CONFIG_STM32_CAN2 +#endif + +#ifdef CONFIG_STM32_CAN1 +# define CAN_PORT 1 +#else +# define CAN_PORT 2 +#endif + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ +int can_devinit(void); + +/************************************************************************************ + * Name: can_devinit + * + * Description: + * All STM32 architectures must provide the following interface to work with + * examples/can. + * + ************************************************************************************/ + +int can_devinit(void) +{ + static bool initialized = false; + struct can_dev_s *can; + int ret; + + /* Check if we have already initialized */ + + if (!initialized) { + /* Call stm32_caninitialize() to get an instance of the CAN interface */ + + can = stm32_caninitialize(CAN_PORT); + + if (can == NULL) { + canerr("ERROR: Failed to get CAN interface\n"); + return -ENODEV; + } + + /* Register the CAN driver at "/dev/can0" */ + + ret = can_register("/dev/can0", can); + + if (ret < 0) { + canerr("ERROR: can_register failed: %d\n", ret); + return ret; + } + + /* Now we are initialized */ + + initialized = true; + } + + return OK; +} +#endif /* CONFIG_CAN */ diff --git a/boards/cuav/x25-evo/src/hw_config.h b/boards/cuav/x25-evo/src/hw_config.h new file mode 100644 index 00000000000..3703686f0b9 --- /dev/null +++ b/boards/cuav/x25-evo/src/hw_config.h @@ -0,0 +1,128 @@ +/* + * hw_config.h + * + * Created on: May 17, 2015 + * Author: david_s5 + */ + +#ifndef HW_CONFIG_H_ +#define HW_CONFIG_H_ + +/**************************************************************************** + * 10-8--2016: + * To simplify the ripple effect on the tools, we will be using + * /dev/serial/by-id/PX4 to locate PX4 devices. Therefore + * moving forward all Bootloaders must contain the prefix "PX4 BL " + * in the USBDEVICESTRING + * This Change will be made in an upcoming BL release + ****************************************************************************/ +/* + * Define usage to configure a bootloader + * + * + * Constant example Usage + * APP_LOAD_ADDRESS 0x08004000 - The address in Linker Script, where the app fw is org-ed + * BOOTLOADER_DELAY 5000 - Ms to wait while under USB pwr or bootloader request + * BOARD_FMUV2 + * INTERFACE_USB 1 - (Optional) Scan and use the USB interface for bootloading + * INTERFACE_USART 1 - (Optional) Scan and use the Serial interface for bootloading + * USBDEVICESTRING "PX4 BL FMU v2.x" - USB id string + * USBPRODUCTID 0x0011 - PID Should match defconfig + * BOOT_DELAY_ADDRESS 0x000001a0 - (Optional) From the linker script from Linker Script to get a custom + * delay provided by an APP FW + * BOARD_TYPE 9 - Must match .prototype boad_id + * _FLASH_KBYTES (*(uint16_t *)0x1fff7a22) - Run time flash size detection + * BOARD_FLASH_SECTORS ((_FLASH_KBYTES == 0x400) ? 11 : 23) - Run time determine the physical last sector + * BOARD_FLASH_SECTORS 11 - Hard coded zero based last sector + * BOARD_FLASH_SIZE (_FLASH_KBYTES*1024)- Total Flash size of device, determined at run time. + * (1024 * 1024) - Hard coded Total Flash of device - The bootloader and app reserved will be deducted + * programmatically + * + * BOARD_FIRST_FLASH_SECTOR_TO_ERASE 2 - Optional sectors index in the flash_sectors table (F4 only), to begin erasing. + * This is to allow sectors to be reserved for app fw usage. That will NOT be erased + * during a FW upgrade. + * The default is 0, and selects the first sector to be erased, as the 0th entry in the + * flash_sectors table. Which is the second physical sector of FLASH in the device. + * The first physical sector of FLASH is used by the bootloader, and is not defined + * in the table. + * + * APP_RESERVATION_SIZE (BOARD_FIRST_FLASH_SECTOR_TO_ERASE * 16 * 1024) - Number of bytes reserved by the APP FW. This number plus + * BOOTLOADER_RESERVATION_SIZE will be deducted from + * BOARD_FLASH_SIZE to determine the size of the App FW + * and hence the address space of FLASH to erase and program. + * USBMFGSTRING "PX4 AP" - Optional USB MFG string (default is '3D Robotics' if not defined.) + * SERIAL_BREAK_DETECT_DISABLED - Optional prevent break selection on Serial port from entering or staying in BL + * + * * Other defines are somewhat self explanatory. + */ + +/* Boot device selection list*/ +#define USB0_DEV 0x01 +#define SERIAL0_DEV 0x02 +#define SERIAL1_DEV 0x04 + +#define APP_LOAD_ADDRESS 0x08020000 +#define BOOTLOADER_DELAY 5000 +#define INTERFACE_USB 1 +#define INTERFACE_USB_CONFIG "/dev/ttyACM0" +#define BOARD_VBUS MK_GPIO_INPUT(GPIO_OTGFS_VBUS) + +//#define USE_VBUS_PULL_DOWN +#define INTERFACE_USART 1 +#define INTERFACE_USART_CONFIG "/dev/ttyS0,1500000" +#define BOOT_DELAY_ADDRESS 0x000001a0 +#define BOARD_TYPE 7002 +#define _FLASH_KBYTES (*(uint32_t *)0x1FF1E880) +#define BOARD_FLASH_SECTORS (15) +#define BOARD_FLASH_SIZE (_FLASH_KBYTES * 1024) + +#define OSC_FREQ 16 + +#define BOARD_PIN_LED_ACTIVITY GPIO_nLED_BLUE // BLUE +#define BOARD_PIN_LED_BOOTLOADER GPIO_nLED_GREEN // GREEN +#define BOARD_LED_ON 0 +#define BOARD_LED_OFF 1 + +#define SERIAL_BREAK_DETECT_DISABLED 1 + +/* + * Uncommenting this allows to force the bootloader through + * a PWM output pin. As this can accidentally initialize + * an ESC prematurely, it is not recommended. This feature + * has not been used and hence defaults now to off. + * + * # define BOARD_FORCE_BL_PIN_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN14) + * # define BOARD_FORCE_BL_PIN_IN (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTE|GPIO_PIN11) + * + * # define BOARD_POWER_PIN_OUT (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_SET|GPIO_PORTA|GPIO_PIN4) + * # define BOARD_POWER_ON 1 + * # define BOARD_POWER_OFF 0 + * # undef BOARD_POWER_PIN_RELEASE // Leave pin enabling Power - un comment to release (disable power) + * +*/ + +#if !defined(ARCH_SN_MAX_LENGTH) +# define ARCH_SN_MAX_LENGTH 12 +#endif + +#if !defined(APP_RESERVATION_SIZE) +# define APP_RESERVATION_SIZE 0 +#endif + +#if !defined(BOARD_FIRST_FLASH_SECTOR_TO_ERASE) +# define BOARD_FIRST_FLASH_SECTOR_TO_ERASE 1 +#endif + +#if !defined(USB_DATA_ALIGN) +# define USB_DATA_ALIGN +#endif + +#ifndef BOOT_DEVICES_SELECTION +# define BOOT_DEVICES_SELECTION USB0_DEV|SERIAL0_DEV|SERIAL1_DEV +#endif + +#ifndef BOOT_DEVICES_FILTER_ONUSB +# define BOOT_DEVICES_FILTER_ONUSB USB0_DEV|SERIAL0_DEV|SERIAL1_DEV +#endif + +#endif /* HW_CONFIG_H_ */ diff --git a/boards/cuav/x25-evo/src/i2c.cpp b/boards/cuav/x25-evo/src/i2c.cpp new file mode 100644 index 00000000000..153819ca5de --- /dev/null +++ b/boards/cuav/x25-evo/src/i2c.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * Copyright (C) 2025 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), + initI2CBusInternal(3), + initI2CBusInternal(4), +}; diff --git a/boards/cuav/x25-evo/src/init.c b/boards/cuav/x25-evo/src/init.c new file mode 100644 index 00000000000..5a026b0b7b4 --- /dev/null +++ b/boards/cuav/x25-evo/src/init.c @@ -0,0 +1,277 @@ +/**************************************************************************** + * + * Copyright (c) 2012-2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file init.c + * + * PX4FMU-specific early startup code. This file implements the + * board_app_initialize() function that is called early by nsh during startup. + * + * Code here is run before the rcS script is invoked; it should start required + * subsystems and perform board-specific initialization. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "board_config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arm_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* + * Ideally we'd be able to get these from arm_internal.h, + * but since we want to be able to disable the NuttX use + * of leds for system indication at will and there is no + * separate switch, we need to build independent of the + * CONFIG_ARCH_LEDS configuration switch. + */ +__BEGIN_DECLS +extern void led_init(void); +extern void led_on(int led); +extern void led_off(int led); +__END_DECLS + + +/************************************************************************************ + * Name: board_peripheral_reset + * + * Description: + * + ************************************************************************************/ +__EXPORT void board_peripheral_reset(int ms) +{ + /* set the peripheral rails off */ + + VDD_5V_PERIPH_EN(false); + VDD_3V3_SENSORS3_EN(false); + VDD_3V3_SENSORS4_EN(false); + board_control_spi_sensors_power(false, 0xffff); + + /* wait for the peripheral rail to reach GND */ + usleep(ms * 1000); + syslog(LOG_DEBUG, "reset done, %d ms\n", ms); + + /* re-enable power */ + + /* switch the peripheral rail back on */ + board_control_spi_sensors_power(true, 0xffff); + VDD_5V_PERIPH_EN(true); + VDD_3V3_SENSORS3_EN(true); + VDD_3V3_SENSORS4_EN(true); + +} + +/************************************************************************************ + * Name: board_on_reset + * + * Description: + * Optionally provided function called on entry to board_system_reset + * It should perform any house keeping prior to the rest. + * + * status - 1 if resetting to boot loader + * 0 if just resetting + * + ************************************************************************************/ +__EXPORT void board_on_reset(int status) +{ + for (int i = 0; i < DIRECT_PWM_OUTPUT_CHANNELS; ++i) { + px4_arch_configgpio(io_timer_channel_get_gpio_output(i)); + } + + 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) +{ + board_on_reset(-1); /* Reset PWM first thing */ + + /* configure LEDs */ + + board_autoled_initialize(); + + /* configure pins */ + + const uint32_t gpio[] = PX4_GPIO_INIT_LIST; + px4_gpio_init(gpio, arraySize(gpio)); + + /* configure USB interfaces */ + + stm32_usbinitialize(); + + VDD_3V3_ETH_POWER_EN(true); + +} + +/**************************************************************************** + * Name: board_app_initialize + * + * Description: + * Perform application specific initialization. This function is never + * called directly from application code, but only indirectly via the + * (non-standard) boardctl() interface using the command BOARDIOC_INIT. + * + * Input Parameters: + * arg - The boardctl() argument is passed to the board_app_initialize() + * implementation without modification. The argument has no + * meaning to NuttX; the meaning of the argument is a contract + * between the board-specific initalization logic and the the + * matching application logic. The value cold be such things as a + * mode enumeration value, a set of DIP switch switch settings, a + * pointer to configuration data read from a file or serial FLASH, + * or whatever you would like to do with it. Every implementation + * should accept zero/NULL as a default configuration. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +__EXPORT int board_app_initialize(uintptr_t arg) +{ +#if !defined(BOOTLOADER) + + /* Power on Interfaces */ + VDD_3V3_SD_CARD_EN(true); + VDD_5V_PERIPH_EN(true); + VDD_5V_HIPOWER_EN(true); + VDD_PWM_POWER_EN(true); + VDD_3V3_SENSORS3_EN(true); + VDD_3V3_SENSORS4_EN(true); + VDD_5V_RC_EN(true); + + /* Need hrt running before using the ADC */ + + px4_platform_init(); + + // Use the default HW_VER_REV(0x0,0x0) for Ramtron + + stm32_spiinitialize(); + + /* Configure the HW based on the manifest */ + + px4_platform_configure(); + + /* Configure the Actual SPI interfaces (after we determined the HW version) */ + + stm32_spiinitialize(); + + board_spi_reset(10, 0xffff); + + /* Configure the DMA allocator */ + + if (board_dma_alloc_init() < 0) { + syslog(LOG_ERR, "[boot] DMA alloc FAILED\n"); + } + +# if defined(SERIAL_HAVE_RXDMA) + // set up the serial DMA polling at 1ms intervals for received bytes that have not triggered a DMA event. + static struct hrt_call serial_dma_call; + hrt_call_every(&serial_dma_call, 1000, 1000, (hrt_callout)stm32_serial_dma_poll, NULL); +# endif + + /* initial LED state */ + drv_led_start(); + led_off(LED_RED); + led_on(LED_GREEN); // Indicate Power. + led_off(LED_BLUE); + + if (board_hardfault_init(2, true) != 0) { + led_on(LED_RED); + } + + // Ensure Power is off for > 10 mS + usleep(15 * 1000); + VDD_3V3_SD_CARD_EN(true); + usleep(500 * 1000); + +# ifdef CONFIG_MMCSD + int ret = stm32_sdio_initialize(); + + if (ret != OK) { + led_on(LED_RED); + return ret; + } + +# endif /* CONFIG_MMCSD */ + +#endif /* !defined(BOOTLOADER) */ + + return OK; +} diff --git a/boards/cuav/x25-evo/src/led.c b/boards/cuav/x25-evo/src/led.c new file mode 100644 index 00000000000..74f1a65ffd6 --- /dev/null +++ b/boards/cuav/x25-evo/src/led.c @@ -0,0 +1,236 @@ +/**************************************************************************** + * + * Copyright (c) 2025 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 fmu_led.c + * + * PX4FMU LED backend. + */ + +#include + +#include + +#include "chip.h" +#include "stm32_gpio.h" +#include "board_config.h" + +#include +#include + +/* + * Ideally we'd be able to get these from arm_internal.h, + * but since we want to be able to disable the NuttX use + * of leds for system indication at will and there is no + * separate switch, we need to build independent of the + * CONFIG_ARCH_LEDS configuration switch. + */ +__BEGIN_DECLS +extern void led_init(void); +extern void led_on(int led); +extern void led_off(int led); +extern void led_toggle(int led); +__END_DECLS + +#ifdef CONFIG_ARCH_LEDS +static bool nuttx_owns_leds = true; +// B R S G +// 0 1 2 3 +static const uint8_t xlatpx4[] = {1, 2, 4, 0}; +# define xlat(p) xlatpx4[(p)] +static uint32_t g_ledmap[] = { + GPIO_nLED_GREEN, // Indexed by BOARD_LED_GREEN + GPIO_nLED_BLUE, // Indexed by BOARD_LED_BLUE + GPIO_nLED_RED, // Indexed by BOARD_LED_RED + GPIO_nSAFETY_SWITCH_LED_OUT, // Indexed by LED_SAFETY by xlatpx4 +}; + +#else + +# define xlat(p) (p) +static uint32_t g_ledmap[] = { + GPIO_nLED_BLUE, // Indexed by LED_BLUE + GPIO_nLED_RED, // Indexed by LED_RED, LED_AMBER + GPIO_nSAFETY_SWITCH_LED_OUT, // Indexed by LED_SAFETY (defaulted to an input) + GPIO_nLED_GREEN, // Indexed by LED_GREEN +}; + +#endif + +__EXPORT void led_init(void) +{ + + for (size_t l = 0; l < (sizeof(g_ledmap) / sizeof(g_ledmap[0])); l++) { + if (g_ledmap[l] != 0) { + stm32_configgpio(g_ledmap[l]); + } + } +} + +static void phy_set_led(int led, bool state) +{ + /* Drive Low to switch on */ + + if (g_ledmap[led] != 0) { + stm32_gpiowrite(g_ledmap[led], !state); + } +} + +static bool phy_get_led(int led) +{ + /* If Low it is on */ + if (g_ledmap[led] != 0) { + return !stm32_gpioread(g_ledmap[led]); + } + + return false; +} + +__EXPORT void led_on(int led) +{ + phy_set_led(xlat(led), true); +} + +__EXPORT void led_off(int led) +{ + phy_set_led(xlat(led), false); +} + +__EXPORT void led_toggle(int led) +{ + phy_set_led(xlat(led), !phy_get_led(xlat(led))); +} + +#ifdef CONFIG_ARCH_LEDS +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_autoled_initialize + ****************************************************************************/ + +void board_autoled_initialize(void) +{ + led_init(); +} + +/**************************************************************************** + * Name: board_autoled_on + ****************************************************************************/ + +void board_autoled_on(int led) +{ + if (!nuttx_owns_leds) { + return; + } + + switch (led) { + default: + break; + + case LED_HEAPALLOCATE: + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_IRQSENABLED: + phy_set_led(BOARD_LED_BLUE, false); + phy_set_led(BOARD_LED_GREEN, true); + break; + + case LED_STACKCREATED: + phy_set_led(BOARD_LED_GREEN, true); + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_INIRQ: + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_SIGNAL: + phy_set_led(BOARD_LED_GREEN, true); + break; + + case LED_ASSERTION: + phy_set_led(BOARD_LED_RED, true); + phy_set_led(BOARD_LED_BLUE, true); + break; + + case LED_PANIC: + phy_set_led(BOARD_LED_RED, true); + break; + + case LED_IDLE : /* IDLE */ + phy_set_led(BOARD_LED_RED, true); + break; + } +} + +/**************************************************************************** + * Name: board_autoled_off + ****************************************************************************/ + +void board_autoled_off(int led) +{ + if (!nuttx_owns_leds) { + return; + } + + switch (led) { + default: + break; + + case LED_SIGNAL: + phy_set_led(BOARD_LED_GREEN, false); + break; + + case LED_INIRQ: + phy_set_led(BOARD_LED_BLUE, false); + break; + + case LED_ASSERTION: + phy_set_led(BOARD_LED_RED, false); + phy_set_led(BOARD_LED_BLUE, false); + break; + + case LED_PANIC: + phy_set_led(BOARD_LED_RED, false); + break; + + case LED_IDLE : /* IDLE */ + phy_set_led(BOARD_LED_RED, false); + break; + } +} + +#endif /* CONFIG_ARCH_LEDS */ diff --git a/boards/cuav/x25-evo/src/mtd.cpp b/boards/cuav/x25-evo/src/mtd.cpp new file mode 100644 index 00000000000..6e4188e2652 --- /dev/null +++ b/boards/cuav/x25-evo/src/mtd.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** + * + * Copyright (C) 2025 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 +#include +// KiB BS nB +static const px4_mft_device_t spi5 = { // FM25V05A on FMUM native: 64K X 8, emulated as (1024 Blocks of 64) + .bus_type = px4_mft_device_t::SPI, + .devid = SPIDEV_FLASH(0) +}; +static const px4_mft_device_t i2c4 = { // 24LC64T on IMU 8K 32 X 256 + .bus_type = px4_mft_device_t::I2C, + .devid = PX4_MK_I2C_DEVID(4, 0x50) +}; + +static const px4_mtd_entry_t fmum_fram = { + .device = &spi5, + .npart = 1, + .partd = { + { + .type = MTD_PARAMETERS, + .path = "/fs/mtd_params", + .nblocks = (65536 / (1 << CONFIG_RAMTRON_EMULATE_SECTOR_SHIFT)) + } + }, +}; + +static const px4_mtd_entry_t imu_eeprom = { + .device = &i2c4, + .npart = 2, + .partd = { + { + .type = MTD_CALDATA, + .path = "/fs/mtd_caldata", + .nblocks = 248 + }, + { + .type = MTD_NET, + .path = "/fs/mtd_net", + .nblocks = 8 // 256 = 32 * 8 + } + }, +}; + +static const px4_mtd_manifest_t board_mtd_config = { + .nconfigs = 2, + .entries = { + &fmum_fram, + &imu_eeprom + } +}; + +static const px4_mft_entry_s mtd_mft = { + .type = MTD, + .pmft = (void *) &board_mtd_config, +}; + + +static const px4_mft_s mft = { + .nmft = 1, + .mfts = { + &mtd_mft, + } +}; + +const px4_mft_s *board_get_manifest(void) +{ + return &mft; +} diff --git a/boards/cuav/x25-evo/src/sdio.c b/boards/cuav/x25-evo/src/sdio.c new file mode 100644 index 00000000000..20023819189 --- /dev/null +++ b/boards/cuav/x25-evo/src/sdio.c @@ -0,0 +1,177 @@ +/**************************************************************************** + * + * Copyright (C) 2014, 2025 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "board_config.h" +#include "stm32_gpio.h" +#include "stm32_sdmmc.h" + +#ifdef CONFIG_MMCSD + + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Card detections requires card support and a card detection GPIO */ + +#define HAVE_NCD 1 +#if !defined(GPIO_SDMMC1_NCD) +# undef HAVE_NCD +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR struct sdio_dev_s *sdio_dev; +#ifdef HAVE_NCD +static bool g_sd_inserted = 0xff; /* Impossible value */ +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_ncd_interrupt + * + * Description: + * Card detect interrupt handler. + * + ****************************************************************************/ + +#ifdef HAVE_NCD +static int stm32_ncd_interrupt(int irq, FAR void *context) +{ + bool present; + + present = !stm32_gpioread(GPIO_SDMMC1_NCD); + + if (sdio_dev && present != g_sd_inserted) { + sdio_mediachange(sdio_dev, present); + g_sd_inserted = present; + } + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_sdio_initialize + * + * Description: + * Initialize SDIO-based MMC/SD card support + * + ****************************************************************************/ + +int stm32_sdio_initialize(void) +{ + int ret; + +#ifdef HAVE_NCD + /* Card detect */ + + bool cd_status; + + /* Configure the card detect GPIO */ + + stm32_configgpio(GPIO_SDMMC1_NCD); + + /* Register an interrupt handler for the card detect pin */ + + stm32_gpiosetevent(GPIO_SDMMC1_NCD, true, true, true, stm32_ncd_interrupt); +#endif + + /* Mount the SDIO-based MMC/SD block driver */ + /* First, get an instance of the SDIO interface */ + + finfo("Initializing SDIO slot %d\n", SDIO_SLOTNO); + + sdio_dev = sdio_initialize(SDIO_SLOTNO); + + if (!sdio_dev) { + syslog(LOG_ERR, "[boot] Failed to initialize SDIO slot %d\n", SDIO_SLOTNO); + return -ENODEV; + } + + /* Now bind the SDIO interface to the MMC/SD driver */ + + finfo("Bind SDIO to the MMC/SD driver, minor=%d\n", SDIO_MINOR); + + ret = mmcsd_slotinitialize(SDIO_MINOR, sdio_dev); + + if (ret != OK) { + syslog(LOG_ERR, "[boot] Failed to bind SDIO to the MMC/SD driver: %d\n", ret); + return ret; + } + + finfo("Successfully bound SDIO to the MMC/SD driver\n"); + +#ifdef HAVE_NCD + /* Use SD card detect pin to check if a card is g_sd_inserted */ + + cd_status = !stm32_gpioread(GPIO_SDMMC1_NCD); + finfo("Card detect : %d\n", cd_status); + + sdio_mediachange(sdio_dev, cd_status); +#else + /* Assume that the SD card is inserted. What choice do we have? */ + + sdio_mediachange(sdio_dev, true); +#endif + + return OK; +} + +#endif /* CONFIG_MMCSD */ diff --git a/boards/cuav/x25-evo/src/spi.cpp b/boards/cuav/x25-evo/src/spi.cpp new file mode 100644 index 00000000000..2857860b572 --- /dev/null +++ b/boards/cuav/x25-evo/src/spi.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** + * + * Copyright (C) 2020, 2025 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_IIM42652, SPI::CS{GPIO::PortI, GPIO::Pin12}, SPI::DRDY{GPIO::PortJ, GPIO::Pin7}), + }, {GPIO::PortF, GPIO::Pin4}), + initSPIBus(SPI::Bus::SPI2, { + initSPIDevice(DRV_IMU_DEVTYPE_IIM42653, SPI::CS{GPIO::PortH, GPIO::Pin5}, SPI::DRDY{GPIO::PortG, GPIO::Pin3}), + }, {GPIO::PortI, GPIO::Pin4}), + initSPIBus(SPI::Bus::SPI4, { + initSPIDevice(DRV_MAG_DEVTYPE_RM3100, SPI::CS{GPIO::PortH, GPIO::Pin15}, SPI::DRDY{GPIO::PortG, GPIO::Pin1}), + initSPIDevice(DRV_BARO_DEVTYPE_BMP581, SPI::CS{GPIO::PortJ, GPIO::Pin6}, SPI::DRDY{GPIO::PortJ, GPIO::Pin0}), + }, {GPIO::PortJ, GPIO::Pin4}), + initSPIBus(SPI::Bus::SPI5, { + initSPIDevice(DRV_IMU_DEVTYPE_IIM42653, SPI::CS{GPIO::PortJ, GPIO::Pin12}, SPI::DRDY{GPIO::PortG, GPIO::Pin6}), + initSPIDevice(SPIDEV_FLASH(0), SPI::CS{GPIO::PortG, GPIO::Pin7}), + }, {GPIO::PortI, GPIO::Pin11}), + initSPIBusExternal(SPI::Bus::SPI6, { + initSPIConfigExternal(SPI::CS{GPIO::PortI, GPIO::Pin10}, SPI::DRDY{GPIO::PortD, GPIO::Pin11}), + }), +}; + +static constexpr bool unused = validateSPIConfig(px4_spi_buses); diff --git a/boards/cuav/x25-evo/src/timer_config.cpp b/boards/cuav/x25-evo/src/timer_config.cpp new file mode 100644 index 00000000000..76afbbcbaf8 --- /dev/null +++ b/boards/cuav/x25-evo/src/timer_config.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include + +/* Timer allocation + * + * TIM5_CH4 T FMU_CH1 PI0 + * TIM5_CH3 T FMU_CH2 PH12 + * TIM5_CH2 T FMU_CH3 PH11 + * TIM5_CH1 T FMU_CH4 PH10 + * TIM4_CH2 T FMU_CH5 PD13 + * TIM4_CH3 T FMU_CH6 PD14 + * TIM4_CH1 T FMU_CH7 PD12 + * TIM4_CH4 T FMU_CH8 PD15 + * + * + * TIM1_CH2 T FMU_CAP1 < Capture PE11 + * TIM1_CH1 T FMU_CAP2 < Capture PE9 + * TIM1_CH3 T FMU_CAP3 < Capture PJ9 + * TIM8_CH2 T FMU_CAP4 < Capture PI6 + * TIM8_CH3 T FMU_CAP5 < Capture PI7 + * TIM8_CH1 T FMU_CAP6 < Capture PI5 + * TIM12_CH1 T FMU_CAP7 < Capture PH6 + * TIM12_CH2 T FMU_CAP8 < Capture PH9 + * + * + * TIM14_CH1 T BUZZER_1 - Driven by other driver + * TIM8_CH1_IN T FMU_PPM_INPUT - Sampled byt HRT by other driver + */ + +constexpr io_timers_t io_timers[MAX_IO_TIMERS] = { + initIOTimer(Timer::Timer5, DMA{DMA::Index1}), + initIOTimer(Timer::Timer4, DMA{DMA::Index1}), + initIOTimer(Timer::Timer1), + initIOTimer(Timer::Timer8), + initIOTimer(Timer::Timer12), +}; + +constexpr timer_io_channels_t timer_io_channels[MAX_TIMER_IO_CHANNELS] = { + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel4}, {GPIO::PortI, GPIO::Pin0}), + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel3}, {GPIO::PortH, GPIO::Pin12}), + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel2}, {GPIO::PortH, GPIO::Pin11}), + initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel1}, {GPIO::PortH, GPIO::Pin10}), + initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel2}, {GPIO::PortD, GPIO::Pin13}), + initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel3}, {GPIO::PortD, GPIO::Pin14}), + initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel1}, {GPIO::PortD, GPIO::Pin12}), + initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel4}, {GPIO::PortD, GPIO::Pin15}), + initIOTimerChannelCapture(io_timers, {Timer::Timer1, Timer::Channel2}, {GPIO::PortE, GPIO::Pin11}), + initIOTimerChannelCapture(io_timers, {Timer::Timer1, Timer::Channel1}, {GPIO::PortE, GPIO::Pin9}), + initIOTimerChannelCapture(io_timers, {Timer::Timer1, Timer::Channel3}, {GPIO::PortJ, GPIO::Pin9}), + initIOTimerChannelCapture(io_timers, {Timer::Timer8, Timer::Channel2}, {GPIO::PortI, GPIO::Pin6}), + initIOTimerChannelCapture(io_timers, {Timer::Timer8, Timer::Channel3}, {GPIO::PortI, GPIO::Pin7}), + initIOTimerChannelCapture(io_timers, {Timer::Timer8, Timer::Channel1}, {GPIO::PortI, GPIO::Pin5}), + initIOTimerChannelCapture(io_timers, {Timer::Timer12, Timer::Channel1}, {GPIO::PortH, GPIO::Pin6}), + initIOTimerChannelCapture(io_timers, {Timer::Timer12, Timer::Channel2}, {GPIO::PortH, GPIO::Pin9}), +}; + +constexpr io_timers_channel_mapping_t io_timers_channel_mapping = + initIOTimerChannelMapping(io_timers, timer_io_channels); diff --git a/boards/cuav/x25-evo/src/usb.c b/boards/cuav/x25-evo/src/usb.c new file mode 100644 index 00000000000..9950272eaff --- /dev/null +++ b/boards/cuav/x25-evo/src/usb.c @@ -0,0 +1,105 @@ +/**************************************************************************** + * + * Copyright (C) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file px4fmu_usb.c + * + * Board-specific USB functions. + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "board_config.h" + +/************************************************************************************ + * Definitions + ************************************************************************************/ + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_usbinitialize + * + * Description: + * Called to setup USB-related GPIO pins for the PX4FMU board. + * + ************************************************************************************/ + +__EXPORT void stm32_usbinitialize(void) +{ + /* The OTG FS has an internal soft pull-up */ + + /* Configure the OTG FS VBUS sensing GPIO, Power On, and Overcurrent GPIOs */ + +#ifdef CONFIG_STM32H7_OTGFS + stm32_configgpio(GPIO_OTGFS_VBUS); +#endif +} + +/************************************************************************************ + * Name: stm32_usbsuspend + * + * Description: + * Board logic must provide the stm32_usbsuspend logic if the USBDEV driver is + * used. This function is called whenever the USB enters or leaves suspend mode. + * This is an opportunity for the board logic to shutdown clocks, power, etc. + * while the USB is suspended. + * + ************************************************************************************/ + +__EXPORT void stm32_usbsuspend(FAR struct usbdev_s *dev, bool resume) +{ + uinfo("resume: %d\n", resume); +} diff --git a/docs/assets/flight_controller/cuav_x25-evo/X25-EVO.jpg b/docs/assets/flight_controller/cuav_x25-evo/X25-EVO.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bc68ecb20dd05ac7f6a563758869e6854b69e4ae GIT binary patch literal 66957 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 literal 0 HcmV?d00001 diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index b63f062e8b6..97d1e1d63a4 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -167,6 +167,7 @@ - [Wiring Quickstart](assembly/quick_start_cuav_v5_plus.md) - [CUAV V5 nano (FMUv5)](flight_controller/cuav_v5_nano.md) - [CUAV V5 nano Wiring Quickstart](assembly/quick_start_cuav_v5_nano.md) + - [CUAV X25 EVO](flight_controller/cuav_x25-evo.md) - [CubePilot Cube Orange+ (CubePilot)](flight_controller/cubepilot_cube_orangeplus.md) - [CubePilot Cube Orange (CubePilot)](flight_controller/cubepilot_cube_orange.md) - [CubePilot Cube Yellow (CubePilot)](flight_controller/cubepilot_cube_yellow.md) diff --git a/docs/en/flight_controller/autopilot_manufacturer_supported.md b/docs/en/flight_controller/autopilot_manufacturer_supported.md index 6889952b291..cb882028756 100644 --- a/docs/en/flight_controller/autopilot_manufacturer_supported.md +++ b/docs/en/flight_controller/autopilot_manufacturer_supported.md @@ -20,6 +20,7 @@ The boards in this category are: - [CUAV Nora](../flight_controller/cuav_nora.md)(CUAV X7 variant) - [CUAV V5+](../flight_controller/cuav_v5_plus.md) (FMUv5) - [CUAV V5 nano](../flight_controller/cuav_v5_nano.md) (FMUv5) +- [CUAV X25 EVO](../flight_controller/cuav_x25-evo.md) - [CubePilot Cube Orange+](../flight_controller/cubepilot_cube_orangeplus.md) - [CubePilot Cube Orange](../flight_controller/cubepilot_cube_orange.md) - [CubePilot Cube Yellow](../flight_controller/cubepilot_cube_yellow.md) diff --git a/docs/en/flight_controller/cuav_x25-evo.md b/docs/en/flight_controller/cuav_x25-evo.md new file mode 100644 index 00000000000..2d53c32fbd2 --- /dev/null +++ b/docs/en/flight_controller/cuav_x25-evo.md @@ -0,0 +1,161 @@ +# CUAV X25-EVO + +:::warning +PX4 does not manufacture this (or any) autopilot. +Contact the [manufacturer](https://store.cuav.net/) for hardware support or compliance issues. +::: + +The _X25-EVO_ is an advanced autopilot manufactured by CUAV®. + +The autopilot is recommended for commercial system integration but is also suitable for academic research and any other applications. + +![X25-EVO AutoPilot - hero image](../../assets/flight_controller/cuav_x25-evo/X25-EVO.jpg) + +The X25-EVO brings you ultimate performance, stability, and reliability in every aspect. + +::: info +These flight controllers are [manufacturer supported](../flight_controller/autopilot_manufacturer_supported.md). +::: + +### Features + +- Arm® Cortex-M7® processor (STM32H743XI) with Floating-Point Unit (FPU), operating at 480MHz, and featuring 2MB Flash memory. Enables developers to enhance productivity and efficiency, allowing for more complex algorithms and models. +- Automotive-grade RM3100 compass. Designed for better stability and anti-interference capability. +- Triple-redundant IMUs and dual-redundant barometers located on separate buses. If the PX4 autopilot detects a sensor failure, the system seamlessly switches to another sensor to maintain flight control reliability. +- Independent LDO power control supplies power to each sensor group. A vibration isolation system filters high-frequency vibrations and reduces noise to ensure accurate readings, enabling better overall flight performance for the vehicle. +- Integrated Microchip Ethernet PHY for high-speed communication with onboard devices like mission computers via Ethernet. +- Dual temperature compensation systems, located on the IMU board and FMU board respectively. Temperature is controlled by onboard heating resistors to achieve the optimal operating temperature for the IMUs. +- PWM servo output voltage switchable between 3.3V or 5V. +- Modular design for DIY carrier boards. + +### Processors & Sensors + +- Main Processor: STM32H743XI + - 32-bit Arm® Cortex®-M7, 480MHz, 2MB Flash, 1MB RAM +- Onboard Sensors: + - Accel/Gyro: IIM42652\*2 + - Accel/Gyro: IIM42653 + - Magnetometer: RM3100 + - Barometer: BMP581 + - Barometer: ICP-20100 + +### Electrical Data + +- Rated Voltage: + - Input Voltage: 10~18V + - USB Power Input: 4.75~5.25V + - Servo Rail Input: 0~9.9V +- Rated Current: + - Total Output Max Current: 10A + - TELEM1 and TELEM2 Output Current limiter: 4A + - CAN1 and CAN2 Output Current limiter: 2.4A + - Other Ports Output Current limiter: 1.5A + +### Interfaces + +- 16x PWM Servo Outputs +- 1x Dedicated R/C Input for Spektrum / DSM and S.Bus +- 1x Analog/PWM RSSI Input +- 2x TELEM Ports (with full flow control) +- 1x UART4 Port +- 2x GPS Ports + - 1x Full GPS plus Safety Switch Port (GPS1) + - 1x Basic GPS Port (with I2C, GPS2) +- 1x USB Port (TYPE-C) +- 1x Ethernet Port + - Transformerless application + - 100Mbps +- 3x I2C Bus Ports +- 1x SPI Bus + - 1x Chip Select Line + - 1x Data Ready Line + - 1x SPI Reset Line +- 5x CAN Ports for CAN Peripherals + - 3x CAN1 Bus Multiplexed Ports + - 2x CAN2 Bus Multiplexed Ports +- 2x Power Input Ports + - DroneCAN/UAVCAN Power Input +- 2x AD Ports + - Analog Input (3.3V) + - Analog Input (6.6V - not supported by PX4) +- 1x Dedicated Debug Port + - FMU Debug + +### Mechanical Data + + - Not provided. + +## Purchase Channels + +Order from [CUAV](https://store.cuav.net/). + +## Assembly/Setup + + - Not provided. + +## Pin Definitions + + - Not provided. + +## Serial Port Mapping + +| UART | Device | Port | +| ------ | ---------- | ------------- | +| USART1 | /dev/ttyS0 | GPS1 | +| USART2 | /dev/ttyS1 | GPS2 | +| USART3 | /dev/ttyS2 | Debug Console | +| UART4 | /dev/ttyS3 | UART4 | +| UART5 | /dev/ttyS4 | TELEM2 | +| USART6 | /dev/ttyS5 | RC | +| UART7 | /dev/ttyS6 | TELEM1 | + +## Voltage Ratings + +The _X25-EVO_ achieves triple redundancy on power supplies if three power sources are provided. The three power rails are POWERC1, POWERC2, and USB. +- **POWER C1** and **POWER C2** are DroneCAN/UAVCAN battery interfaces. + +**Normal Operation Maximum Ratings** + +Under these conditions, all power sources will be used to power the system in the following order: +1. **POWER C1** and **POWER C2** Inputs (10V to 18V) +2. USB Input (4.75V to 5.25V) + +**Voltage monitoring** + +Digital DroneCAN/UAVCAN battery monitoring is enabled by default. + +## Building Firmware + +:::tip +Most users do not need to build this firmware! +It is pre-built and installed automatically by _QGroundControl_ when the appropriate hardware is connected. +::: + +To [build PX4](../dev_setup/building_px4.md) for this target, execute: + +``` +make cuav_x25-evo_default +``` + + + +## Debug Port + +The [PX4 System Console](../debug/system_console.md) and [SWD Interface](../debug/swd_debug.md) operate on the **FMU Debug** port. + +| Pin | Signal | Volt | +| -------- | ---------------- | ----- | +| 1 (red) | 5V+ | +5V | +| 2 (blk) | DEBUG TX (OUT) | +3.3V | +| 3 (blk) | DEBUG RX (IN) | +3.3V | +| 4 (blk) | FMU_SWDIO | +3.3V | +| 5 (blk) | FMU_SWCLK | +3.3V | +| 6 (blk) | GND | GND | + +## Supported Platforms / Airframes + +Any multirotor/airplane/rover or boat that can be controlled using normal RC servos or Futaba S-Bus servos. The complete set of supported configurations can be found in the [Airframe Reference](../airframes/airframe_reference.md). + +## Further info + +- [CUAV Docs](https://doc.cuav.net/)