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 a938bde2fa..c75ef38326 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 7e45de7b92..ca8392b43f 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 0000000000..421ff0e988 --- /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 0000000000..19b6e662be --- /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 0000000000..71d0a62541 --- /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 0000000000..7fdaa6d89e --- /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 0000000000..0656504cba --- /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 0000000000..6ac34ad53a --- /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 0000000000..a19eb6d016 --- /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 0000000000..a5cd2829ca --- /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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's_>)@)Ae~Y6d-Hm^7;kh*liF;; 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 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 0000000000..98e18e27be --- /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 0000000000..8ef1ad0b40 --- /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 0000000000..cc42ea6f01 --- /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 0000000000..bb33d3cfda --- /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 0000000000..e2527d322b --- /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 0000000000..4c544f6086 --- /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 0000000000..03d028fa14 --- /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 0000000000..6c576f1a9d --- /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 0000000000..4484463860 --- /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 0000000000..5790900708 --- /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 0000000000..6db1696208 --- /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 0000000000..728bcc7a55 --- /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 0000000000..2bef9ae0f6 --- /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 0000000000..a55550c0e5 --- /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 0000000000..beb675064b --- /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 0000000000..34cf9f6e8b --- /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 0000000000..4cdbcbc2f3 --- /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 0000000000..3703686f0b --- /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 0000000000..153819ca5d --- /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 0000000000..5a026b0b7b --- /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 0000000000..74f1a65ffd --- /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 0000000000..6e4188e265 --- /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 0000000000..2002381918 --- /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 0000000000..2857860b57 --- /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 0000000000..76afbbcbaf --- /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 0000000000..9950272eaf --- /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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 HyP5wV^KE5O literal 0 HcmV?d00001 diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index b63f062e8b..97d1e1d63a 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 6889952b29..cb88202875 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 0000000000..2d53c32fbd --- /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/)