mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-17 09:54:37 +00:00
semtech lr2021 evaluation kit port
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.*
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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")
|
||||
@@ -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 = <NRF_PSEL(UART_TX, 1, 9)>;
|
||||
};
|
||||
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 1, 8)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ uart20_sleep: uart20_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 1, 9)>,
|
||||
<NRF_PSEL(UART_RX, 1, 8)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ i2c22_default: i2c22_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 1, 10)>,
|
||||
<NRF_PSEL(TWIM_SCL, 1, 11)>;
|
||||
};
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ i2c22_sleep: i2c22_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 1, 10)>,
|
||||
<NRF_PSEL(TWIM_SCL, 1, 11)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ spi00_default: spi00_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 2, 1)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 2, 2)>,
|
||||
<NRF_PSEL(SPIM_MISO, 2, 4)>;
|
||||
};
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ spi00_sleep: spi00_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 2, 1)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 2, 2)>,
|
||||
<NRF_PSEL(SPIM_MISO, 2, 4)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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 <nordic/nrf54l15_cpuapp.dtsi>
|
||||
#include "seeed_lr2021_evk-pinctrl.dtsi"
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
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 = <INPUT_KEY_0>;
|
||||
label = "User Button";
|
||||
};
|
||||
};
|
||||
|
||||
/* Button chain — same mapping as RAK4631 (rak4631/board.overlay). */
|
||||
user_btn_longpress {
|
||||
compatible = "zephyr,input-longpress";
|
||||
input = <&buttons>;
|
||||
input-codes = <INPUT_KEY_0>;
|
||||
short-codes = <INPUT_KEY_A>;
|
||||
long-codes = <INPUT_KEY_ENTER>;
|
||||
long-delay-ms = <1000>;
|
||||
};
|
||||
|
||||
page_btn_multitap {
|
||||
compatible = "zephcore,input-multi-tap";
|
||||
input-codes = <INPUT_KEY_A>;
|
||||
tap-codes = <INPUT_KEY_1 INPUT_KEY_LEFT>;
|
||||
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 = <NRF5X_REG_MODE_DCDC>;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 <vendor/nordic/nrf54l15_cpuapp_partition.dtsi>
|
||||
|
||||
/*
|
||||
* 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 = <ADC_ACQ_TIME_DEFAULT>;
|
||||
* zephyr,input-positive = <NRF_SAADC_AIN7>;
|
||||
* zephyr,resolution = <12>;
|
||||
* };
|
||||
* };
|
||||
*
|
||||
* / {
|
||||
* zephyr,user {
|
||||
* io-channels = <&adc 7>;
|
||||
* vbat-mv-multiplier = <7200>;
|
||||
* };
|
||||
* };
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user