Arduino BSP for Nordic nRF54L05 / nRF54L10 / nRF54L15 with the
s145 SoftDevice v9.0.0. Derived from Adafruit_nRF52_Arduino and
reworked for nRF54L peripherals.
PlatformIO only. Consumed exclusively through
caveman99/platform-nordicnrf54.
There is no platform.txt, boards.txt, or Arduino IDE / Board
Manager support. Do not propose adding them.
| Repo | Role |
|---|---|
caveman99/platform-nordicnrf54 |
PlatformIO platform: build flags, board JSONs, upload tooling, examples |
caveman99/nRF54_Bootloader |
DFU bootloader source + per-board hex. PIO package framework-arduinoadafruitnrf54-bootloader |
- Never add
Co-Authored-By: Claude(or any AI attribution) trailers. - Imperative subject, terse but specific. Reference SHAs of related prior commits where helpful.
- Multi-paragraph body for non-trivial changes: state the symptom, the root cause, and what the fix does.
- Default to writing no comments. Add one only when the why is non-obvious — a hidden constraint, a workaround, a behavior that would surprise a reader. Don't explain what the code does.
- Don't add backwards-compat shims, dead branches, or speculative abstractions. Trust framework guarantees.
- C++17 is the standard; no RTTI, no exceptions (set by the platform).
Some fixes belong in platform-nordicnrf54
(builder/frameworks/arduino/adafruit.py), not here:
- Build flags (
-mcpu,-mfpu, include paths, CPPDEFINES) - Board JSON metadata
- Upload tooling
- LDF / library resolution
Some fixes belong here (the framework):
- C/C++ source code, headers, linker scripts, variants
library.propertiesfor bundled librariesnrfx_config.hand per-driver guards
When in doubt: if the change affects how the compiler is invoked, it's platform-side. If it affects what gets compiled, it's framework-side.
The platform's
builder/frameworks/arduino/adafruit.py
adds -include cores/nRF5/nordic/nrf54l_compat.h to CCFLAGS. This
aliases nRF52 peripheral names (NRF_PWM0..2, NRF_TWIM0/1,
NRF_SPIM0/2, NRF_UARTE0/1, NRF_GPIOTE, NRF_TIMER2, etc.) to
their nRF54L equivalents. Most of the Adafruit code compiles
unmodified because of this header.
Scope rule for nrf54l_compat.h: symbol-name aliases only.
Register-shape changes (split-bank INTENSETn, split
DRIVE0/DRIVE1 fields) must NOT be aliased — the nrfx HAL uses
#if defined(...) checks on the legacy macros as chip-detection
markers, and aliasing them makes the HAL take the wrong branch.
Rewrite the consuming source in those cases (see WInterrupts.c
and wiring_digital.c for two existing precedents).
Every driver .c file in cores/nRF5/nordic/nrfx/drivers/src/ is
wrapped in #if NRFX_CHECK(NRFX_<X>_ENABLED) ... #endif, and
cores/nRF5/nordic/nrfx_config.h defaults every driver to
_ENABLED = 1. Drivers are disabled only after the compiler flags
a specific error. Current disable list (with the error that prompted
each):
NRFX_ADC_ENABLED = 0—NRF_ADC / ADC_IRQnundeclared (nRF51 legacy ADC)NRFX_BELLBOARD_ENABLED = 0—NRF_BELLBOARDundeclared (nRF54H IPC); also the source of crypticnrfx_utils.h:517errors viaNRFX_OFFSETOFNRFX_CLOCK_LFCLK_ENABLED = 0—NRFX_CLOCK_CONFIG_LF_SRCundefined; LFCLK is started bycores/nRF5/wiring.cdirectlyNRFX_IPCT_ENABLED = 0— IPCT is nRF54H DPPI cross-domain routing; the helper innrfx/helpers/internal/nrfx_gppiv1_ipct.c#errors"Platform not supported" otherwiseNRFX_VEVIF_ENABLED = 0— VPR-coprocessor-side driver; the app-core MDK hasVPR00_IRQHandlerbut not theVPRCLIC_*_IRQnvectorsnrfx_vevif.cneedsNRFX_USBD_ENABLED = NRFX_USBREG_ENABLED = 0— no USB peripheral on nRF54L
When a new driver fails to compile, add it to the disabled list in
nrfx_config.h with a one-line comment naming the error. Do not
preemptively disable drivers just because the framework doesn't link
against them — the linker eliminates unused code via --gc-sections.
The nRF54L SAADC implementation lives in
cores/nRF5/wiring_analog_nRF54L.c. nRF54L's SAADC differs from nRF52
in five ways: 0.9 V internal reference (not 0.6 V), multiplier gain
enum (Gain2_8 = 2/8, not Gain1_6 = 1/6), microsecond TACQ count
(not enum), byte-count MAXCNT (not sample count), structured PSELP
(CONNECT | PORT | PIN, not AnalogInput0..7). Don't try to merge
the two — they're genuinely different peripherals.
cores/nRF5/linker/nrf54_common.ld defines __flash_arduino_start
and __bootloader_addr so InternalFS / flash_nrf5x.c work
chip-independently. Per-chip values:
| Chip | App FLASH end | Bootloader |
|---|---|---|
| nRF54L05 | 0x47000 |
0x50000 |
| nRF54L10 | 0xC7000 |
0xD0000 |
| nRF54L15 | 0x147000 |
0x150000 |
28 KB carved off the top of FLASH for LittleFS, 8 KB gap to bootloader.
Confirmed against the bootloader repo's linker/ scripts.
The system area (SoftDevice + bootloader) sits at the top of RRAM
on nRF54L — the application starts at 0x1000 (right after the MBR)
and grows up toward the bootloader. This is the inverse of nRF52
(SoftDevice at low addresses, app grows up toward bootloader at top).
Reasoning by analogy with nRF52 layout will get linker addresses wrong.
The default Wire instance (NRF_TWIM0 → NRF_TWIM20 via compat) sits
on the SERIAL20 fabric, which on XIAO is also where SPIM lands. The
XIAO variants override WIRE_TWIM / WIRE_TWIS / WIRE_IRQN /
WIRE_IRQ_HANDLER in their variant.h to point at TWIM22 (a
dedicated TWI controller). Don't remove the override without
verifying the routing on actual hardware.
nRF54L has no USB peripheral. Uart.h defines Serial as
Serial1 so portable Arduino sketches compile. On XIAO boards
Serial1 runs through the on-board SAMD11 USB-CDC bridge.
cores/nRF5/ - core sources (Arduino API + FreeRTOS port + nrfx tree)
Arduino.h - main include
Uart.{cpp,h} - UARTE-backed HardwareSerial (Serial1 = primary)
Wire/SPI/HardwarePWM - register-direct via HAL inlines
WInterrupts.c - attachInterrupt; uses HAL nrf_gpiote_int_*
wiring_analog_nRF54L.c - SAADC
freertos/ - FreeRTOS kernel (capital-S Source/) + GRTC port
linker/ - .ld scripts (chip + common)
nordic/
nrf54l_compat.h - force-included; aliases nRF52 names → nRF54L
nrfx/ - upstream Nordic nrfx (mostly dead via guards)
drivers/src/*.c - all wrapped in #if NRFX_<X>_ENABLED
nrfx_config.h - one place to flip drivers on/off
softdevice/s145_nrf54l_9.0.0_API/ - SoftDevice headers
libraries/ - PIO LDF-discovered bundled libs
Bluefruit54Lib - BLE API on top of s145
nRF54Crypto - CRACEN + TinyCrypt for LESC (replaces CC310)
Adafruit_LittleFS - filesystem
InternalFileSytem - LittleFS-on-sd_flash for bonding
Wire/SPI/Servo/PDM/SoftwareSerial/RotaryEncoder
variants/ - per-board pin tables + linker scripts
xiao_nrf54l15(_sense)
nrf54l05dk / nrf54l10dk / nrf54l15dk
bootloader/s145/9.0.0/ - SoftDevice hex (bootloader hex lives in nRF54_Bootloader)
tests/blink/ - CI smoke test
.github/workflows/githubci.yml - PIO matrix build for all 5 variants