diff --git a/Tools/teensy_uploader.py b/Tools/teensy_uploader.py new file mode 100644 index 0000000000..aad97135c3 --- /dev/null +++ b/Tools/teensy_uploader.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +############################################################################ +# +# Copyright (c) 2024 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. +# +############################################################################ + +# +# Serial firmware uploader for the Teensy + +import sys +import argparse +import time +import sys +import usb.core +import subprocess + +from sys import platform as _platform +from pymavlink import mavutil + +try: + import serial +except ImportError as e: + print("Failed to import serial: " + str(e)) + print("") + print("You may need to install it using:") + print(" pip3 install --user pyserial") + print("") + sys.exit(1) + +# Define time to use time.time() by default +def _time(): + return time.time() + +class uploader(object): + + def __init__(self, portname): + self.mavlink = mavutil.mavlink_connection(portname) + + def send_reboot(self): + try: + self.mavlink.wait_heartbeat() + self.mavlink.reboot_autopilot(True) + except: + pass + return True + +TEENSY_BL_VENDORID = 0x16c0 +TEENSY_BL_PRODUCTID = 0x0478 + +def main(): + # Parse commandline arguments + parser = argparse.ArgumentParser(description="Firmware uploader for the PX autopilot system.") + parser.add_argument('--port', action="store", required=True, help="Comma-separated list of serial port(s) to which the FMU may be attached") + parser.add_argument('--force', action='store_true', default=False, help='Override board type check, or silicon errata checks and continue loading') + parser.add_argument('--boot-delay', type=int, default=None, help='minimum boot delay to store in flash') + parser.add_argument('--vendor-id', type=lambda x: int(x,0), default=None, help='PX4 USB vendorid') + parser.add_argument('--product-id', type=lambda x: int(x,0), default=None, help='PX4 USB productid') + parser.add_argument('firmware', action="store", nargs='+', help="Firmware file(s)") + args = parser.parse_args() + + found_bootloader = False + + # find USB devices + dev = usb.core.find(idVendor=TEENSY_BL_VENDORID, idProduct=TEENSY_BL_PRODUCTID) + # loop through devices, printing vendor and product ids in decimal and hex + if dev is not None: + print("Found teensy bootloader") + found_bootloader = True + else: + dev = usb.core.find(idVendor=args.vendor_id, idProduct=args.product_id) + if dev is None: + print("No PX4 Device found try to press the button program push button") + print("Attempting to reboot into Teensy bootloader...", end="", flush=True) + + try: + while True: + portlist = [] + patterns = args.port.split(",") + # on unix-like platforms use glob to support wildcard ports. This allows + # the use of /dev/serial/by-id/usb-3D_Robotics on Linux, which prevents the upload from + # causing modem hangups etc + if "linux" in _platform or "darwin" in _platform or "cygwin" in _platform: + import glob + for pattern in patterns: + portlist += glob.glob(pattern) + else: + portlist = patterns + + for port in portlist: + try: + if "linux" in _platform: + # Linux, don't open Mac OS and Win ports + if "COM" not in port and "tty.usb" not in port: + up = uploader(port) + elif "darwin" in _platform: + # OS X, don't open Windows and Linux ports + if "COM" not in port and "ACM" not in port: + up = uploader(port) + elif "cygwin" in _platform: + # Cygwin, don't open native Windows COM and Linux ports + if "COM" not in port and "ACM" not in port: + up = uploader(port) + elif "win" in _platform: + # Windows, don't open POSIX ports + if "/" not in port: + up = uploader(port) + except Exception: + # open failed, rate-limit our attempts + time.sleep(0.05) + + # and loop to the next port + continue + + while True: + up.send_reboot() + + # wait for the reboot, without we might run into Serial I/O Error 5 + time.sleep(0.25) + + # wait for the close, without we might run into Serial I/O Error 6 + time.sleep(0.3) + + dev = usb.core.find(idVendor=TEENSY_BL_VENDORID, idProduct=TEENSY_BL_PRODUCTID) + # loop through devices, printing vendor and product ids in decimal and hex + if dev is not None: + print("") + print("Found teensy bootloader") + found_bootloader = True + break + + print('.', end="", flush=True) + + if not found_bootloader: + # Go to the next port + continue + + if(found_bootloader): + while True: + result = subprocess.Popen("teensy_loader_cli -v --mcu=TEENSY41 " + args.firmware[0], shell=True) + text = result.communicate()[0] + if(result.returncode == 0): + sys.exit(0) + + # Delay retries to < 20 Hz to prevent spin-lock from hogging the CPU + time.sleep(0.05) + + # CTRL+C aborts the upload/spin-lock by interrupt mechanics + except KeyboardInterrupt: + print("\n Upload aborted by user.") + sys.exit(0) + +if __name__ == '__main__': + main() diff --git a/platforms/nuttx/CMakeLists.txt b/platforms/nuttx/CMakeLists.txt index cd8385827e..ba89894efe 100644 --- a/platforms/nuttx/CMakeLists.txt +++ b/platforms/nuttx/CMakeLists.txt @@ -297,6 +297,29 @@ else() endif() +# create .hex if configured in NuttX +if (CONFIG_INTELHEX_BINARY) + + string(REPLACE ".elf" ".hex" hex_package ${PX4_BINARY_DIR}/${FW_NAME}) + + add_custom_command( + OUTPUT ${hex_package} + COMMAND + ${CMAKE_OBJCOPY} -O ihex + ${PX4_BINARY_DIR}/${PX4_CONFIG}.elf + ${hex_package} + DEPENDS + ${PX4_BINARY_DIR}/${PX4_CONFIG}.bin + airframes_xml + parameters_xml + COMMENT "Creating ${hex_package}" + WORKING_DIRECTORY ${PX4_BINARY_DIR} + ) + + add_custom_target(px4_hex ALL DEPENDS ${hex_package}) + +endif() + # create .px4 with parameter and airframe metadata if (TARGET parameters_xml AND TARGET airframes_xml) @@ -383,7 +406,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Debug/gdbinit.in ${PX4_BINARY_DIR}/.g if(NOT NUTTX_DIR MATCHES "external") if(CONFIG_ARCH_CHIP_MIMXRT1062DVL6A) set(DEBUG_DEVICE "MIMXRT1062XXX6A") - set(DEBUG_SVD_FILE "MIMXRT1052.svd") + set(DEBUG_SVD_FILE "MIMXRT1062.xml") elseif(CONFIG_ARCH_CHIP_MIMXRT1176DVMAA) set(DEBUG_DEVICE "MIMXRT1176DVMAA") set(DEBUG_SVD_FILE "MIMXRT1176_cm7.xml") diff --git a/platforms/nuttx/cmake/upload.cmake b/platforms/nuttx/cmake/upload.cmake index ce776b8a62..9d3013fbd9 100644 --- a/platforms/nuttx/cmake/upload.cmake +++ b/platforms/nuttx/cmake/upload.cmake @@ -35,6 +35,7 @@ set(vendorstr_underscore) set(productstr_underscore) string(REPLACE " " "_" vendorstr_underscore ${CONFIG_CDCACM_VENDORSTR}) +string(REPLACE "," "_" vendorstr_underscore "${vendorstr_underscore}") string(REPLACE " " "_" productstr_underscore ${CONFIG_CDCACM_PRODUCTSTR}) set(serial_ports) diff --git a/platforms/nuttx/src/px4/nxp/imxrt/board_reset/CMakeLists.txt b/platforms/nuttx/src/px4/nxp/imxrt/board_reset/CMakeLists.txt index cba22cb320..deff20f14c 100644 --- a/platforms/nuttx/src/px4/nxp/imxrt/board_reset/CMakeLists.txt +++ b/platforms/nuttx/src/px4/nxp/imxrt/board_reset/CMakeLists.txt @@ -35,7 +35,9 @@ px4_add_library(arch_board_reset board_reset.cpp ) +if (CONFIG_ARCH_FAMILY_IMXRT117x) target_link_libraries(arch_board_reset PRIVATE arch_board_romapi) +endif() # up_systemreset if (NOT DEFINED CONFIG_BUILD_FLAT) diff --git a/platforms/nuttx/src/px4/nxp/imxrt/board_reset/board_reset.cpp b/platforms/nuttx/src/px4/nxp/imxrt/board_reset/board_reset.cpp index 6f1e21700d..1d29a64dfb 100644 --- a/platforms/nuttx/src/px4/nxp/imxrt/board_reset/board_reset.cpp +++ b/platforms/nuttx/src/px4/nxp/imxrt/board_reset/board_reset.cpp @@ -42,11 +42,12 @@ #include #include #include + +#ifdef CONFIG_ARCH_FAMILY_IMXRT117x #include - - #include #include +#endif #define BOOT_RTC_SIGNATURE 0xb007b007 #define PX4_IMXRT_RTC_REBOOT_REG 3 @@ -58,9 +59,13 @@ static int board_reset_enter_bootloader() { +#ifdef CONFIG_ARCH_FAMILY_IMXRT117x uint32_t regvalue = BOOT_RTC_SIGNATURE; modifyreg32(IMXRT_SNVS_LPCR, 0, SNVS_LPCR_GPR_Z_DIS); putreg32(regvalue, PX4_IMXRT_RTC_REBOOT_REG_ADDRESS); +#elif defined(BOARD_HAS_TEENSY_BOOTLOADER) + asm("BKPT #251"); /* Enter Teensy MKL02 bootloader */ +#endif return OK; } @@ -68,13 +73,18 @@ int board_reset(int status) { if (status == REBOOT_TO_BOOTLOADER) { board_reset_enter_bootloader(); + } - } else if (status == REBOOT_TO_ISP) { +#if defined(BOARD_HAS_ISP_BOOTLOADER) + + else if (status == REBOOT_TO_ISP) { uint32_t arg = 0xeb100000; ROM_API_Init(); ROM_RunBootloader(&arg); } +#endif + #if defined(BOARD_HAS_ON_RESET) board_on_reset(status); #endif