From 32c449d8e68250a27f96adadae9486c0abb32854 Mon Sep 17 00:00:00 2001 From: pennam Date: Mon, 22 Jun 2026 17:21:58 +0200 Subject: [PATCH 1/5] llext: add naked trampoline ABI helpers for armv6 --- cores/arduino/llext_wrappers.c | 25 +++++++++++++++++++++++++ loader/llext_exports.c | 11 +++++++++++ 2 files changed, 36 insertions(+) diff --git a/cores/arduino/llext_wrappers.c b/cores/arduino/llext_wrappers.c index afba3d196..8a307ead6 100644 --- a/cores/arduino/llext_wrappers.c +++ b/cores/arduino/llext_wrappers.c @@ -74,6 +74,20 @@ * The naked trampoline uses r12 (ip, AAPCS inter-procedure scratch register) * which is never an argument register, so r0–r3 and d0–d7 are untouched. */ +#if defined(__ARM_ARCH_6M__) || (defined(__ARM_ARCH) && __ARM_ARCH < 7) +/* Thumb-1 (Cortex-M0/M0+): movw/movt are Thumb-2 only, use a PC-relative + * literal pool load via r4 (callee-saved, push/pop balanced). r0-r3 carry + * the RTABI double arguments and must not be touched. + */ +#define VN(name) \ + __attribute__((naked)) void name(void) { \ + __asm__("push {r4}\n\t" \ + "ldr r4, =__real_" #name "\n\t" \ + "mov r12, r4\n\t" \ + "pop {r4}\n\t" \ + "bx r12"); \ + } +#else #define VN(name) \ __attribute__((naked)) void name(void) { \ __asm__("movw r12, #:lower16:__real_" #name "\n\t" \ @@ -81,6 +95,7 @@ "bx r12"); \ } #endif +#endif /* string.h */ W3(void *, memcpy, void *, const void *, size_t) @@ -203,6 +218,16 @@ VN(__aeabi_idivmod) VN(__aeabi_uidivmod) VN(__aeabi_ldivmod) VN(__aeabi_uldivmod) +#if defined(__ARM_ARCH_6M__) || (defined(__ARM_ARCH) && __ARM_ARCH < 7) +/* + * Thumb1 switch-dispatch helpers. + */ +VN(__gnu_thumb1_case_uqi) +VN(__gnu_thumb1_case_sqi) +VN(__gnu_thumb1_case_uhi) +VN(__gnu_thumb1_case_shi) +VN(__gnu_thumb1_case_si) +#endif #endif /* stdio.h */ diff --git a/loader/llext_exports.c b/loader/llext_exports.c index 7555ed97f..184f08ab5 100644 --- a/loader/llext_exports.c +++ b/loader/llext_exports.c @@ -378,6 +378,17 @@ EXPORT_AEABI_SYM(__aeabi_idivmod); EXPORT_AEABI_SYM(__aeabi_uidivmod); EXPORT_AEABI_SYM(__aeabi_ldivmod); EXPORT_AEABI_SYM(__aeabi_uldivmod); + +#if defined(__ARM_ARCH_6M__) || (defined(__ARM_ARCH) && __ARM_ARCH < 7) +/* + * Thumb1 switch-dispatch helpers. + */ +EXPORT_AEABI_SYM(__gnu_thumb1_case_uqi); +EXPORT_AEABI_SYM(__gnu_thumb1_case_sqi); +EXPORT_AEABI_SYM(__gnu_thumb1_case_uhi); +EXPORT_AEABI_SYM(__gnu_thumb1_case_shi); +EXPORT_AEABI_SYM(__gnu_thumb1_case_si); +#endif #endif #if defined(CONFIG_CPP) From 01953b48271008447dc7a625330633c8cd7f03c5 Mon Sep 17 00:00:00 2001 From: Gilberto Conti Date: Mon, 26 Jan 2026 12:37:12 +0100 Subject: [PATCH 2/5] variants: add arduino_nano_rp2040 Signed-off-by: Gilberto Conti --- boards.txt | 69 ++++- extra/build.sh | 2 +- loader/fixups.c | 48 +++ loader/llext_exports.c | 5 + platform.txt | 10 +- .../arduino_nano_connect_rp2040.conf | 25 ++ .../arduino_nano_connect_rp2040.overlay | 290 ++++++++++++++++++ .../arduino_nano_connect_rp2040/nina_pins.cpp | 18 ++ .../arduino_nano_connect_rp2040/nina_pins.h | 72 +++++ .../pins_arduino.h | 10 + .../arduino_nano_connect_rp2040/variant.cpp | 23 ++ .../arduino_nano_connect_rp2040/variant.h | 135 ++++++++ 12 files changed, 704 insertions(+), 3 deletions(-) create mode 100644 variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.conf create mode 100644 variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.overlay create mode 100644 variants/arduino_nano_connect_rp2040/nina_pins.cpp create mode 100644 variants/arduino_nano_connect_rp2040/nina_pins.h create mode 100644 variants/arduino_nano_connect_rp2040/pins_arduino.h create mode 100644 variants/arduino_nano_connect_rp2040/variant.cpp create mode 100644 variants/arduino_nano_connect_rp2040/variant.h diff --git a/boards.txt b/boards.txt index dbf28935a..e7b6325a1 100644 --- a/boards.txt +++ b/boards.txt @@ -660,4 +660,71 @@ unoq.bootloader.tool.default=remoteocd unoq.bootloader.file=zephyr-{build.variant}.elf unoq.bootloader.target=stm32u585zitxq -########################################################################################## +############################################################################################################## + +nano_connect.name=Arduino Nano RP2040 Connect +nano_connect.build.core=arduino +nano_connect.build.crossprefix=arm-zephyr-eabi- +nano_connect.build.compiler_path={runtime.tools.arm-zephyr-eabi-0.16.8.path}/bin/ + +nano_connect.menu.debug.false=Standard +nano_connect.menu.debug.true=Debug +nano_connect.menu.debug.true.build.zsk_args.debug=-debug + +nano_connect.menu.link_mode.dynamic=Dynamic +nano_connect.menu.link_mode.static=Static +nano_connect.menu.link_mode.static.build.link_mode=static +nano_connect.menu.link_mode.static.upload.extension=bin-zsk.bin + +nano_connect.build.zephyr_target=arduino_nano_connect +nano_connect.build.zephyr_args= +nano_connect.build.zephyr_hals=hal_rpi_pico +nano_connect.build.artifact=zephyr_main +nano_connect.build.variant=arduino_nano_connect_rp2040 +nano_connect.build.mcu=cortex-m0plus +nano_connect.build.fpu= +nano_connect.build.architecture=cortex-m0plus +nano_connect.compiler.zephyr.arch.define= + +nano_connect.build.float-abi=-mfloat-abi=soft +nano_connect.build.extra_flags= +nano_connect.build.postbuild.cmd="{tools.imgtool.path}/{tools.imgtool.cmd}" exit +nano_connect.recipe.hooks.objcopy.postobjcopy.3.pattern="{runtime.tools.bin2uf2.path}/bin2uf2" {upload.address} {upload.familyid} "{build.path}/{build.project_name}.{upload.extension}" "{build.path}/{build.project_name}.uf2" +nano_connect.build.board=NANO_RP2040_CONNECT +nano_connect.compiler.zephyr= +nano_connect.vid.0=0x2341 +nano_connect.pid.0=0x005E +nano_connect.upload_port.0.vid=0x2341 +nano_connect.upload_port.0.pid=0x005E + +nano_connect.upload.tool=picotool +nano_connect.upload.tool.default=picotool +nano_connect.upload.protocol= +nano_connect.upload.transport= +nano_connect.upload.vid=0x2341 +nano_connect.upload.pid=0x005E +nano_connect.upload.interface=0 +nano_connect.upload.use_1200bps_touch=true +nano_connect.upload.wait_for_upload_port=false +nano_connect.upload.native_usb=true +nano_connect.upload.maximum_size=16777216 +nano_connect.upload.maximum_data_size=270336 + +nano_connect.upload.address=0x100E0000 +nano_connect.upload.familyid=0xe48bff56 + +nano_connect.bootloader.tool=picotool +nano_connect.bootloader.tool.default=picotool +nano_connect.bootloader.vid=0x2341 +nano_connect.bootloader.pid=0x005E +nano_connect.bootloader.interface=0 +nano_connect.bootloader.file=zephyr-{build.variant} + +nano_connect.debug.tool=gdb +nano_connect.debug.server.openocd.scripts.0=interface/{programmer.protocol}.cfg +nano_connect.debug.server.openocd.scripts.1={programmer.transport_script} +nano_connect.debug.server.openocd.scripts.2=target/rp2040-core0.cfg +nano_connect.debug.cortex-debug.custom.request=attach +nano_connect.debug.svd_file={runtime.platform.path}/svd/rp2040.svd + +############################################################################################################## diff --git a/extra/build.sh b/extra/build.sh index 190a13e3e..72396c924 100755 --- a/extra/build.sh +++ b/extra/build.sh @@ -100,7 +100,7 @@ line_continuation='\\$' # match lines ending with '\' c_comment='\s*\/\*.*?\*\/' # match C-style comments and any preceding space perl -i -pe "s/${c_comment}//gs unless /${line_preproc_ok}/ || (/${line_comment_only}/ && !/${line_continuation}/)" $(find ${VARIANT_DIR}/llext-edk/include/ -type f) -for ext in elf bin hex; do +for ext in elf bin hex uf2; do rm -f firmwares/zephyr-$variant.$ext if [ -f ${BUILD_DIR}/zephyr/zephyr.$ext ]; then cp ${BUILD_DIR}/zephyr/zephyr.$ext firmwares/zephyr-$variant.$ext diff --git a/loader/fixups.c b/loader/fixups.c index da5df64f1..5903c20e7 100644 --- a/loader/fixups.c +++ b/loader/fixups.c @@ -53,6 +53,54 @@ SYS_INIT(disable_bootloader_mpu, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAU SYS_INIT(disable_mpu_rasr_xn, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); #endif +#if defined(CONFIG_BOARD_ARDUINO_NANO_CONNECT) +#include +#include +#include + +/* + * Double-tap reset detection: if the board is reset twice within 500ms, + * enter USB bootloader (BOOTSEL) mode. This mirrors the original + * ArduinoCore-mbed NANO_RP2040_CONNECT behavior. + * + * A magic token is stored in uninitialized RAM (.noinit), which survives + * a warm reset but is lost on power cycle. On boot: + * - If the token is present: a second reset happened quickly, so we + * clear the token and enter USB boot mode (never returns). + * - If not: write the token, wait 500ms, then clear it and boot normally. + */ +static const uint32_t magic_token[] = { + 0xf01681de, + 0xbd729b29, + 0xd359be7a, +}; + +/* Non-static so the variant's _on_1200_bps() can arm the same magic. */ +uint32_t magic_location[3] __attribute__((section(".noinit.double_tap"))); + +#define NANO_RP2040_LED_PIN 6 + +int double_tap_check(void) { + if (magic_location[0] == magic_token[0] && magic_location[1] == magic_token[1] && + magic_location[2] == magic_token[2]) { + magic_location[0] = 0; + reset_usb_boot(1 << NANO_RP2040_LED_PIN, 0); + /* never returns */ + } + + for (int i = 0; i < 3; i++) { + magic_location[i] = magic_token[i]; + } + + k_busy_wait(500000); + + magic_location[0] = 0; + return 0; +} + +SYS_INIT(double_tap_check, POST_KERNEL, 0); +#endif + #if defined(CONFIG_INPUT) #include #include diff --git a/loader/llext_exports.c b/loader/llext_exports.c index 184f08ab5..8f303ac57 100644 --- a/loader/llext_exports.c +++ b/loader/llext_exports.c @@ -466,3 +466,8 @@ STRUCT_SECTION_END_EXTERN(device); EXPORT_SYMBOL(TYPE_SECTION_START(device)); EXPORT_SYMBOL(TYPE_SECTION_END(device)); #endif + +#if defined(CONFIG_BOARD_ARDUINO_NANO_CONNECT) +extern uint32_t magic_location[3]; +EXPORT_SYMBOL(magic_location); +#endif diff --git a/platform.txt b/platform.txt index 9bf4101dd..9c541554c 100644 --- a/platform.txt +++ b/platform.txt @@ -251,7 +251,15 @@ tools.picotool.path={runtime.tools.rp2040tools.path} tools.picotool.cmd=rp2040load tools.picotool.upload.params.verbose=-v tools.picotool.upload.params.quiet= -tools.picotool.upload.pattern="{path}/{cmd}" {upload.verbose} -D "{build.path}/{build.project_name}.elf" +tools.picotool.upload.pattern="{path}/{cmd}" {upload.verbose} -D "{build.path}/{build.project_name}" + +tools.picotool.erase.params.verbose= +tools.picotool.erase.params.quiet= +tools.picotool.erase.pattern= + +tools.picotool.bootloader.params.verbose=-v +tools.picotool.bootloader.params.quiet= +tools.picotool.bootloader.pattern="{path}/{cmd}" {upload.verbose} -D "{runtime.platform.path}/firmwares/{bootloader.file}" # # IMGTOOL diff --git a/variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.conf b/variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.conf new file mode 100644 index 000000000..e7b896244 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.conf @@ -0,0 +1,25 @@ +# Copyright (c) Arduino s.r.l. and/or its affiliated companies +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="Arduino Nano RP2040 Connect" +CONFIG_USB_DEVICE_MANUFACTURER="Arduino" +CONFIG_USB_DEVICE_VID=0x2341 +CONFIG_USB_DEVICE_PID=0x005E + +CONFIG_USB_CDC_ACM=y +CONFIG_USB_CDC_ACM_RINGBUF_SIZE=1024 +CONFIG_UART_LINE_CTRL=y +CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT=y + +CONFIG_HEAP_MEM_POOL_SIZE=16384 +CONFIG_MAIN_STACK_SIZE=32768 +CONFIG_LLEXT_HEAP_SIZE=64 + +CONFIG_LLEXT_STORAGE_WRITABLE=n +CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n + +CONFIG_ADC=y +CONFIG_DAC=n +CONFIG_PWM=y + diff --git a/variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.overlay b/variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.overlay new file mode 100644 index 000000000..6ecd771bf --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.overlay @@ -0,0 +1,290 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + zephyr,user { + /* + * Arduino Nano RP2040 Connect pin mapping + * Arduino Pin -> RP2040 GPIO + */ + digital-pin-gpios = <&gpio0 1 0>, /* D0 - RX */ + <&gpio0 0 0>, /* D1 - TX */ + <&gpio0 25 0>, /* D2 */ + <&gpio0 15 0>, /* D3 */ + <&gpio0 16 0>, /* D4 */ + <&gpio0 17 0>, /* D5 */ + <&gpio0 18 0>, /* D6 */ + <&gpio0 19 0>, /* D7 */ + <&gpio0 20 0>, /* D8 */ + <&gpio0 21 0>, /* D9 */ + <&gpio0 5 0>, /* D10 - SPI SS */ + <&gpio0 7 0>, /* D11 - SPI MOSI */ + <&gpio0 4 0>, /* D12 - SPI MISO */ + <&gpio0 6 0>, /* D13 - SPI SCK / LED */ + <&gpio0 26 0>, /* D14 / A0 */ + <&gpio0 27 0>, /* D15 / A1 */ + <&gpio0 28 0>, /* D16 / A2 */ + <&gpio0 29 0>, /* D17 / A3 */ + <&gpio0 12 0>, /* D18 / A4 / SDA */ + <&gpio0 13 0>, /* D19 / A5 / SCL */ + <&gpio0 2 0>, /* D20 - NINA GPIO0 */ + <&gpio0 24 0>, /* D21 - IMU IRQ */ + <&gpio0 22 0>, /* D22 - PDM DIN */ + <&gpio0 23 0>, /* D23 - PDM CLK */ + <&gpio0 3 0>, /* D24 - NINA RESET */ + <&gpio0 8 0>, /* D25 - NINA SPI MISO */ + <&gpio0 9 0>, /* D26 - NINA SPI CS */ + <&gpio0 10 0>, /* D27 - NINA SPI ACK */ + <&gpio0 11 0>, /* D28 - NINA SPI MOSI */ + <&gpio0 14 0>; /* D29 - NINA SPI SCK */ + + builtin-led-gpios = <&gpio0 6 0>; /* D13 = GPIO6 */ + + /* + * PWM-capable pins on Arduino Nano RP2040 Connect + * Listed by Arduino pin number with corresponding GPIO + */ + pwm-pin-gpios = <&gpio0 25 0>, /* D2 -> GPIO25 -> PWM4B */ + <&gpio0 15 0>, /* D3 -> GPIO15 -> PWM7B */ + <&gpio0 16 0>, /* D4 -> GPIO16 -> PWM0A */ + <&gpio0 17 0>, /* D5 -> GPIO17 -> PWM0B */ + <&gpio0 18 0>, /* D6 -> GPIO18 -> PWM1A */ + <&gpio0 19 0>, /* D7 -> GPIO19 -> PWM1B */ + <&gpio0 20 0>, /* D8 -> GPIO20 -> PWM2A */ + <&gpio0 21 0>, /* D9 -> GPIO21 -> PWM2B */ + <&gpio0 5 0>, /* D10 -> GPIO5 -> PWM2B */ + <&gpio0 7 0>, /* D11 -> GPIO7 -> PWM3B */ + <&gpio0 4 0>, /* D12 -> GPIO4 -> PWM2A */ + <&gpio0 6 0>; /* D13 -> GPIO6 -> PWM3A (LED) */ + + adc-pin-gpios = <&gpio0 26 0>, /* A0 -> GPIO26 -> ADC0 */ + <&gpio0 27 0>, /* A1 -> GPIO27 -> ADC1 */ + <&gpio0 28 0>, /* A2 -> GPIO28 -> ADC2 */ + <&gpio0 29 0>; /* A3 -> GPIO29 -> ADC3 */ + + pwms = <&pwm 25 255 PWM_POLARITY_NORMAL>, /* D2 GPIO25 */ + <&pwm 15 255 PWM_POLARITY_NORMAL>, /* D3 GPIO15 */ + <&pwm 16 255 PWM_POLARITY_NORMAL>, /* D4 GPIO16 */ + <&pwm 17 255 PWM_POLARITY_NORMAL>, /* D5 GPIO17 */ + <&pwm 18 255 PWM_POLARITY_NORMAL>, /* D6 GPIO18 */ + <&pwm 19 255 PWM_POLARITY_NORMAL>, /* D7 GPIO19 */ + <&pwm 20 255 PWM_POLARITY_NORMAL>, /* D8 GPIO20 */ + <&pwm 21 255 PWM_POLARITY_NORMAL>, /* D9 GPIO21 */ + <&pwm 5 255 PWM_POLARITY_NORMAL>, /* D10 GPIO5 */ + <&pwm 7 255 PWM_POLARITY_NORMAL>, /* D11 GPIO7 */ + <&pwm 4 255 PWM_POLARITY_NORMAL>, /* D12 GPIO4 */ + <&pwm 6 255 PWM_POLARITY_NORMAL>; /* D13 GPIO6 */ + + io-channels = <&adc 0>, /* A0 */ + <&adc 1>, /* A1 */ + <&adc 2>, /* A2 */ + <&adc 3>; /* A3 */ + + serials = <&uart0>, <&uart1>, <&uart2>; + cdc-acm-serial = <&board_cdc_acm_uart>; + i2cs = <&i2c0>; + spis = <&spi0>, <&spi1>; + }; +}; + +&pinctrl { + /* + * Additional PWM pin control for Arduino analogWrite() + * PWM slice assignment: GPIO / 2 (integer division) + * PWM channel: A if GPIO even, B if GPIO odd + */ + + /* D4 -> GPIO16 -> PWM0A */ + pwm_ch0a_default: pwm_ch0a_default { + group1 { + pinmux = ; + }; + }; + + /* D5 -> GPIO17 -> PWM0B */ + pwm_ch0b_default: pwm_ch0b_default { + group1 { + pinmux = ; + }; + }; + + /* D6 -> GPIO18 -> PWM1A */ + pwm_ch1a_default: pwm_ch1a_default { + group1 { + pinmux = ; + }; + }; + + /* D7 -> GPIO19 -> PWM1B */ + pwm_ch1b_default: pwm_ch1b_default { + group1 { + pinmux = ; + }; + }; + + /* + * D8 -> GPIO20 -> PWM2A, + * D12 -> GPIO4 -> PWM2A + */ + pwm_ch2a_default: pwm_ch2a_default { + group1 { + pinmux = , ; + }; + }; + + /* D9 -> GPIO21 -> PWM2B, D10 -> GPIO5 -> PWM2B */ + pwm_ch2b_default: pwm_ch2b_default { + group1 { + pinmux = , ; + }; + }; + + /* D13 -> GPIO6 -> PWM3A (LED) */ + pwm_ch3a_default: pwm_ch3a_default { + group1 { + pinmux = ; + }; + }; + + /* D11 -> GPIO7 -> PWM3B */ + pwm_ch3b_default: pwm_ch3b_default { + group1 { + pinmux = ; + }; + }; + + /* D2 -> GPIO25 -> PWM4B */ + pwm_ch4b_default: pwm_ch4b_default { + group1 { + pinmux = ; + }; + }; + + /* D3 -> GPIO15 -> PWM7B */ + pwm_ch7b_default: pwm_ch7b_default { + group1 { + pinmux = ; + }; + }; + + /* + * UART1 pin control (NINA module) + * TX -> GPIO8 + * RX -> GPIO9 + * CTS -> GPIO10 + * RTS -> GPIO11 + */ + uart1_default: uart1_default { + group1 { + pinmux = ; + }; + group2 { + pinmux = ; + input-enable; + }; + group3 { + pinmux = ; + input-enable; + }; + group4 { + pinmux = ; + }; + }; + +}; + +&pwm { + status = "okay"; + pinctrl-0 = <&pwm_ch0a_default &pwm_ch0b_default + &pwm_ch1a_default &pwm_ch1b_default + &pwm_ch2a_default &pwm_ch2b_default + &pwm_ch3a_default &pwm_ch3b_default + &pwm_ch4b_default &pwm_ch7b_default>; + pinctrl-names = "default"; + divider-frac-4 = <15>; + divider-int-4 = <255>; +}; + +&adc { + #address-cells = <1>; + #size-cells = <0>; + + /* A0 -> GPIO26 -> ADC0 */ + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; + + /* A1 -> GPIO27 -> ADC1 */ + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; + + /* A2 -> GPIO28 -> ADC2 */ + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; + + /* A3 -> GPIO29 -> ADC3 */ + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; + +/* UART1 for NINA module */ +&uart1 { + status = "okay"; + pinctrl-0 = <&uart1_default>; + pinctrl-names = "default"; + current-speed = <115200>; + hw-flow-control; +}; + +&zephyr_udc0 { + board_cdc_acm_uart: board_cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + }; +}; + +&flash0 { + partitions { + user_sketch: partition@e0000 { + reg = <0x0E0000 0x20000>; + }; + }; +}; + +nina_spi: &spi1 { + status = "okay"; + /delete-node/ nina_w102@0; +}; + +/* + * Alias for NINA WiFi UART (uart1) without hardware flow control, + * used for firmware flashing mode. Normal operation uses uart1 directly + * (with hw-flow-control enabled above). Having both lets Arduino sketch + * code select the right configuration at compile time. + */ +uart2: &uart1 { + /delete-node/ nina_prog; +}; diff --git a/variants/arduino_nano_connect_rp2040/nina_pins.cpp b/variants/arduino_nano_connect_rp2040/nina_pins.cpp new file mode 100644 index 000000000..bdfb9da60 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/nina_pins.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nina_pins.h" + +// RGB LEDs on NINA module +NinaPin LEDR(27); +NinaPin LEDG(25); +NinaPin LEDB(26); + +// Analog pins on NINA module +NinaPin A4(34); +NinaPin A5(39); +NinaPin A6(36); +NinaPin A7(35); diff --git a/variants/arduino_nano_connect_rp2040/nina_pins.h b/variants/arduino_nano_connect_rp2040/nina_pins.h new file mode 100644 index 000000000..2c2d9198f --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/nina_pins.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NINA_PINS_ +#define _NINA_PINS_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include "Arduino.h" + +/****************************************************************************** + * PREPROCESSOR-MAGIC + ******************************************************************************/ + +#if __has_include("WiFiNINA.h") +# define NINA_ATTRIBUTE +#else +# define NINA_ATTRIBUTE __attribute__ ((error("Please include WiFiNINA.h to use this pin"))) +#endif + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +int getAnalogReadResolution(); + +class NinaPin { +public: + NinaPin(int _pin) : pin(_pin) {}; + int get() { + return pin; + }; + int analogReadResolution() { + return getAnalogReadResolution(); + }; + bool operator== (NinaPin const & other) const { + return pin == other.pin; + } + //operator int() = delete; + __attribute__ ((error("Change me to a #define"))) operator int(); +private: + int pin; +}; + +extern NinaPin LEDR; +extern NinaPin LEDG; +extern NinaPin LEDB; +extern NinaPin A4; +extern NinaPin A5; +extern NinaPin A6; +extern NinaPin A7; + +#define NINA_PINS_AS_CLASS + +/****************************************************************************** + * FUNCTION DECLARATION + ******************************************************************************/ + +void NINA_ATTRIBUTE pinMode (NinaPin pin, PinMode mode); +PinStatus NINA_ATTRIBUTE digitalRead (NinaPin pin); +void NINA_ATTRIBUTE digitalWrite(NinaPin pin, PinStatus value); +int NINA_ATTRIBUTE analogRead (NinaPin pin); +void NINA_ATTRIBUTE analogWrite (NinaPin pin, int value); + +#undef NINA_ATTRIBUTE + +#endif /* _NINA_PINS_ */ diff --git a/variants/arduino_nano_connect_rp2040/pins_arduino.h b/variants/arduino_nano_connect_rp2040/pins_arduino.h new file mode 100644 index 000000000..490e179d3 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/pins_arduino.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// For compatibility with Arduino mbed core +#include "variant.h" diff --git a/variants/arduino_nano_connect_rp2040/variant.cpp b/variants/arduino_nano_connect_rp2040/variant.cpp new file mode 100644 index 000000000..0ba4b2c82 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/variant.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Arduino.h" +#include + +/* + * Arm the loader's double_tap magic and reset. The loader's + * double_tap_check() (loader/fixups.c) sees the magic on next boot and + * enters USB BOOTSEL via libpico's reset_usb_boot(). Magic values must + * match those in loader/fixups.c. + */ +extern "C" uint32_t magic_location[3]; + +void _on_1200_bps() { + magic_location[0] = 0xf01681de; + magic_location[1] = 0xbd729b29; + magic_location[2] = 0xd359be7a; + NVIC_SystemReset(); +} diff --git a/variants/arduino_nano_connect_rp2040/variant.h b/variants/arduino_nano_connect_rp2040/variant.h new file mode 100644 index 000000000..6babd7cc0 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/variant.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifndef __PINS_ARDUINO__ +#define __PINS_ARDUINO__ + +#include + +// Pin count +// ---- +#define PINS_COUNT (30u) +#define NUM_DIGITAL_PINS (30u) +#define NUM_ANALOG_INPUTS (4u) +#define NUM_ANALOG_OUTPUTS (0u) + +// LEDs +// ---- +#define PIN_LED (13u) +#define LED_BUILTIN PIN_LED + +// Note: RGB LEDs (LEDR, LEDG, LEDB) are on the NINA module +// and require WiFiNINA library to control + +// Analog pins +// ----------- +// Note: A0-A3 are defined as enum values in Arduino.h via devicetree +#define PIN_A0 (14u) +#define PIN_A1 (15u) +#define PIN_A2 (16u) +#define PIN_A3 (17u) + +#define ADC_RESOLUTION 12 + +// PDM Interfaces +// --------------- +#define PIN_PDM_CLK (23) +#define PIN_PDM_DIN (22) + +// IMU Interrupt +#define INT_IMU (21) + +// Serial +// ------ +#define PIN_SERIAL_RX (0ul) +#define PIN_SERIAL_TX (1ul) + +#define SERIAL_HOWMANY 3 + +// Serial1: Hardware UART (D0/D1) +#define SERIAL1_TX (1u) +#define SERIAL1_RX (0u) + +// Serial2: NINA HCI (D25/D26 with flow control) +#define SERIAL2_TX (25u) +#define SERIAL2_RX (26u) +#define SERIAL2_CTS (27u) +#define SERIAL2_RTS (28u) + +// Serial3: NINA communication (D25/D26) +#define SERIAL3_TX (25u) +#define SERIAL3_RX (26u) + +// USB CDC +#define SERIAL_CDC 1 +#define HAS_UNIQUE_ISERIAL_DESCRIPTOR +#define BOARD_VENDORID 0x2341 +#define BOARD_PRODUCTID 0x005E +#define BOARD_NAME "Arduino Nano RP2040 Connect" + +#define USB_MAX_POWER (500) + +// SPI +// --- +#define SPI_HOWMANY (2) + +#define PIN_SPI_MISO (12u) +#define PIN_SPI_MOSI (11u) +#define PIN_SPI_SCK (13u) +#define PIN_SPI_SS (10u) + +static const uint8_t SS = PIN_SPI_SS; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +// SPI1 (NINA WiFi module SPI bus) +#define PIN_SPI1_MISO (25u) // GPIO8 +#define PIN_SPI1_SS (26u) // GPIO9 +#define PIN_SPI1_MOSI (28u) // GPIO11 +#define PIN_SPI1_SCK (29u) // GPIO14 + +// Wire (I2C) +// ---------- +#define WIRE_HOWMANY (1) + +#define PIN_WIRE_SDA (18u) +#define PIN_WIRE_SCL (19u) + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; + +// NINA WiFi module pins +// --------------------- +#define NINA_RESETN (24u) +#define NINA_GPIO0 (20u) + +#define SPIWIFI_SS (26u) +#define SPIWIFI_ACK (27u) +#define SPIWIFI_RESET (NINA_RESETN) + +// Crypto (ATECC608A on I2C bus) +#define CRYPTO_WIRE Wire + +// NINA WiFi SPI interface +#define SPIWIFI SPI1 + +// Serial port definitions +#define SERIAL_PORT_USBVIRTUAL SerialUSB +#define SERIAL_PORT_MONITOR SerialUSB +#define SERIAL_PORT_HARDWARE Serial1 +#define SERIAL_PORT_HARDWARE_OPEN Serial2 + +// NINA Serial aliases +#define SerialNina Serial3 +#define SerialHCI Serial2 + +// Note: nina_pins.h should be included after Arduino.h +// It is automatically included by WiFiNINA.h when needed + +#endif /* __PINS_ARDUINO__ */ From 47801fbbf9d568078cc3270dc509cbfc8cf034d7 Mon Sep 17 00:00:00 2001 From: Gilberto Conti Date: Wed, 29 Apr 2026 11:57:21 +0200 Subject: [PATCH 3/5] libraries: spi: skip re-init on nano_connect when already ready On nano_connect the SPI driver is already initialized Signed-off-by: Gilberto Conti --- libraries/SPI/SPI.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index cd78db5b8..73eb3ad5c 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -114,11 +114,18 @@ void arduino::ZephyrSPI::detachInterrupt() { } void arduino::ZephyrSPI::begin() { - +#if defined(CONFIG_BOARD_ARDUINO_NANO_CONNECT) + /* On nano_connect the SPI driver is already initialized by the loader; + * calling init() again on an already-ready device hangs. */ + if (!device_is_ready(spi_dev)) { + (void)zephyr::arduino::init_dev_apply_pinctrl(spi_dev); + } +#else /* Re-apply DEFAULT pinctrl state so shared pins * are remuxed back to SPI after other peripherals have used them. */ (void)zephyr::arduino::init_dev_apply_pinctrl(spi_dev); +#endif } void arduino::ZephyrSPI::end() { From 0a9a43d2fd1eb8a4fa7a932ab686f28e90f50387 Mon Sep 17 00:00:00 2001 From: Gilberto Conti Date: Wed, 29 Apr 2026 11:09:33 +0200 Subject: [PATCH 4/5] loader: exports: skip SystemCoreClock on RP2040 CMSIS declares SystemCoreClock as a standard symbol but the actual variable is provided only by SoC-specific code. Signed-off-by: Gilberto Conti --- loader/llext_exports.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader/llext_exports.c b/loader/llext_exports.c index 8f303ac57..831f7e689 100644 --- a/loader/llext_exports.c +++ b/loader/llext_exports.c @@ -454,7 +454,7 @@ EXPORT_SYMBOL(arm_irq_is_enabled); EXPORT_SYMBOL(arm_irq_priority_set); #endif -#if defined(__arm__) +#if defined(__arm__) && !defined(CONFIG_SOC_FAMILY_RPI_PICO) EXPORT_SYMBOL(SystemCoreClock); #endif From af88df6a084f1a23ed5d3771584cc68c3df304f9 Mon Sep 17 00:00:00 2001 From: pennam Date: Mon, 22 Jun 2026 21:57:22 +0200 Subject: [PATCH 5/5] ci: rp2040: add test setup --- .../known_example_issues.txt | 10 ++++++++++ .../arduino_nano_connect_rp2040/test_setup.sh | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 variants/arduino_nano_connect_rp2040/known_example_issues.txt create mode 100644 variants/arduino_nano_connect_rp2040/test_setup.sh diff --git a/variants/arduino_nano_connect_rp2040/known_example_issues.txt b/variants/arduino_nano_connect_rp2040/known_example_issues.txt new file mode 100644 index 000000000..d68ba6b01 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/known_example_issues.txt @@ -0,0 +1,10 @@ +# Copyright (c) Arduino s.r.l. and/or its affiliated companies +# SPDX-License-Identifier: Apache-2.0 + +# This file contains a list of examples that are shipped with the core (or used +# by continuous integration tests), but are known to be currently failing for +# this specific variant. +# +# Each line in this file is treated as a regular expression and will be matched +# against the path of each sketch found in this repo. If a match is found, the +# sketch compilation result will be ignored. diff --git a/variants/arduino_nano_connect_rp2040/test_setup.sh b/variants/arduino_nano_connect_rp2040/test_setup.sh new file mode 100644 index 000000000..95addf038 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/test_setup.sh @@ -0,0 +1,20 @@ +# Copyright (c) Arduino s.r.l. and/or its affiliated companies +# SPDX-License-Identifier: Apache-2.0 + +# This script is sourced by extra/ci_apply_tests.sh to customize the CI test +# list for this board variant. Three helper functions are available: +# get_branch_tip [ ...] +# get_latest_release [ ...] +# skip_for_this_board +# +# get_branch_tip / get_latest_release add external repos to the test list +# (the repos must have been downloaded by ci_fetch_tests.sh first). +# skip_for_this_board removes all tests under the given path prefix. + +skip_for_this_board libraries/Camera +skip_for_this_board libraries/Ethernet +skip_for_this_board libraries/Storage +skip_for_this_board libraries/WiFi +skip_for_this_board libraries/Zephyr_SDRAM +skip_for_this_board libraries/RTC +skip_for_this_board libraries/CAN