diff --git a/.gitignore b/.gitignore index 5c3ff0e6e..fec4815fa 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ cxxflags.txt includes.txt syms-dynamic.ld syms-static.ld +tls-syms.S diff --git a/boards.txt b/boards.txt index 8a0b9dbf2..ebcd97bb2 100644 --- a/boards.txt +++ b/boards.txt @@ -757,4 +757,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/cores/arduino/llext_wrappers.c b/cores/arduino/llext_wrappers.c index 1bac447f1..fbb9a55a2 100644 --- a/cores/arduino/llext_wrappers.c +++ b/cores/arduino/llext_wrappers.c @@ -62,6 +62,25 @@ __real_##name(a); \ } +#ifdef CONFIG_ARM +/* ARM EABI thread pointer access */ +W0(size_t, __aeabi_read_tp) + +/* + * Thumb1 switch-dispatch helpers. + * + * These use a non-standard calling convention: BL sets LR to the jump table + * base (not a return address). With -mlong-calls the compiler lowers each + * void-void wrapper to a tail call (ldr r3, =addr; bx r3), leaving LR + * unchanged so the real function sees the original table pointer. + */ +V0(__gnu_thumb1_case_uqi) +V0(__gnu_thumb1_case_sqi) +V0(__gnu_thumb1_case_uhi) +V0(__gnu_thumb1_case_shi) +V0(__gnu_thumb1_case_si) +#endif + /* string.h */ W3(void *, memcpy, void *, const void *, size_t) W3(void *, memmove, void *, const void *, size_t) diff --git a/extra/build.sh b/extra/build.sh index e490e1c3f..008195c73 100755 --- a/extra/build.sh +++ b/extra/build.sh @@ -91,7 +91,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 @@ -102,6 +102,7 @@ cp ${BUILD_DIR}/zephyr/.config firmwares/zephyr-$variant.config # Generate the provides.ld file for linked builds echo "Generating exported symbol scripts" +extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -T > ${VARIANT_DIR}/tls-syms.S extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -L > ${VARIANT_DIR}/syms-dynamic.ld extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -LF \ "+kheap_llext_heap" \ diff --git a/extra/gen_provides.py b/extra/gen_provides.py index 39bfa8efd..47a303312 100755 --- a/extra/gen_provides.py +++ b/extra/gen_provides.py @@ -122,6 +122,9 @@ def main(): argparser.add_argument('-F', '--funcs', action='store_true', help='Extract all public functions') + argparser.add_argument('-T', '--tls-defs', + action='store_true', + help='Extract TLS symbol offsets to a .S file') argparser.add_argument('file', help='ELF file to parse') argparser.add_argument('syms', nargs='*', @@ -129,10 +132,19 @@ def main(): args = argparser.parse_intermixed_args() + wants_provides = bool(args.syms or args.funcs or args.llext) + if wants_provides and args.tls_defs: + sys.stderr.write("Cannot generate TLS defs when also generating PROVIDEs.\n") + sys.exit(1) + elif not wants_provides and not args.tls_defs: + sys.stderr.write("Nothing specified for export.\n") + sys.exit(1) + exact_syms = set() regex_syms = set() deref_syms = set() sized_syms = set() + tls_syms = set() # tuple of (name, value, size) rename_map = {} for sym in args.syms: sym_class = None @@ -184,6 +196,11 @@ def main(): fail = False for name, sym in all_syms.items(): + if sym['st_info']['type'] == 'STT_TLS': + # Collect TLS symbol information, ignore for PROVIDEs + tls_syms.add((name, sym['st_value'], sym['st_size'])) + continue + value = None comment = [] if name in exact_syms or any(re.match(r, name) for r in regex_syms): @@ -220,10 +237,6 @@ def main(): if name in sized_syms: out_syms[name + "_size"] = (sym['st_size'], [f"size of {name}"]) - if not out_syms: - sys.stderr.write("No symbols found matching the criteria.\n") - fail = True - if fail: sys.exit(1) @@ -235,16 +248,47 @@ def main(): * SHA256: {elf_sha} */ """) - sym_comment = nul_comment = "" - for name, (value, comments) in sorted(out_syms.items(), key=lambda x: x[0]): - if args.verbose: - comment = ', '.join(sorted(comments)) - sym_comment = f"/* {comment} */" - nul_comment = f" ({comment})" - if value: - print(f"PROVIDE({name} = {value:#010x});{sym_comment}") + + if wants_provides: + if not out_syms: + print("/* No symbols found matching the criteria */") + sys.stderr.write("warning: no symbols found matching the criteria.\n") else: - print(f"/* NULL {name}{nul_comment} */") + sym_comment = nul_comment = "" + for name, (value, comments) in sorted(out_syms.items(), key=lambda x: x[0]): + if args.verbose: + comment = ', '.join(sorted(comments)) + sym_comment = f"/* {comment} */" + nul_comment = f" ({comment})" + if value: + print(f"PROVIDE({name} = {value:#010x});{sym_comment}") + else: + print(f"/* NULL {name}{nul_comment} */") + + if args.tls_defs: + # ARM and AArch64 use '@' as a line-comment character in GAS, so the + # assembler requires '%' as the type prefix there. All other targets use '@'. + prefix = '%' if elf['e_machine'] in ('EM_ARM', 'EM_AARCH64') else '@' + + # The TLS Variant 1 layout (used by ARM/AArch64) places a Thread + # Control Block (TCB) before the TLS data. The thread pointer + # returned by __aeabi_read_tp() points to the TCB, so the actual + # runtime address of a TLS variable must be offset by that size. + # + # TCB is 2 pointers: 8 bytes on 32-bit, 16 bytes on 64-bit. + # See zephyr/arch/arm/core/tls.c: arch_tls_stack_setup(). + tcb_size = (elf.elfclass // 8) * 2 + print(f"/* Offsets include {tcb_size} bytes for TCB data */") + + # Sort by offset first, then size + for name, offset, size in sorted(tls_syms, key=lambda x: (x[1], x[2])): + offset += tcb_size + print( + f"\n/* TLS offset {offset:#x}: {name} ({size} bytes) */\n" + f".global {name}\n" + f".type {name}, {prefix}tls_object\n" + f".set {name}, {offset}" + ) #------------------------------------------------------------------------------- if __name__ == '__main__': diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index cbcaa1b22..509cfe99a 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -115,7 +115,9 @@ void arduino::ZephyrSPI::detachInterrupt() { } void arduino::ZephyrSPI::begin() { - spi_dev->ops.init(spi_dev); + if (!device_is_ready(spi_dev)) { + spi_dev->ops.init(spi_dev); + } } void arduino::ZephyrSPI::end() { diff --git a/loader/fixups.c b/loader/fixups.c index 1054933f3..d584eb266 100644 --- a/loader/fixups.c +++ b/loader/fixups.c @@ -53,6 +53,53 @@ 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_RP2040_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, +}; + +static 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 57adc393a..05ce3452b 100644 --- a/loader/llext_exports.c +++ b/loader/llext_exports.c @@ -297,6 +297,20 @@ EXPORT_SYMBOL(ring_buf_area_finish); #endif EXPORT_SYMBOL(sys_clock_cycle_get_32); + +#if defined(CONFIG_ARM) +extern uint32_t __aeabi_read_tp(void); +EXPORT_LIBC_SYM(__aeabi_read_tp); +extern void __gnu_thumb1_case_uqi(void); +EXPORT_LIBC_SYM(__gnu_thumb1_case_uqi); +extern void __gnu_thumb1_case_sqi(void); +EXPORT_LIBC_SYM(__gnu_thumb1_case_sqi); +extern void __gnu_thumb1_case_uhi(void); +EXPORT_LIBC_SYM(__gnu_thumb1_case_uhi); +extern void __gnu_thumb1_case_shi(void); +EXPORT_LIBC_SYM(__gnu_thumb1_case_shi); +extern void __gnu_thumb1_case_si(void); +EXPORT_LIBC_SYM(__gnu_thumb1_case_si); FORCE_EXPORT_SYM(__aeabi_dcmpun); FORCE_EXPORT_SYM(__aeabi_dcmple); FORCE_EXPORT_SYM(__aeabi_d2lz); @@ -326,6 +340,8 @@ FORCE_EXPORT_SYM(__aeabi_idivmod); FORCE_EXPORT_SYM(__aeabi_ldivmod); FORCE_EXPORT_SYM(__aeabi_ul2f); FORCE_EXPORT_SYM(__aeabi_dcmpge); +FORCE_EXPORT_SYM(__aeabi_lmul); +#endif #if defined (CONFIG_CPP) FORCE_EXPORT_SYM(__cxa_pure_virtual); diff --git a/platform.txt b/platform.txt index ee295bd0a..493e6ba13 100644 --- a/platform.txt +++ b/platform.txt @@ -249,7 +249,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..c70fb0a44 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/arduino_nano_connect_rp2040.conf @@ -0,0 +1,34 @@ +# 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_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_UART_LINE_CTRL=y + +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_LLEXT_STORAGE_WRITABLE=n + +CONFIG_HEAP_MEM_POOL_SIZE=16384 +CONFIG_MAIN_STACK_SIZE=32768 +CONFIG_LLEXT_HEAP_SIZE=64 +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 + +CONFIG_LOG_BACKEND_UART=y +CONFIG_LOG_BACKEND_UART_AUTOSTART=n +CONFIG_LOG_DEFAULT_LEVEL=2 + +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..ceb001266 --- /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 = <&board_cdc_acm_uart>, <&uart0>, <&uart1>, <&uart2>; + cdc-acm = <&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..d98a78e33 --- /dev/null +++ b/variants/arduino_nano_connect_rp2040/variant.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Arduino.h" + +#define NANO_RP2040_LED_PIN 6 + +void _on_1200_bps() { + /* + * Call reset_usb_boot() directly via the RP2040 ROM lookup table. + * This avoids depending on rom_reset_usb_boot from libpico (not + * exported to LLEXT sketches). The ROM is always at 0x00000000: + * 0x14: 16-bit offset of the function table + * 0x18: 16-bit offset of the table lookup function + * 'UB' is the 2-byte code for reset_usb_boot (ROM_FUNC_RESET_USB_BOOT). + */ + typedef void *(*rom_lookup_fn)(uint16_t *table, uint32_t code); + typedef void __attribute__((noreturn)) (*reset_usb_boot_fn)(uint32_t, uint32_t); + + rom_lookup_fn lookup = (rom_lookup_fn)(uintptr_t)(*(volatile uint16_t *)0x18u); + uint16_t *table = (uint16_t *)(uintptr_t)(*(volatile uint16_t *)0x14u); + + reset_usb_boot_fn fn = (reset_usb_boot_fn) lookup(table, ('U' | ('B' << 8))); + fn(1u << NANO_RP2040_LED_PIN, 0u); +} 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__ */