From a1b5cfc7243691d50f2b59e34b9b7ee550a41ea5 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Sat, 12 Sep 2026 13:58:53 +0200 Subject: [PATCH] semtech lr2021 evaluation kit port --- build.sh | 1 + gen_provider_catalog.py | 5 + releasenotes/RELEASE_NOTES_1.17.5-zephcore.md | 229 +++++---- zephcore/boards/example_board/README.md | 11 +- .../promicro_sx1262/promicro_sx1262.dts | 28 + .../nrf54l/seeed_lr2021_evk/Kconfig.defconfig | 9 + .../seeed_lr2021_evk/Kconfig.seeed_lr2021_evk | 6 + .../nrf54l/seeed_lr2021_evk/board.cmake | 19 + .../boards/nrf54l/seeed_lr2021_evk/board.conf | 16 + .../boards/nrf54l/seeed_lr2021_evk/board.yml | 9 + .../nrf54l/seeed_lr2021_evk/bringup.conf | 48 ++ .../seeed_lr2021_evk/pre_dt_board.cmake | 3 + .../seeed_lr2021_evk-pinctrl.dtsi | 68 +++ .../seeed_lr2021_evk_nrf54l15_cpuapp.dts | 480 ++++++++++++++++++ ...seeed_lr2021_evk_nrf54l15_cpuapp_defconfig | 32 ++ .../seeed_lr2021_evk/support/openocd.cfg | 182 +++++++ 16 files changed, 1052 insertions(+), 94 deletions(-) create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.defconfig create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.seeed_lr2021_evk create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/board.cmake create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/board.conf create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/board.yml create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/bringup.conf create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/pre_dt_board.cmake create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk-pinctrl.dtsi create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp.dts create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp_defconfig create mode 100644 zephcore/boards/nrf54l/seeed_lr2021_evk/support/openocd.cfg diff --git a/build.sh b/build.sh index 848eece..971699c 100644 --- a/build.sh +++ b/build.sh @@ -43,6 +43,7 @@ Linux_boards=( nRF54L_boards=( me25ls02/nrf54l15/cpuapp xiao_nrf54l15/nrf54l15/cpuapp + seeed_lr2021_evk/nrf54l15/cpuapp ) # SWD-only ARM platforms. Neither SoC has a USB device peripheral, so there is diff --git a/gen_provider_catalog.py b/gen_provider_catalog.py index 8164640..9fb7acb 100644 --- a/gen_provider_catalog.py +++ b/gen_provider_catalog.py @@ -226,6 +226,11 @@ BOARDS = [ # USB cable is enough (openocd/pyocd) -- still not a browser flash flow. dict(stem="xiao_nrf54l15-nrf54l15-cpuapp", kind="nrf54l", device="Seeed Studio Xiao nRF54L15", new=True, img="lora.svg"), + # Same XIAO nRF54L15 host and the same CMSIS-DAP story, but a different kit: + # the Semtech LoRa Plus expansion board with a Wio-LR2021 module. Separate + # tile because the radio and the whole D0..D10 wiring differ. + dict(stem="seeed_lr2021_evk-nrf54l15-cpuapp", kind="nrf54l", + device="Semtech LR2021 LoRa Plus EVK", new=True, img="lora.svg"), # --- SWD-only ARM boards (download-only tiles): new tiles ------------- # Same shape as the nRF54L entries: no USB device peripheral means no diff --git a/releasenotes/RELEASE_NOTES_1.17.5-zephcore.md b/releasenotes/RELEASE_NOTES_1.17.5-zephcore.md index d78a305..4857ccc 100644 --- a/releasenotes/RELEASE_NOTES_1.17.5-zephcore.md +++ b/releasenotes/RELEASE_NOTES_1.17.5-zephcore.md @@ -1,93 +1,136 @@ -# ZephCore 1.17.5-zephcore - -> [!NOTE] -> **Draft — release in progress.** Covers what is on `dev` so far. - ---- - -## Bluetooth range on ESP32 boards was 9 dB down - -Every ESP32 board has been advertising and connecting at 0 dBm; stock MeshCore runs the same radios -at +9 dBm. Zephyr's driver builds its default from the `CONFIG_BT_CTLR_TX_PWR_*` chain, and with none -of them set it fell through to the 0 dB arm — silently, with nothing visible at build time or on the -node. ESP32 boards now transmit at +9 dBm. - -> [!NOTE] -> **Nothing to change on your side.** No setting, no re-pairing. nRF52, nRF54L and MG24 were never -> affected — they set their own transmit power. - ---- - -## GPS on Heltec WiFi LoRa 32 V4 and V4.3 - -Both boards bring a GPS UART and its power pins out to the header, and ZephCore described neither, so -attaching a module did nothing. They now match every other GPS board: NMEA on UART1 at 9600 baud, with -enable and reset driven rather than left floating. - -> [!NOTE] -> **For an external module.** Neither board ships with a receiver; boards with nothing attached are -> unaffected. - ---- - -## The confirmation prompt now works on single-button boards - -Shutdown, DFU and off-grid confirm with two ENTER presses. On single-button boards ENTER is a -one-second hold, and the window was half a second — so the second hold always landed too late and -re-armed the prompt instead of confirming. The window is now three seconds on every board that emits -ENTER through a long-press filter. Six had it; eight did not: - -| | | -|---|---| -| Heltec WiFi LoRa 32 V3 | Heltec Wireless Tracker | -| Heltec WiFi LoRa 32 V4 | Heltec Wireless Tracker V2 | -| Heltec WiFi LoRa 32 V4.3 | LilyGo T3-S3 | -| Meshnology W12 | TTGO T-Beam | - -Joystick boards (Wio Tracker L1, GAT562) use a different menu and were never affected. - -Thanks to **bisbille** for finding this and fixing the first two boards. - ---- - -## Changing frequency or spreading factor now resets adaptive CAD - -The learned CAD threshold is an offset from a per-SF, per-bandwidth base. Change the preset and the -base moves, but the node kept the old offset — leaving it too sensitive to transmit or too deaf to -defer. `set radio`, `set freq` and the app's radio settings now perform a full `set cad.reset` when -frequency, bandwidth or spreading factor changes. Coding rate is excluded: it changes airtime, not the -threshold. `tempradio` uses its preset's own base and hands the offset back on revert without writing -flash; `get cad` shows `a:tmp` while a window is open. - -> [!NOTE] -> **Nothing to change on your side.** The reset is automatic and the node re-converges in an hour or -> two. `set cad.reset` by hand is no longer needed after a preset change. - -Thanks to **Codes** for reporting it. - ---- - -## ProMicro SX1262: Bluetooth dropped every few seconds, and the screen never worked - -The nRF52840 SuperMini has no 32.768 kHz crystal, but the board was configured as though it did — so -the Bluetooth controller and the kernel tick ran off a floating oscillator while claiming 50 ppm -accuracy, and the link dropped every few seconds. It now uses the calibrated internal RC at 250 ppm, -matching stock MeshCore's settings for the same silicon. Timing that hangs off the same clock steadies -with it, including the return to receive after a transmission — so repeats of your own message, and -zero-hop ping replies, are no longer missed. - -The OLED was never described for this board at all. It is now an SSD1306 on the same I2C pins stock -MeshCore uses, optional at runtime. - -> [!NOTE] -> **Check transmit power if your module has no amplifier.** The 10 dBm default is the safe drive level -> for an E22-900M30S. On a bare module (HT-RA62, E22-900M22S) that is your antenna power — 12 dB under -> stock — which reads as no repeats and failed zero-hop pings. `set tx 22` once and it sticks. - -Thanks to **Mike's Allotment** for the report. - ---- - -## Also in this release - -*To be filled in as further changes land.* +# ZephCore 1.17.5-zephcore + +> [!NOTE] +> **Draft — release in progress.** Covers what is on `dev` so far. + +--- + +## Bluetooth range on ESP32 boards was 9 dB down + +Every ESP32 board has been advertising and connecting at 0 dBm; stock MeshCore runs the same radios +at +9 dBm. Zephyr's driver builds its default from the `CONFIG_BT_CTLR_TX_PWR_*` chain, and with none +of them set it fell through to the 0 dB arm — silently, with nothing visible at build time or on the +node. ESP32 boards now transmit at +9 dBm. + +> [!NOTE] +> **Nothing to change on your side.** No setting, no re-pairing. nRF52, nRF54L and MG24 were never +> affected — they set their own transmit power. + +--- + +## GPS on Heltec WiFi LoRa 32 V4 and V4.3 + +Both boards bring a GPS UART and its power pins out to the header, and ZephCore described neither, so +attaching a module did nothing. They now match every other GPS board: NMEA on UART1 at 9600 baud, with +enable and reset driven rather than left floating. + +> [!NOTE] +> **For an external module.** Neither board ships with a receiver; boards with nothing attached are +> unaffected. + +--- + +## The confirmation prompt now works on single-button boards + +Shutdown, DFU and off-grid confirm with two ENTER presses. On single-button boards ENTER is a +one-second hold, and the window was half a second — so the second hold always landed too late and +re-armed the prompt instead of confirming. The window is now three seconds on every board that emits +ENTER through a long-press filter. Six had it; eight did not: + +| | | +|---|---| +| Heltec WiFi LoRa 32 V3 | Heltec Wireless Tracker | +| Heltec WiFi LoRa 32 V4 | Heltec Wireless Tracker V2 | +| Heltec WiFi LoRa 32 V4.3 | LilyGo T3-S3 | +| Meshnology W12 | TTGO T-Beam | + +Joystick boards (Wio Tracker L1, GAT562) use a different menu and were never affected. + +Thanks to **bisbille** for finding this and fixing the first two boards. + +--- + +## Changing frequency or spreading factor now resets adaptive CAD + +The learned CAD threshold is an offset from a per-SF, per-bandwidth base. Change the preset and the +base moves, but the node kept the old offset — leaving it too sensitive to transmit or too deaf to +defer. `set radio`, `set freq` and the app's radio settings now perform a full `set cad.reset` when +frequency, bandwidth or spreading factor changes. Coding rate is excluded: it changes airtime, not the +threshold. `tempradio` uses its preset's own base and hands the offset back on revert without writing +flash; `get cad` shows `a:tmp` while a window is open. + +> [!NOTE] +> **Nothing to change on your side.** The reset is automatic and the node re-converges in an hour or +> two. `set cad.reset` by hand is no longer needed after a preset change. + +Thanks to **Codes** for reporting it. + +--- + +## ProMicro SX1262: Bluetooth dropped every few seconds, and the screen never worked + +The nRF52840 SuperMini has no 32.768 kHz crystal, but the board was configured as though it did — so +the Bluetooth controller and the kernel tick ran off a floating oscillator while claiming 50 ppm +accuracy, and the link dropped every few seconds. It now uses the calibrated internal RC at 250 ppm, +matching stock MeshCore's settings for the same silicon. Timing that hangs off the same clock steadies +with it, including the return to receive after a transmission — so repeats of your own message, and +zero-hop ping replies, are no longer missed. + +The OLED was never described for this board at all. It is now an SSD1306 on the same I2C pins stock +MeshCore uses, optional at runtime. + +That display also needed a second fix, found while bringing up the LR2021 EVK below, which had the +identical fault. On these Nordic chips the screen is redrawn in one large transfer, and the driver +needs somewhere to assemble it; the space reserved for that defaults to 16 bytes, far short of the +1025 a 128x64 screen needs. The panel answered every setup command and then never drew anything — +which looks exactly like a broken display rather than a misconfigured one. Both boards now reserve +enough. + +> [!NOTE] +> **The ProMicro half of this is unconfirmed on hardware.** The fault was identified from the LR2021 +> EVK's logs and the fix is the same single line, but nobody here has a ProMicro with a screen +> attached to check. If yours has an OLED, we would like to hear either way. + +> [!NOTE] +> **Check transmit power if your module has no amplifier.** The 10 dBm default is the safe drive level +> for an E22-900M30S. On a bare module (HT-RA62, E22-900M22S) that is your antenna power — 12 dB under +> stock — which reads as no repeats and failed zero-hop pings. `set tx 22` once and it sticks. + +Thanks to **Mike's Allotment** for the report. + +--- + +## New board: Semtech LR2021 LoRa Plus Evaluation Kit + +The **Semtech LR2021 LoRa Plus EVK** is now supported — Seeed's kit built around Semtech's +fourth-generation LoRa transceiver. It is three boards stacked: a XIAO nRF54L15 for the processor, the +LoRa Plus expansion board for the display, buttons, Grove ports and antenna sockets, and a Wio-LR2021 +radio module. Companion and repeater builds are both provided, and the 128x64 OLED and the expansion +board's user button work as they do on any other screen-equipped board. + +Build it with `seeed_lr2021_evk/nrf54l15/cpuapp`. It is a separate board from the plain **XIAO +nRF54L15**, which is the same processor on a Wio-SX1262 carrier — the two are wired differently and +the firmware is not interchangeable. + +> [!IMPORTANT] +> **Two things on the hardware to check before first power-on.** The small two-pin **IDCC** header +> feeds power to the radio module; if its jumper is missing the radio is simply unpowered and looks +> dead. And the radio module connects to the board's SMA sockets through **U.FL pigtails you fit +> yourself** — connect the sub-GHz (LF) one before transmitting. Transmitting at full power into an +> unconnected antenna port can damage the amplifier. + +> [!NOTE] +> **This board can only be flashed with SWD.** The nRF54L15 has no USB hardware, so there is no +> drag-and-drop UF2 and no update over a cable. The expansion board's USB-C socket reaches a SAMD11 +> debug bridge, which is enough on its own — no separate probe needed — and the same socket carries the +> console at 115200 baud. The firmware is published as a `.hex` file, and the Mesh America configurator +> lists the board as a download rather than offering to flash it. + +The kit's 2.4 GHz antenna port is not used. MeshCore is a sub-GHz protocol, so only the LF port carries +traffic. + +--- + +## Also in this release + +*To be filled in as further changes land.* diff --git a/zephcore/boards/example_board/README.md b/zephcore/boards/example_board/README.md index 64bb8d5..941aa29 100644 --- a/zephcore/boards/example_board/README.md +++ b/zephcore/boards/example_board/README.md @@ -143,11 +143,20 @@ revision. |----------------------|------------------------------------------------------------------------|-----------------| | XIAO nRF54L15 | `west build -b xiao_nrf54l15/nrf54l15/cpuapp zephcore --no-sysbuild` | `west flash` | | MinewSemi ME25LS02 | `west build -b me25ls02/nrf54l15/cpuapp zephcore --no-sysbuild` | SWD (`west flash`) | +| Semtech LR2021 LoRa Plus EVK | `west build -b seeed_lr2021_evk/nrf54l15/cpuapp zephcore --no-sysbuild` | `west flash` | Requires J-Link or CMSIS-DAP (built into XIAO board via SAMD11 bridge). The `--no-sysbuild` flag is required (no MCUboot support yet). -The SoC has no USB peripheral at all, so neither board has a UF2 or DFU path — `zephyr.hex` +The LR2021 EVK is the Seeed/Semtech kit (SKU 100039980): a XIAO nRF54L15 plus the +LoRa Plus Expansion Board plus a Wio-LR2021 module, all in XIAO sockets sharing one +set of D0..D10 nets. It is a distinct board from `xiao_nrf54l15` above, which is the +same MCU on a Wio-SX1262 carrier with incompatible wiring. Two hardware gotchas +before first boot: the **IDCC jumper** must be fitted or the radio has no power, and +the **LF U.FL pigtail** must be connected before transmitting. The board's `.dts` +header carries the full pin map and the reasoning behind each choice. + +The SoC has no USB peripheral at all, so none of these boards has a UF2 or DFU path — `zephyr.hex` links at RRAM base 0x0 and is the complete image, written over SWD. On the ME25LS02's MX25LE02 carrier the USB-C port is a CH340x UART bridge (console only), so it needs an external probe. diff --git a/zephcore/boards/nrf52840/promicro_sx1262/promicro_sx1262.dts b/zephcore/boards/nrf52840/promicro_sx1262/promicro_sx1262.dts index 31501ee..eeb57f3 100644 --- a/zephcore/boards/nrf52840/promicro_sx1262/promicro_sx1262.dts +++ b/zephcore/boards/nrf52840/promicro_sx1262/promicro_sx1262.dts @@ -174,6 +174,34 @@ pinctrl-1 = <&i2c0_sleep>; pinctrl-names = "default", "sleep"; + /* + * Required by the SSD1306 below, not an optimisation. + * + * The display driver flushes with i2c_burst_write_dt(): a 1-byte control + * message plus the whole 1024-byte framebuffer, no STOP between. TWIM is + * EasyDMA and needs one contiguous buffer, so both are copied into the + * concat buffer — which defaults to 16 bytes (nordic,nrf-twim.yaml). Every + * flush then fails with "(1 + 1024 > 16)" while the panel still ACKs its + * whole init sequence, so the logs say the display initialised and the + * screen stays dark. The binding is explicit that this property "must be + * provided when interacting with devices like the SSD1306 display". + * + * 1088 = 1 + 1024, rounded. Nothing else on this bus needs more: the + * sensors in sensors-i2c.dtsi all do short register transfers. + * + * Boards on the legacy "nordic,nrf-twi" driver (rak4631, gat562_30s) never + * hit this — it is not EasyDMA. But nRF52840's i2c0 defaults to + * "nordic,nrf-twim", which this node keeps, so this board does. + * lilygo_timpulse_plus sets 257 for the same reason, sized to its 64x32. + * An SH1106 (wio_tracker_l1) writes per page instead, so 1 + width is + * enough there. + * + * Found 2026-09-12 on seeed_lr2021_evk, whose display was dark for exactly + * this reason. This board's OLED was only described in the 1.17.5 draft, and this + * fix has NOT been confirmed on hardware here — no panel to hand. + */ + zephyr,concat-buf-size = <1088>; + /* SSD1306 OLED — optional; absent panels fail device_is_ready() and are skipped */ ssd1306: ssd1306@3c { compatible = "solomon,ssd1306"; diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.defconfig b/zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.defconfig new file mode 100644 index 0000000..66672b6 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +if BOARD_SEEED_LR2021_EVK_NRF54L15_CPUAPP + +config HAS_BT_CTLR + default BT + +endif # BOARD_SEEED_LR2021_EVK_NRF54L15_CPUAPP diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.seeed_lr2021_evk b/zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.seeed_lr2021_evk new file mode 100644 index 0000000..7890a33 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/Kconfig.seeed_lr2021_evk @@ -0,0 +1,6 @@ +# Semtech LR2021 LoRa Plus EVK board configuration +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +config BOARD_SEEED_LR2021_EVK + select SOC_NRF54L15_CPUAPP if BOARD_SEEED_LR2021_EVK_NRF54L15_CPUAPP diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/board.cmake b/zephcore/boards/nrf54l/seeed_lr2021_evk/board.cmake new file mode 100644 index 0000000..e139897 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/board.cmake @@ -0,0 +1,19 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT +# +# The nRF54L15 has no USB peripheral and this kit has no bootloader — the +# expansion board's USB-C goes to a SAMD11 that presents CMSIS-DAP for flashing +# and a USB CDC for the uart20 console. So openocd over CMSIS-DAP is the default +# path and is listed first; J-Link/nrfutil work through the SWD header (J7_SWD). +# +# support/openocd.cfg is Zephyr's XIAO nRF54L15 config, copied verbatim. It +# defaults the interface to cmsis-dap and carries the nRF54L CTRL-AP recovery +# procedure, which is what un-bricks a board whose APPROTECT got engaged. + +board_runner_args(openocd "--cmd-load=nrf54l-load" -c "targets nrf54l.cpu") +board_runner_args(jlink "--device=nRF54L15_M33" "--speed=4000") + +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/board.conf b/zephcore/boards/nrf54l/seeed_lr2021_evk/board.conf new file mode 100644 index 0000000..31a3d9c --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/board.conf @@ -0,0 +1,16 @@ +# Semtech LR2021 LoRa Plus EVK (XIAO nRF54L15 + Wio-LR2021) +# Board-specific configuration + +# Board identification +CONFIG_ZEPHCORE_BOARD_NAME="LR2021 LoRa Plus EVK" + +# Device Information Service model name +CONFIG_BT_DIS_MODEL_NUMBER_STR="Seeed LR2021 LoRa Plus EVK" + +# LR2021 radio driver +CONFIG_ZEPHCORE_RADIO_LR2021=y + +# Same SoC and RAM budget as xiao_nrf54l15 — 450 contacts is the ceiling that +# still fits. This board additionally carries the OLED UI, so if RAM gets tight +# this is the first knob to turn. +CONFIG_ZEPHCORE_MAX_CONTACTS=450 diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/board.yml b/zephcore/boards/nrf54l/seeed_lr2021_evk/board.yml new file mode 100644 index 0000000..36ee542 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/board.yml @@ -0,0 +1,9 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +board: + name: seeed_lr2021_evk + full_name: Semtech LR2021 LoRa Plus EVK (XIAO nRF54L15 + Wio-LR2021) + vendor: seeed + socs: + - name: nrf54l15 diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/bringup.conf b/zephcore/boards/nrf54l/seeed_lr2021_evk/bringup.conf new file mode 100644 index 0000000..ce546d7 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/bringup.conf @@ -0,0 +1,48 @@ +# LR2021 LoRa Plus EVK bring-up logging. +# +# Everything needed to see why a peripheral did not come up, in one build: +# west build -b seeed_lr2021_evk/nrf54l15/cpuapp zephcore --pristine --no-sysbuild -- \ +# -DEXTRA_CONF_FILE="boards/common/debug.conf;boards/nrf54l/seeed_lr2021_evk/bringup.conf" +# +# Not for daily use — this is loud and costs RAM. +# +# The console is uart20 through the expansion board's SAMD11 bridge, and that +# bridge's USB CDC stays enumerated on the host across nRF resets. So unlike the +# native-USB nRF52 boards, nothing is lost at boot: open the terminal first, then +# press RESET, and the log starts from line one. That is why this file does NOT +# carry meshtracker_x1's LOG_PROCESS_THREAD_STARTUP_DELAY_MS workaround — there +# is no enumeration race to lose the early messages to. + +# LR2021: chip dumps, modem_cfg lines, decoded error words, raw SPI frames. +# +# This is the setting that answers the board's one genuinely open hardware +# question. The module schematic says 32 MHz crystal with the VTCXO pin wired as +# an NTC input, so the board declares no TCXO — but Semtech's own shield overlay +# for this kit declares a 1.8 V TCXO instead. At DBG the driver prints +# "init: TCXO disabled (XTAL mode)" when it takes our reading, and the whole +# HF_XOSC_START path is visible if it turns out we are wrong. +# +# Worth knowing which way the safety net runs: the driver's TCXO->XTAL fallback +# is gated on `tcxo_voltage_mv > 0`, so it only rescues the "declared a TCXO that +# is not there" direction — the one that wedged promicro_lr2021. This board +# declares none, so if the schematic reading were wrong the OTHER way there is no +# automatic recovery. It is not silent though: dump_chip_state() logs the chip's +# error word at LOG_INF on every init step, and HF_XOSC_START is bit 0. So +# `[post-cal] ... err=0x0001` is the tell. (Arduino MeshCore instead guesses TCXO +# first and retries at 0 V on RadioLib -706/-707 — see +# src/helpers/radiolib/CustomLR2021.h, which is where our fallback came from.) +CONFIG_LORA_LOG_LEVEL_DBG=y + +# The OLED's I2C address is strapped by JP1 on the expansion board (0x3C default, +# 0x3D jumpered). A blank screen reads like a broken display driver, so make the +# bus and the display driver say which address they actually talked to. +CONFIG_I2C_LOG_LEVEL_INF=y +CONFIG_DISPLAY_LOG_LEVEL_DBG=y + +# K1 on P2.09 has no external pull-up and no hardware debounce — confirm the +# presses arrive and that the longpress/multitap chain sees them. +CONFIG_INPUT_LOG_LEVEL_DBG=y + +# This board's RRAM partition layout is new: app links at 0x0, LittleFS at +# 0x14E000. Show why a mount failed, not just that it did. +CONFIG_FS_LOG_LEVEL_DBG=y diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/pre_dt_board.cmake b/zephcore/boards/nrf54l/seeed_lr2021_evk/pre_dt_board.cmake new file mode 100644 index 0000000..3789630 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/pre_dt_board.cmake @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT +# Suppress "unique_unit_address_if_enabled" to handle some overlaps +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk-pinctrl.dtsi b/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk-pinctrl.dtsi new file mode 100644 index 0000000..b0eb334 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk-pinctrl.dtsi @@ -0,0 +1,68 @@ +/* + * Semtech LR2021 LoRa Plus EVK — pin control + * Copyright (c) 2026 ZephCore + * SPDX-License-Identifier: MIT + * + * Pin assignments are the XIAO nRF54L15's fixed connector mapping, copied from + * Zephyr's xiao_nrf54l15-pinctrl.dtsi. Only the three buses this kit actually + * uses are declared: + * + * uart20 P1.09 TX / P1.08 RX — console, via the SAMD11 CMSIS-DAP bridge + * i2c22 P1.10 SDA / P1.11 SCL — D4/D5: OLED + the two I2C Grove ports + * spi00 P2.01 SCK / P2.02 MOSI / P2.04 MISO — D8/D10/D9: LR2021 + * + * uart21 (D6/D7) is deliberately absent — see the DIO7/DIO11 note in the .dts. + */ + +&pinctrl { + /omit-if-no-ref/ uart20_default: uart20_default { + group1 { + psels = ; + }; + + group2 { + psels = ; + bias-pull-up; + }; + }; + + /omit-if-no-ref/ uart20_sleep: uart20_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /omit-if-no-ref/ i2c22_default: i2c22_default { + group1 { + psels = , + ; + }; + }; + + /omit-if-no-ref/ i2c22_sleep: i2c22_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /omit-if-no-ref/ spi00_default: spi00_default { + group1 { + psels = , + , + ; + }; + }; + + /omit-if-no-ref/ spi00_sleep: spi00_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp.dts b/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp.dts new file mode 100644 index 0000000..ff0a9d7 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp.dts @@ -0,0 +1,480 @@ +/* + * Semtech LR2021 LoRa Plus Evaluation Kit (Seeed SKU 100039980) + * Copyright (c) 2026 ZephCore + * SPDX-License-Identifier: MIT + * + * Three boards in a XIAO-socket sandwich, not one board: + * + * Seeed XIAO nRF54L15 — the MCU + * LoRa Plus Expansion Board V2a — OLED, buttons, Grove, SMA, SWD, USB-C + * Wio-LR2021 module — LR2021WIO1XDCS, 868/915 MHz, switchless + * + * Both modules plug into the same expansion board and share ONE set of D0..D10 + * nets. There is no bus mux: whatever the MCU drives on D0..D10 reaches the + * radio module directly. + * + * Verified 2026-09-12 against primary sources, all kept in + * devdocs/seeed_lr2021_evk/ — the Wio-LR2021 module schematic (e769v02a), the + * expansion board schematic (e758v03a), the XIAO nRF54L15 schematic, and Seeed's + * wiki pin table. Every control-pin assignment below is independently confirmed + * by Semtech's own devicetree for this kit (github.com/Lora-net/usp_zephyr, + * boards/shields/semtech_wio_lr20xx/), saved alongside those. + * + * XIAO connector map (Seeed wiki table / Zephyr seeed_xiao_connector.dtsi): + * D0 P1.04 LR2021 IRQ (chip DIO8) D8 P2.01 SPI SCK (spi00) + * D1 P1.05 LR2021 BUSY D9 P2.04 SPI MISO (spi00) + * D2 P1.06 LR2021 NRESET D10 P2.02 SPI MOSI (spi00) + * D3 P1.07 LR2021 NSS + * D4 P1.10 I2C SDA (i2c22) — OLED + 2x Grove + * D5 P1.11 I2C SCL (i2c22) — OLED + 2x Grove + * D6 P2.08 LR2021 DIO7 / uart21 TX / Grove UART (see the note at uart21) + * D7 P2.07 LR2021 DIO11 / uart21 RX / Grove UART + * D14 P2.09 K1 user button on the expansion board + * + * Hardware prerequisites, both easy to miss and both present as "the firmware + * does not work": + * - The IDCC 2-pin header carries SYS_3V3 to the radio module. Open jumper = + * the LR2021 has no power and probes as a dead chip. A shunt (or an ammeter) + * must be fitted. + * - The module terminates in U.FL; the SMA connectors are on the expansion + * board. Both are joined by pigtails the user fits. Do NOT transmit with the + * LF pigtail missing — +22 dBm into an open is a PA-damage risk, not just + * poor range. + * + * Flashing: the nRF54L15 has no USB peripheral, so there is no UF2 and no DFU. + * The expansion board's USB-C goes to a SAMD11 presenting CMSIS-DAP for SWD + * flashing plus a USB CDC for the uart20 console. Published artifact is + * zephyr.hex, which links at RRAM base 0x0. + */ + +/dts-v1/; +#include +#include "seeed_lr2021_evk-pinctrl.dtsi" +#include + +/ { + model = "Semtech LR2021 LoRa Plus EVK"; + compatible = "seeed,lr2021-evk"; + + chosen { + zephyr,sram = &cpuapp_sram; + zephyr,flash = &cpuapp_rram; + zephyr,flash-controller = &rram_controller; + zephyr,console = &uart20; + zephyr,shell-uart = &uart20; + zephyr,display = &ssd1306; + /* + * Deliberately no zephyr,code-partition: there is no bootloader + * on this kit, so the image links at RRAM base 0x0 and the ROM + * starts it directly. Same choice as me25ls02, the other + * bootloader-less nRF54L15 board here. + */ + }; + + aliases { + lora0 = &lora; + led0 = &user_led; + sw0 = &user_button; + }; + + leds { + compatible = "gpio-leds"; + + /* On the XIAO core board itself, not the expansion board. */ + user_led: led_0 { + gpios = <&gpio2 0 GPIO_ACTIVE_HIGH>; + label = "User LED"; + }; + }; + + buttons: buttons { + compatible = "gpio-keys"; + + /* + * K1 on the expansion board: a bare TS-1111A between P2.09 and + * GND, no external pull-up and no hardware debounce, so the + * internal pull-up is required. + * + * The XIAO core board carries a second user button of its own on + * P0.00 (also pull-up / active low). It is deliberately left out: + * the UI is built around one button, and K1 is the one the user + * can actually reach with the boards stacked. + */ + user_button: button_0 { + gpios = <&gpio2 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + zephyr,code = ; + label = "User Button"; + }; + }; + + /* Button chain — same mapping as RAK4631 (rak4631/board.overlay). */ + user_btn_longpress { + compatible = "zephyr,input-longpress"; + input = <&buttons>; + input-codes = ; + short-codes = ; + long-codes = ; + long-delay-ms = <1000>; + }; + + page_btn_multitap { + compatible = "zephcore,input-multi-tap"; + input-codes = ; + tap-codes = ; + tap-delay-ms = <400>; + }; + + /* + * XIAO 2.4 GHz antenna switch. P2.05 (RF_SW_CTL) and P2.03 (RF_SW_PWR) + * gate the switch between the on-board chip antenna and the U.FL, per the + * XIAO schematic. Both are boot-on regulators exactly as upstream Zephyr + * declares them — drop either and the SoC's own BLE radio comes up deaf. + * Nothing to do with the LR2021, which has its own separate front end. + */ + rfsw_ctl: rfsw-ctl { + compatible = "regulator-fixed"; + regulator-name = "rfsw-ctl"; + enable-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + regulator-boot-on; + }; + + rfsw_pwr: rfsw-pwr { + compatible = "regulator-fixed"; + regulator-name = "rfsw-pwr"; + enable-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>; + regulator-boot-on; + }; + + /* + * Battery sense rail enable (P1.15), gating the 2:1 divider on P1.14/AIN7. + * Declared so the rail is powered, but the ADC channel is NOT enabled — + * see the block at the bottom of this file for why. + */ + vbat_pwr: vbat-pwr { + compatible = "regulator-fixed"; + regulator-name = "vbat"; + enable-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + regulator-boot-on; + }; +}; + +&cpuapp_sram { + status = "okay"; +}; + +&grtc { + owned-channels = <0 1 2 3 4 5 6 7 8 9 10 11>; + /* Channels 7-11 reserved for Zero Latency IRQs, 3-4 for FLPR */ + child-owned-channels = <3 4 7 8 9 10 11>; + status = "okay"; +}; + +&clock { + status = "okay"; +}; + +/* Console: P1.09 TX / P1.08 RX -> SAMD11 CMSIS-DAP bridge -> USB CDC */ +&uart20 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart20_default>; + pinctrl-1 = <&uart20_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* + * uart21 (D6/D7) stays disabled, and this is not just tidiness. + * + * A6_D6_TX and A7_D7_RX are simultaneously the nRF54L15's uart21, the Grove + * UART connector J6, SWD header pins 7/8, AND — through the Wio-LR2021 + * castellation — the radio's DIO7 and DIO11. Driving them as a UART toggles two + * of the LR2021's DIOs. We configure neither DIO for anything (DIO8 is the IRQ, + * and the front end is switchless so no DIO drives an RF switch), so it is very + * likely harmless, but the coupling is real and Semtech's own sample overlay + * disables this UART too. If a Grove UART peripheral is ever wanted here, that + * needs bench verification, not reasoning. + */ +&uart21 { + status = "disabled"; +}; + +/* + * i2c22 = D4/D5. Free for the OLED and the two I2C Grove ports because the + * Wio-LR2021 module leaves its pins 5/6 (SDA/SCL) unconnected — see the + * castellation table in devdocs/seeed_lr2021_evk/HARDWARE.md. + * + * Note this is the opposite of boards/nrf54l/xiao_nrf54l15, where the same bus + * is disabled because that carrier puts the SX1262's NSS and RXEN on D4/D5. + */ +&i2c22 { + status = "okay"; + pinctrl-0 = <&i2c22_default>; + pinctrl-1 = <&i2c22_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = <400000>; + + /* + * Mandatory for the SSD1306, not an optimisation. + * + * The display driver flushes the framebuffer with i2c_burst_write_dt(): + * a 1-byte control message and the 1024-byte framebuffer, with no STOP + * between them. TWIM is EasyDMA and needs one contiguous buffer, so the + * driver copies both into the concat buffer — which defaults to 16 bytes + * (nordic,nrf-twim.yaml). Every flush then fails with + * "Need to use the internal driver buffer but its size is insufficient + * (1 + 1024 > 16)" + * and the panel stays dark even though it ACKed its whole init sequence. + * That is exactly what happened on the first hardware bring-up, 2026-09-12. + * The binding says as much: this property "must be provided when + * interacting with devices like the SSD1306 display". + * + * 1088 = 1 + 1024, rounded up. Semtech's shield overlay and Zephyr's own + * ssd1306 shield both use 4096, but this is a static RAM buffer and the + * companion build already sits near 88% of RAM, so it is sized to the + * largest transfer this bus can actually carry: nothing else lives on + * i2c22 but the panel and the (unpopulated) Grove ports. + * + * Boards driving an SSD1306 over "nordic,nrf-twi" — the legacy non-DMA + * driver, which is what rak4631 and gat562_30s use — do not need this. + * Boards on "nordic,nrf-twim" do, sized to their own panel: + * lilygo_timpulse_plus sets 257 for its 64x32 (1 + 256). + */ + zephyr,concat-buf-size = <1088>; + + /* + * ZJY-2864KSWPG01, an SSD1306-class 128x64 panel strapped for IIC. + * + * Address: JP1 on the expansion board selects it via the panel's D/C pin + * (R24 4.7K to GND / R26 0R / R25 4.7K to VDD). 0x3C is the default + * strap and what Semtech's own overlay uses; 0x3D is the jumpered + * alternative. If the display stays blank, probe the bus before + * suspecting anything else. + * + * No reset-gpios: /RES is an RC power-on reset on the expansion board + * (R21 4.7K to VDD, C7 10uF to GND, D2 1N4148 discharge), not an MCU pin. + * + * Panel settings mirror Semtech's shield overlay for this exact kit. + */ + ssd1306: ssd1306@3c { + compatible = "solomon,ssd1306"; + reg = <0x3c>; + width = <128>; + height = <64>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <63>; + segment-remap; + com-invdir; + inversion-on; + prechargep = <0x22>; + }; +}; + +/* i2c30 (D11/D12) reaches only the Arduino headers, and the XIAO's own IMU, + * which ZephCore does not use. Left off to save the bus and its power. */ +&i2c30 { + status = "disabled"; +}; + +&spi00 { + status = "okay"; + cs-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; /* D3 = P1.07 NSS */ + pinctrl-0 = <&spi00_default>; + pinctrl-1 = <&spi00_sleep>; + pinctrl-names = "default", "sleep"; + + /* + * Nordic's 0xFF idle byte is read by the LR2021 as a bogus opcode, so the + * bus must idle at the chip's NOP. Both promicro_lr2021 and Semtech's own + * shield overlay do this; it is not optional. + */ + overrun-character = <0x00>; + + /* + * SoC default rx-delay is kept. xiao_nrf54l15 — the same MCU board, same + * spi00, 8 MHz — keeps it and works; me25ls02 deletes it because + * MinewSemi's sample does. Neither has been A/B'd on hardware, so each + * board keeps its own known-good configuration rather than guessing. + */ + + lora: lora@0 { + compatible = "semtech,lr2021"; + reg = <0>; + /* + * 8 MHz, matching meshtracker_x1 and promicro_lr2021. Semtech's + * shield runs this module at 16 MHz — worth trying later as its + * own change, once the link is known good at 8. + */ + spi-max-frequency = <8000000>; + + reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; /* D2 = P1.06 */ + busy-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; /* D1 = P1.05 */ + dio1-gpios = <&gpio1 4 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; /* D0 = P1.04 */ + irq-dio = <8>; /* chip DIO8 -> module pin 1 -> D0 */ + + /* + * NO TCXO. The module schematic shows crystal Q1, 32 MHz, across + * XTA/XTB, and the chip's dual-function VTCXO/VNTC pin is wired + * as the NTC thermistor input (R3 + ERT-JZER104F), not as a TCXO + * supply. So tcxo-voltage-mv stays at its default of 0. + * + * Semtech's own shield overlay declares a 1.8 V TCXO here, which + * contradicts their schematic. The schematic wins. This is worth + * being firm about: declaring a TCXO on a module that has none + * WEDGED the chip on promicro_lr2021 — HF-XOSC startup hang, BUSY + * stuck, BLE never came up. + */ + + rx-boosted; + + /* + * Switchless dual-band front end: RFO_LF and RFO_HF each drive + * their own fixed matching network straight to their own U.FL. + * There is no antenna switch and no DIO driving one, so every + * rfswitch-* mask stays at its default of 0 — same as + * meshtracker_x1. ANT_LF is the sub-GHz SMA; the HF/2.4 GHz path + * is simply unused by MeshCore. + */ + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +/* P1.x and P2.x — the LR2021 IRQ (P1.04) and the K1 button (P2.09). */ +&gpiote20 { + status = "okay"; +}; + +&gpiote30 { + status = "okay"; +}; + +®ulators { + status = "okay"; +}; + +&vregmain { + status = "okay"; + regulator-initial-mode = ; +}; + +/* + * The XIAO fits its own 32 MHz and 32.768 kHz crystals and uses the SoC's + * internal load capacitors for both. Values copied verbatim from upstream + * Zephyr's xiao_nrf54l15 DTS. + * + * LFXO being real is the notable difference from me25ls02, which has no + * 32.768 kHz crystal and runs the calibrated RC at +/-250 ppm. This board gets + * crystal accuracy, so it is the better of the two for anything timing-sensitive. + * + * Unrelated, and worth not confusing: the Wio-LR2021 module has no 32 kHz + * crystal of its own, so the LR2021's internal LF clock is RC. We never use the + * chip's LF timer, so that does not matter here — but it is the reason not to + * copy an LF-XOSC setting onto the radio node from another board. + */ +&lfxo { + load-capacitors = "internal"; + load-capacitance-femtofarad = <16000>; + status = "okay"; +}; + +&hfxo { + load-capacitors = "internal"; + load-capacitance-femtofarad = <16000>; + status = "okay"; +}; + +&radio { + status = "okay"; +}; + +&temp { + status = "okay"; +}; + +/* Default Nordic RRAM map, then re-carved below for ZephCore. */ +#include + +/* + * RRAM layout (1524 KB total, 0x17D000). + * + * No bootloader: the application starts at 0x0 and the linker is given the + * whole RRAM, so slot0/slot1 are meaningless here and are deleted to keep + * anyone from assuming a DFU layout that does not exist. Layout matches + * me25ls02, the other bootloader-less nRF54L15 board. + * + * 0x000000 - 0x14E000 (1336 KB) application (links from 0x0) + * 0x14E000 - 0x165000 (92 KB) LittleFS: identity, prefs, contacts, + * channels, BLE bonds + * 0x165000 - 0x17D000 (96 KB) unused tail + */ +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &storage_partition; + +&cpuapp_rram { + partitions { + lfs_partition: partition@14e000 { + compatible = "zephyr,mapped-partition"; + label = "lfs"; + reg = <0x14E000 0x17000>; + }; + }; +}; + +/* LittleFS auto-mount — standard /lfs mount point */ +#include "../../common/filesystem.dtsi" + +/* + * Battery voltage — NOT enabled, deliberately. Same reasoning as + * boards/nrf54l/xiao_nrf54l15/board.overlay, which shares this hardware. + * + * The divider is there (2:1 into P1.14/AIN7, gated by vbat_pwr above), but + * ZephyrBoard computes `mV = raw * vbat-mv-multiplier / 4096`, so the multiplier + * has to encode the ADC's full-scale voltage — and the nRF54L15 SAADC is not the + * nRF52 part we have that number for. Its internal reference is 0.9 V, not + * 0.6 V, and DS v1.0 section 8.18.7 states the single-ended range as + * "+/- 0.5*VREF/GAIN" while the saturation condition two lines later implies a + * full span of VREF/GAIN. Those two readings differ by a factor of two, and + * getting it wrong by 2x means a healthy pack reports as flat. + * + * To finish this: enable the block below on a board with a known cell voltage, + * read `raw`, and solve for the multiplier. With gain 1/4 and the 0.9 V + * reference the full scale is either 3.6 V (multiplier 7200 for the 2:1 + * divider) or 1.8 V (multiplier 3600). One measurement settles it — and it + * settles it for the XIAO board too. + * + * &adc { + * status = "okay"; + * + * #address-cells = <1>; + * #size-cells = <0>; + * + * channel@7 { + * reg = <7>; + * zephyr,gain = "ADC_GAIN_1_4"; + * zephyr,reference = "ADC_REF_INTERNAL"; + * zephyr,acquisition-time = ; + * zephyr,input-positive = ; + * zephyr,resolution = <12>; + * }; + * }; + * + * / { + * zephyr,user { + * io-channels = <&adc 7>; + * vbat-mv-multiplier = <7200>; + * }; + * }; + */ diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp_defconfig b/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp_defconfig new file mode 100644 index 0000000..c334067 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/seeed_lr2021_evk_nrf54l15_cpuapp_defconfig @@ -0,0 +1,32 @@ +# Semtech LR2021 LoRa Plus EVK default configuration +# SPDX-License-Identifier: MIT + +CONFIG_ARM_MPU=y +CONFIG_HW_STACK_PROTECTION=y +CONFIG_NULL_POINTER_EXCEPTION_DETECTION_NONE=y + +CONFIG_CACHE_MANAGEMENT=y +CONFIG_EXTERNAL_CACHE=y + +# Console on uart20 -> SAMD11 CMSIS-DAP bridge -> USB CDC +# (the nRF54L15 has no USB peripheral of its own) +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +CONFIG_GPIO=y +CONFIG_PINCTRL=y +CONFIG_SPI=y +CONFIG_I2C=y + +# RRAM + LittleFS +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +CONFIG_NRF_GRTC_START_SYSCOUNTER=y + +# rfsw_ctl / rfsw_pwr are regulator-fixed with regulator-boot-on, and they gate +# the XIAO's own 2.4 GHz antenna switch — without the regulator subsystem BLE +# comes up deaf. The init priority matches upstream's XIAO defconfig. +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_INIT_PRIORITY=45 diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk/support/openocd.cfg b/zephcore/boards/nrf54l/seeed_lr2021_evk/support/openocd.cfg new file mode 100644 index 0000000..717b12c --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk/support/openocd.cfg @@ -0,0 +1,182 @@ +# SPDX-License-Identifier: Apache-2.0 + +if {[info exists env(OPENOCD_INTERFACE)]} { + set INTERFACE $env(OPENOCD_INTERFACE) +} else { + # By default connect over Debug USB port using the CMSIS-DAP interface + set INTERFACE "cmsis-dap" +} + +source [find interface/$INTERFACE.cfg] + +set _CHIPNAME nrf54l + +# Work-area is a space in RAM used for flash programming, by default use 16 KiB. +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x4000 +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x6ba02477 +} + +# Multidrop instance ID should be configurable by FW in TAD TINSTANCE register. +# Writes to the register are ignored due to a silicon erratum. +if { [info exists SWD_INSTANCE_ID] } { + set _SWD_INSTANCE_ID $SWD_INSTANCE_ID +} else { + set _SWD_INSTANCE_ID 0 +} + +transport select swd + +swd newdap $_CHIPNAME cpu -expected-id $_CPUTAPID + +if { [info exists SWD_MULTIDROP] } { + dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu -dp-id 0x001c0289 -instance-id $_SWD_INSTANCE_ID +} else { + dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu +} + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap -ap-num 0 + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# Create target for the AUX access port (AUX-AP). +target create $_CHIPNAME.aux mem_ap -dap $_CHIPNAME.dap -ap-num 1 + +# AUX-AP is accessible only if CSW Prot[0] bit (Data Access) is set +$_CHIPNAME.dap apsel 1 +$_CHIPNAME.dap apcsw 0x01000000 0x01000000 + +adapter speed 1000 + +# Use main processor as default target. +targets $_TARGETNAME + +if {![using_hla]} { + $_TARGETNAME cortex_m reset_config sysresetreq +} + +proc nrf54l-load {file} { + mww 0x5004b500 0x101 + load_image $file +} + +# Define CTRL_AP_NUM explicitly to avoid variable errors +set CTRL_AP_NUM 2 + +# Custom _nrf_check_ap_lock for nRF54L (adjusted unlocked_value to 1, based on PyOCD's CSW check) +proc _nrf_check_ap_lock { ctrl_ap_num unlocked_value } { + set target [target current] + set dap [$target cget -dap] + set err [catch {set APPROTECTSTATUS [$dap apreg $ctrl_ap_num 0xc]}] ;# 0xc is ERASEPROTECTSTATUS register + if {$err == 0 && $APPROTECTSTATUS < $unlocked_value} { + echo "\[$target\] device has AP lock engaged, trying recover." + poll off + return 1 + } + return 0 +} + +# Custom _nrf_ctrl_ap_recover for nRF54L (adjusted IDR to 0x32880000) +proc _nrf_ctrl_ap_recover { ctrl_ap_num {is_cpunet 0} } { + set target [target current] + set dap [$target cget -dap] + + set IDR [$dap apreg $ctrl_ap_num 0xfc] + if {$IDR != 0x32880000} { ;# IDR for nRF54L CTRL-AP + echo "Error: Cannot access nRF54L CTRL-AP! (IDR: 0x$IDR)" + return + } + + poll off + + # Reset and trigger ERASEALL task + $dap apreg $ctrl_ap_num 4 0 ;# CTRL_AP_ERASEALL = 0x004 + $dap apreg $ctrl_ap_num 4 1 + + # First, wait for BUSY state + set timeout 300 ;# ~30s + for {set i 0} {$i < $timeout} {incr i} { + set ERASEALLSTATUS [$dap apreg $ctrl_ap_num 8] + if {$ERASEALLSTATUS == 2} { ;# BUSY status + break + } + if {$ERASEALLSTATUS == 3} { ;# ERROR status + echo "Error: Erase failed with ERROR status." + return + } + sleep 100 + } + if {$i >= $timeout} { + echo "Error: Timeout waiting for BUSY status." + return + } + + # Then, wait for READYTORESET state + for {set i 0} {$i < $timeout} {incr i} { + set ERASEALLSTATUS [$dap apreg $ctrl_ap_num 8] + if {$ERASEALLSTATUS == 1} { ;# READYTORESET status + echo "\[$target\] device has been successfully erased and unlocked." + break + } + if {$ERASEALLSTATUS == 3} { ;# ERROR status + echo "Error: Erase failed with ERROR status." + break + } + sleep 100 + } + if {$i >= $timeout} { + echo "Error: Timeout waiting for READYTORESET status." + return + } + + # Short delay before reset + sleep 10 + + # Assert reset: write 2 then 0 + $dap apreg $ctrl_ap_num 0 2 ;# CTRL_AP_RESET = 0x000 + sleep 10 + $dap apreg $ctrl_ap_num 0 0 + + # Reset ERASEALL task + $dap apreg $ctrl_ap_num 4 0 + + if { $is_cpunet } { + reset init + } else { + sleep 200 ;# Slightly increased delay + $target arp_examine + poll on + } +} + +lappend _telnet_autocomplete_skip _nrf_check_ap_lock _nrf_ctrl_ap_recover + +if { ![using_hla] } { + # Check AP lock on examine-fail and auto-recover if locked + $_TARGETNAME configure -event examine-fail { + global CTRL_AP_NUM + set target [target current] + if { [_nrf_check_ap_lock $CTRL_AP_NUM 1] } { + nrf54l_mass_erase + $target arp_examine ;# Re-examine after recover + } + } + + # Mass erase and unlock the device using proprietary nRF CTRL-AP (AP #2) + proc nrf54l_mass_erase {} { + global CTRL_AP_NUM + _nrf_ctrl_ap_recover $CTRL_AP_NUM ;# No cpunet for nRF54L + } + add_help_text nrf54l_mass_erase "Mass erase flash and unlock nRF54L device" +} + +flash bank $_CHIPNAME.flash nrf5 0x00000000 0x0017D000 0 0 $_TARGETNAME +# flash bank $_CHIPNAME.uicr nrf5 0x00FFD000 0 0 0 $_TARGETNAME