mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-08-29 06:28:20 +00:00
add muziworks R1 Neo
This commit is contained in:
@@ -35,6 +35,7 @@ Other benefits:
|
||||
| **SenseCAP Solar** | SX1262 | GPS (L76K), QSPI flash, battery monitor |
|
||||
| **XIAO nRF52840 + Wio-SX1262** | SX1262 | Bare XIAO + Wio-SX1262 expansion |
|
||||
| **ProMicro SX1262** | SX1262 (E22-900M30S) | GPS, battery ADC, button, LED |
|
||||
| **muzi works R1 Neo** | SX1262 | GPS, RTC, buzzer, button, LEDs, soft power-off |
|
||||
|
||||
### ESP32
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ nRF_boards=(
|
||||
heltec_t114
|
||||
heltec_t096
|
||||
gat562_30s
|
||||
muziworks_r1neo
|
||||
)
|
||||
|
||||
# Native-Linux presets (not Zephyr boards — built with -b native_sim plus an
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ nRF formatter tools
|
||||
|
||||
| SoftDevice | Boards |
|
||||
|------------|--------|
|
||||
| **v6** | RAK4631, RAK3401 1W, ThinkNode M1/M3/M6, RAK WisMesh Tag, LilyGo T-Echo, LilyGo T-Impulse Plus, ProMicro SX1262, Heltec T114, Heltec T096, GAT562 30s |
|
||||
| **v6** | RAK4631, RAK3401 1W, ThinkNode M1/M3/M6, RAK WisMesh Tag, LilyGo T-Echo, LilyGo T-Impulse Plus, ProMicro SX1262, Heltec T114, Heltec T096, GAT562 30s, muzi works R1 Neo |
|
||||
| **v7** | Wio Tracker L1, T1000-E, Ikoka Nano 30dBm, SenseCAP Solar, XIAO nRF52840 |
|
||||
|
||||
- **`.uf2`** — manual drag-and-drop onto the UF2 mass-storage drive.
|
||||
|
||||
@@ -53,6 +53,7 @@ MAKERS = {
|
||||
"uniteng": "UnitEng",
|
||||
"Ikoka": "Ikoka",
|
||||
"femtofox": "Femtofox",
|
||||
"muziworks": "muzi works",
|
||||
}
|
||||
|
||||
# Manufacturer per device name.
|
||||
@@ -70,6 +71,7 @@ MAKER_BY_DEVICE = {
|
||||
"Elecrow ThinkNode M1": "elecrow",
|
||||
"Elecrow ThinkNode M3": "elecrow",
|
||||
"Elecrow ThinkNode M6": "elecrow",
|
||||
"Elecrow ThinkNode M9": "elecrow",
|
||||
"Ikoka Nano": "Ikoka",
|
||||
"LilyGo T-Echo": "lilygo",
|
||||
"LilyGo T-Beam (SX1262)": "lilygo",
|
||||
@@ -88,6 +90,7 @@ MAKER_BY_DEVICE = {
|
||||
"Femtofox (Luckfox Pico Mini)": "femtofox",
|
||||
"RAK6421 WisMesh (Raspberry Pi)": "rak",
|
||||
"RAK6421 WisMesh (Raspberry Pi 5)": "rak",
|
||||
"muzi works R1 Neo": "muziworks",
|
||||
}
|
||||
|
||||
DESCRIPTION = (
|
||||
@@ -146,6 +149,7 @@ BOARDS = [
|
||||
dict(stem="heltec_t114", kind="nrf", device="Heltec T114",
|
||||
variant="noscreen", subtitle="No screen"),
|
||||
dict(stem="gat562_30s", kind="nrf", device="GAT-IoT GAT562 30s"),
|
||||
dict(stem="muziworks_r1neo", kind="nrf", device="muzi works R1 Neo"),
|
||||
|
||||
# --- nRF52: new ZephCore-only hardware (own tile) ---------------------
|
||||
dict(stem="lilygo_timpulse_plus", kind="nrf", device="LilyGo T-Impulse Plus", new=True, img="lora.svg"),
|
||||
@@ -185,7 +189,7 @@ SOFTDEVICE = {
|
||||
"rak4631": 6, "rak3401_1watt": 6, "thinknode_m1": 6, "thinknode_m3": 6,
|
||||
"thinknode_m6": 6, "rak_wismesh_tag": 6, "lilygo_techo": 6,
|
||||
"lilygo_timpulse_plus": 6, "promicro_sx1262": 6, "heltec_t114": 6,
|
||||
"heltec_t096": 6, "gat562_30s": 6,
|
||||
"heltec_t096": 6, "gat562_30s": 6, "muziworks_r1neo": 6,
|
||||
"wio_tracker_l1": 7, "t1000_e": 7, "ikoka_nano_30dbm": 7,
|
||||
"sensecap_solar": 7, "xiao_nrf52840": 7,
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
---
|
||||
|
||||
A maintenance release: an important **admin-password fix**, **GPS standby power savings**, and the
|
||||
removal of **Adaptive Power Control**.
|
||||
removal of **Adaptive Power Control** — plus two new boards, **muzi works R1 Neo** and an experimental
|
||||
first cut at the **ThinkNode M9**.
|
||||
|
||||
## Highlights
|
||||
|
||||
@@ -87,6 +88,38 @@ What changes for you:
|
||||
- **Your saved settings are safe.** APC's two prefs bytes are kept reserved at their original offsets, so
|
||||
no stored configuration shifts and nothing else is misread on upgrade.
|
||||
|
||||
## New boards
|
||||
|
||||
### muzi works R1 Neo
|
||||
|
||||
A pocket LoRa node — nRF52840 + SX1262 (RAK4630 stamp on a custom muzi baseboard), with GNSS, buzzer,
|
||||
battery-backed RTC and a single user button. No display, so the UI is button actions only. It reuses the
|
||||
RAK4631 flash layout (SoftDevice s140 v6.1.1, app @ `0x26000`), so the stock UF2 bootloader works as-is —
|
||||
no bootloader reflash needed. The board's soft-power design is handled in devicetree: the DCDC rail latches
|
||||
on at boot and is released on shutdown, so long-press power-off works properly.
|
||||
|
||||
Two things still want confirming against real hardware: the **battery divider multiplier** is a placeholder
|
||||
(the two upstreams disagree — check `get batt` against a multimeter), and the **GNSS module identity** is
|
||||
unconfirmed. Reports welcome.
|
||||
|
||||
### ThinkNode M9 — experimental, first testable release
|
||||
|
||||
**Consider this a preview, not a finished port.** It builds, flashes and runs, and it is worth testing if
|
||||
you have the hardware and don't mind rough edges — but several things have not been confirmed on a real
|
||||
device yet, and a few are knowingly missing.
|
||||
|
||||
It is ZephCore's first ESP32-S3 + LR1110 board, so a fair amount of it is new ground rather than a copy of
|
||||
an existing port. Currently unverified: display init parameters (inherited from another ST7789V board — if
|
||||
the screen is mirrored or upside down, that's a known one-line fix), GPS output from the CC1167Q module,
|
||||
battery calibration, and whether the USB-C port reaches the native USB pads at all (BLE is the companion
|
||||
path meanwhile). Not ported: the I2C keyboard, which is driven by a second MCU.
|
||||
|
||||
Also worth knowing on this board specifically: the **SD slot shares the radio's SPI bus**. That has no
|
||||
effect on normal operation, but it does matter if you use the LR1110 updater below.
|
||||
|
||||
Feedback from anyone with an M9 is genuinely useful here — the bring-up list in the board's README says
|
||||
exactly what to look at.
|
||||
|
||||
## Other fixes and improvements
|
||||
|
||||
- **Fixed: Heltec V3 failed to build from source** once device PM was enabled. The upstream Zephyr board
|
||||
@@ -98,6 +131,20 @@ What changes for you:
|
||||
this only bit source builds. (V4 / V4.3 use ZephCore-local board definitions and were never affected.)
|
||||
- **Air530z GPS driver patch** comments corrected to match the new PM strategy.
|
||||
|
||||
## Sidenote: LR1110 radio firmware updater
|
||||
|
||||
Not part of this release and not something you need — but if you have an LR1110 board and feel like it,
|
||||
you can now update the **radio chip's own firmware** to Semtech's current version (`0x0402`, the H1_2026
|
||||
security release). This is the LR1110's internal firmware, not ZephCore. Prebuilt tools for T1000-E and
|
||||
ThinkNode M9 are in [`LR1110_updater/`](https://github.com/liquidraver/ZephCore/tree/master/LR1110_updater)
|
||||
— flash one, watch it over serial, then flash ZephCore again afterwards.
|
||||
|
||||
Worth reading the [README](https://github.com/liquidraver/ZephCore/blob/master/LR1110_updater/README.md)
|
||||
first. Two things in particular: the chip bootloader update it performs is **one-way**, and on the M9 you
|
||||
must **take the SD card out** (it shares the radio's SPI bus and silently corrupts the write — the tool
|
||||
checks and refuses to start, but still). Boards on older firmwares work perfectly well with
|
||||
ZephCore and are fine left alone.
|
||||
|
||||
## Recommended upgrade checklist
|
||||
|
||||
1. **Set an admin password on v1.16.5?** Try the all-lowercase form if you are locked out; otherwise
|
||||
|
||||
@@ -854,6 +854,7 @@ Build strings and flash methods: `boards/supported_boards.md` and `boards/exampl
|
||||
| SenseCAP Solar | nRF52840 | SX1262 | L76K | - | QSPI, battery monitor |
|
||||
| XIAO nRF52840 + Wio-SX1262 | nRF52840 | SX1262 | - | - | - |
|
||||
| ProMicro SX1262 | nRF52840 | SX1262 (E22-900M30S) | Yes | - | Button, LED, battery ADC |
|
||||
| muzi works R1 Neo | nRF52840 | SX1262 | Yes | - | Buzzer, button, RX8130CE RTC, latched-rail power-off |
|
||||
| XIAO nRF54L15 | nRF54L15 | SX1262 | - | - | Contacts capped at 450 |
|
||||
| XIAO ESP32-C3 | ESP32-C3 | SX1262 | - | - | Contacts capped at 300 |
|
||||
| XIAO ESP32-C6 | ESP32-C6 | SX1262 | - | - | - |
|
||||
|
||||
@@ -265,7 +265,7 @@ set(ZEPHCORE_COMMON_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/zephcore_com
|
||||
# Detect platform from board name and add platform-specific common config
|
||||
# For boards with qualifiers like "wio_tracker_l1/nrf52840", check both the full BOARD
|
||||
# string and the BOARD_QUALIFIERS which contains just the qualifier (e.g., "nrf52840")
|
||||
if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wismesh_tag" OR BOARD MATCHES "rak3401_1watt" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "thinknode_m1" OR BOARD MATCHES "thinknode_m3" OR BOARD MATCHES "thinknode_m6" OR BOARD MATCHES "promicro_lr2021" OR BOARD MATCHES "promicro_sx1262" OR BOARD MATCHES "sensecap_solar" OR BOARD MATCHES "xiao_nrf52840" OR BOARD MATCHES "lilygo_techo" OR BOARD MATCHES "lilygo_timpulse_plus" OR BOARD MATCHES "heltec_t114" OR BOARD MATCHES "heltec_t096" OR BOARD MATCHES "gat562")
|
||||
if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wismesh_tag" OR BOARD MATCHES "rak3401_1watt" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "thinknode_m1" OR BOARD MATCHES "thinknode_m3" OR BOARD MATCHES "thinknode_m6" OR BOARD MATCHES "promicro_lr2021" OR BOARD MATCHES "promicro_sx1262" OR BOARD MATCHES "sensecap_solar" OR BOARD MATCHES "xiao_nrf52840" OR BOARD MATCHES "lilygo_techo" OR BOARD MATCHES "lilygo_timpulse_plus" OR BOARD MATCHES "heltec_t114" OR BOARD MATCHES "heltec_t096" OR BOARD MATCHES "gat562" OR BOARD MATCHES "muziworks_r1neo")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf")
|
||||
elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*nrf52.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf")
|
||||
|
||||
@@ -26,6 +26,7 @@ Supported Boards
|
||||
| LilyGo T-Impulse Plus | `west build -b lilygo_timpulse_plus zephcore` | UF2 drag-drop or `west flash` |
|
||||
| Heltec T114 | `west build -b heltec_t114 zephcore` | UF2 drag-drop or `west flash` |
|
||||
| Heltec Mesh Node T096 | `west build -b heltec_t096 zephcore` | UF2 drag-drop or `west flash` |
|
||||
| muzi works R1 Neo | `west build -b muziworks_r1neo zephcore` | UF2 drag-drop or `west flash` |
|
||||
|
||||
**Heltec T114 screenless:** append `boards/nrf52840/heltec_t114/no_display.conf` to `EXTRA_CONF_FILE` for units without the TFT module.
|
||||
|
||||
|
||||
@@ -849,3 +849,33 @@
|
||||
* #include "../../common/sensors-i2c.dtsi"
|
||||
* };
|
||||
*/
|
||||
|
||||
/* --- Latched supply rail / true power-off (soft-power boards) ---
|
||||
*
|
||||
* Only needed if your board has NO mechanical power switch and instead keeps
|
||||
* its own supply alive by asserting a "hold" GPIO — typically a companion I/O
|
||||
* controller that gates a DCDC regulator (see muzi works R1 Neo).
|
||||
*
|
||||
* On such a board sys_poweroff() alone is not a power-off. The SoC reaches
|
||||
* System OFF at ~1 uA, but nRF GPIO output latches persist across the
|
||||
* transition, so the hold line stays asserted and the rail keeps draining the
|
||||
* battery. Declare the pins here and ui_prepare_for_system_off() releases them
|
||||
* as its very last step, after the wake button has been observed released.
|
||||
*
|
||||
* Order is preserved and matters: downstream loads first, rail latch LAST.
|
||||
*
|
||||
* Pins are usually the same ones a gpio-hog asserts at boot. That is fine and
|
||||
* intended — a hog is init-time configuration, not a runtime reservation, so
|
||||
* the hog does the early assert and this node does the shutdown release.
|
||||
*
|
||||
* Boards without this node are entirely unaffected (the loop compiles away).
|
||||
*
|
||||
* / {
|
||||
* poweroff_gpios: poweroff-gpios {
|
||||
* compatible = "zephcore,poweroff-gpios";
|
||||
* gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>, radio module power
|
||||
* <&gpio0 29 GPIO_ACTIVE_HIGH>, I/O controller "MCU on"
|
||||
* <&gpio0 13 GPIO_ACTIVE_HIGH>; rail latch - cuts power
|
||||
* };
|
||||
* };
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Copyright (c) 2026 ZephCore
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
config BOARD_MUZIWORKS_R1NEO
|
||||
select SOC_NRF52840_QIAA
|
||||
@@ -0,0 +1,111 @@
|
||||
# muzi works R1 Neo
|
||||
|
||||
A pocket LoRa node built on the **nRF52840 + SX1262** (RAK4630 stamp module on
|
||||
a custom muzi baseboard), with GNSS, buzzer, battery-backed RTC and a single
|
||||
user button. No display.
|
||||
|
||||
The port is cross-derived from three sources, because none of them agrees with
|
||||
the others on every pin: Arduino MeshCore's
|
||||
[`variants/muziworks_r1_neo`](https://github.com/meshcore-dev/MeshCore/pull/2007),
|
||||
Meshtastic's
|
||||
[`variants/nrf52840/r1-neo`](https://github.com/meshtastic/firmware/blob/master/variants/nrf52840/r1-neo/variant.h),
|
||||
and the vendor's own [product page](https://muzi.works/products/r1-neo-complete-meshtastic-device).
|
||||
Where they conflict, the reasoning is recorded in the DTS comments and below.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# Companion (BLE — default role)
|
||||
west build -b muziworks_r1neo zephcore --pristine
|
||||
|
||||
# Repeater (USB-CDC CLI, no BLE)
|
||||
west build -b muziworks_r1neo zephcore --pristine -- \
|
||||
-DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
||||
|
||||
# Companion + debug logging
|
||||
west build -b muziworks_r1neo zephcore --pristine -- \
|
||||
-DEXTRA_CONF_FILE="boards/common/debug.conf"
|
||||
```
|
||||
|
||||
Flash: drag `build/zephyr/zephyr.uf2` onto the UF2 drive, or `west flash` with
|
||||
a J-Link. Bootloader is Adafruit UF2 / SoftDevice **s140 v6.1.1**, app @
|
||||
`0x26000` (`SD_FWID 0x00B6`) — same layout as RAK4631, so the stock bootloader
|
||||
is reused and no re-flash is needed.
|
||||
|
||||
## Hardware
|
||||
|
||||
| Function | Part | Bus / notes |
|
||||
|------------|----------------------|----------------------------------------------|
|
||||
| MCU | nRF52840 (RAK4630) | BLE5, native USB, 1 MB flash / 256 KB RAM |
|
||||
| LoRa | SX1262 | DIO2 keys the RF switch, DIO3 feeds a 1.8 V TCXO |
|
||||
| GNSS | unconfirmed (GPS/BDS)| UART0 @ 9600, power gated on P1.01 |
|
||||
| RTC | Epson RX8130CE | I2C0 @ 0x32, battery-backed |
|
||||
| Battery | ADC AIN7 (P0.31) | divider ratio unconfirmed — see below |
|
||||
| Input | 1 user button | P0.26, active-HIGH, external pull-down |
|
||||
| Buzzer | piezo | P0.03 (PWM0) |
|
||||
| LEDs | green / blue | P1.04 / P0.28, both active-HIGH |
|
||||
| Power | I/O controller + DCDC| latch P0.13, "MCU on" P0.29 |
|
||||
|
||||
No QSPI external flash — contacts and channels live in internal flash.
|
||||
|
||||
## Pin map
|
||||
|
||||
| Signal | nRF52840 | Signal | nRF52840 |
|
||||
|-----------------|----------|---------------------|----------|
|
||||
| LoRa SCK | P1.11 | LoRa CS | P1.10 |
|
||||
| LoRa MOSI | P1.12 | LoRa RST | P1.06 |
|
||||
| LoRa MISO | P1.13 | LoRa BUSY | P1.14 |
|
||||
| LoRa DIO1 | P1.15 | SX1262 power enable | P1.05 |
|
||||
| GPS MCU-TX / RX | P0.24 / P0.25 | GPS enable | P1.01 |
|
||||
| GPS PPS | P0.02 (unused) | RTC SDA / SCL | P0.19 / P0.20 |
|
||||
| Battery ADC | P0.31 | Battery charge stat | P1.02 (unused) |
|
||||
| User button | P0.26 | Buzzer | P0.03 |
|
||||
| LED green/blue | P1.04 / P0.28 | DCDC latch | P0.13 |
|
||||
| I/O ctrl "MCU on" | P0.29 | MCU_SIGNAL | P0.30 (unused) |
|
||||
|
||||
### Notes worth knowing
|
||||
|
||||
- **GPS TX/RX are swapped relative to the net names.** The schematic labels
|
||||
`UART_GPS_RX` (P0.24) and `UART_GPS_TX` (P0.25) from the *module's* point of
|
||||
view, so the MCU's receive pin is P0.25. Meshtastic maps them that way;
|
||||
Arduino MeshCore takes the labels literally and ends up listening on the
|
||||
module's own input pin. ZephCore follows Meshtastic. Getting this backwards
|
||||
is silent — the UART simply never sees a byte.
|
||||
- **Soft power, not a switch.** The rail is gated by an I/O controller that
|
||||
only holds the DCDC up while P0.13 is asserted, and P0.29 tells it the MCU
|
||||
is alive (this also enables button and LED passthrough). Both are `gpio-hog`
|
||||
at boot and released at shutdown through the `poweroff_gpios` node — without
|
||||
that release, `sys_poweroff()` would leave the rail latched, because nRF52
|
||||
GPIO output latches survive System OFF.
|
||||
- **Long-press = real power-off.** Hold the button ≥1 s: the buzzer plays the
|
||||
shutdown melody, the radio and GNSS are parked, then the latch drops and the
|
||||
board is genuinely off (not a ~1 µA System OFF doze). Press again to boot.
|
||||
- **Multi-tap actions:** 2 taps = flood advert, 3 = buzzer mute toggle,
|
||||
4 = GPS on/off, 5 = LED heartbeat toggle. A single tap maps to "page next",
|
||||
which is a no-op here since there is no display.
|
||||
- **NFC pins** (P0.09/P0.10) are freed via `nfct-pins-as-gpios`; they are NC on
|
||||
this board. Harmless, and consistent with the other RAK-based boards.
|
||||
|
||||
## To verify on real hardware
|
||||
|
||||
1. **Battery multiplier.** `vbat-mv-multiplier = 6146` is a placeholder. The
|
||||
two upstreams disagree on the divider: Arduino MeshCore's constant implies
|
||||
~1.73 (→ 6259, the RAK4631-family value), Meshtastic's variant.h states
|
||||
1.667 (→ 6031). Measure the cell with a DMM, compare against `get batt`,
|
||||
and set the value with ZephCore's convention:
|
||||
`multiplier = divider_ratio * 1.005 * 3600`.
|
||||
2. **GNSS lock.** Confirm NMEA arrives after the TX/RX correction above. If it
|
||||
does not, the next suspect is the module identity — swap
|
||||
`compatible = "luatos,air530z"` for `"gnss-nmea-generic"` plus a
|
||||
`gps-enable` alias (pattern in `heltec_t114` / `thinknode_m9`).
|
||||
3. **Power-off current.** After a long-press shutdown, the board should draw
|
||||
effectively nothing. If it still drains, the latch release order in
|
||||
`poweroff_gpios` may need adjusting.
|
||||
4. **Power-off with USB attached.** The rail may be held up externally, in
|
||||
which case the shutdown falls through to a plain System OFF instead of a
|
||||
true power-off. Expected; confirm it still wakes cleanly on button press.
|
||||
5. **Reboot.** A soft reset floats P0.13 while the SoC restarts, so the I/O
|
||||
controller may cut the rail and turn the board off instead of rebooting.
|
||||
Upstream MeshCore reports the same class of problem on this board. If it
|
||||
reproduces, it is a baseboard behaviour, not something the DTS can fix —
|
||||
document it rather than chasing it in firmware.
|
||||
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000")
|
||||
board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000")
|
||||
|
||||
include(${ZEPHYR_BASE}/boards/common/uf2.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
@@ -0,0 +1,29 @@
|
||||
# muzi works R1 Neo (nRF52840 + SX1262)
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# RAK4630 stamp module on a custom muzi baseboard. Flash layout matches
|
||||
# RAK4631 (SoftDevice s140 v6.1.1, app @ 0x26000), so the stock UF2
|
||||
# bootloader is reused — no re-flash needed.
|
||||
#
|
||||
# The baseboard is a soft-power design: a dedicated I/O controller gates the
|
||||
# DCDC rail and only keeps it up while the MCU holds P0.13 asserted. That is
|
||||
# handled entirely in devicetree — gpio-hog to latch on at boot, and a
|
||||
# zephcore,poweroff-gpios node to release at shutdown.
|
||||
#
|
||||
# Most features are auto-detected from devicetree:
|
||||
# - PWM + buzzer (buzzer nodelabel) -> UI_BUZZER
|
||||
# - user button + LEDs (gpio-keys / gpio-leds) -> UI_BUTTONS
|
||||
# - LoRa RX duty cycle (companion + SX1262)
|
||||
# - GNSS, battery ADC, RX8130CE RTC
|
||||
#
|
||||
# There is no display on this board, so ZEPHCORE_UI_DISPLAY stays off and the
|
||||
# UI reduces to the button actions listed in the DTS.
|
||||
|
||||
# Board identification (matches Arduino MeshCore variant name)
|
||||
CONFIG_ZEPHCORE_BOARD_NAME="muzi works R1 Neo"
|
||||
|
||||
# Device Information Service model name
|
||||
CONFIG_BT_DIS_MODEL_NUMBER_STR="muzi works R1 Neo"
|
||||
|
||||
# SoftDevice firmware ID (s140 v6.1.1 — same as RAK4631)
|
||||
CONFIG_ZEPHCORE_SD_FWID=0x00B6
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2026 ZephCore
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
board:
|
||||
name: muziworks_r1neo
|
||||
full_name: muzi works R1 Neo
|
||||
vendor: muziworks
|
||||
socs:
|
||||
- name: nrf52840
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* muzi works R1 Neo - Pin Control
|
||||
* Copyright (c) 2026 ZephCore
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
&pinctrl {
|
||||
/* GNSS UART.
|
||||
*
|
||||
* Direction note: the schematic net names UART_GPS_RX (P0.24) and
|
||||
* UART_GPS_TX (P0.25) are labelled from the GPS module's point of
|
||||
* view, so UART_GPS_RX is the module's input and therefore the MCU's
|
||||
* TX. Meshtastic's r1-neo variant.h maps them that way
|
||||
* (PIN_SERIAL1_RX 25 / PIN_SERIAL1_TX 24); Arduino MeshCore's variant
|
||||
* takes the net names literally and has them the other way round,
|
||||
* which leaves the MCU listening on the module's own input pin — i.e.
|
||||
* silence, and no fix, ever. Follow Meshtastic here. */
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 24)>;
|
||||
};
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 0, 25)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 24)>,
|
||||
<NRF_PSEL(UART_RX, 0, 25)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* I2C0 - RX8130CE RTC (0x32) + optional Grove-style sensors */
|
||||
i2c0_default: i2c0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 19)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 20)>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_sleep: i2c0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 19)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 20)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* PWM0 for buzzer */
|
||||
pwm0_default: pwm0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 3)>;
|
||||
};
|
||||
};
|
||||
|
||||
pwm0_sleep: pwm0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 3)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI1 for SX1262 - CS controlled manually */
|
||||
spi1_default: spi1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 1, 11)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 1, 12)>,
|
||||
<NRF_PSEL(SPIM_MISO, 1, 13)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1_sleep: spi1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 1, 11)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 1, 12)>,
|
||||
<NRF_PSEL(SPIM_MISO, 1, 13)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* muzi works R1 Neo - nRF52840 + SX1262
|
||||
* Copyright (c) 2026 ZephCore
|
||||
*
|
||||
* Hardware:
|
||||
* - RAK4630 stamp module (nRF52840 + SX1262), s140 v6.1.1 bootloader
|
||||
* - Custom baseboard with a dedicated I/O controller gating a DCDC rail:
|
||||
* the MCU latches its own supply on via P0.13 and signals "MCU is up" on
|
||||
* P0.29, which also enables button/LED passthrough from the controller.
|
||||
* - GNSS on UART0 @ 9600 (module identity unconfirmed - see below)
|
||||
* - RX8130CE battery-backed RTC @ 0x32 on I2C0
|
||||
* - Buzzer on P0.03 (PWM0), green LED P1.04, blue LED P0.28
|
||||
* - Single user button on P0.26, active HIGH, external pull-down
|
||||
* - No display, no QSPI external flash
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include <nordic/nrf52840_partition.dtsi>
|
||||
#include <zephyr/dt-bindings/lora/sx126x.h>
|
||||
#include <zephyr/dt-bindings/adc/nrf-saadc.h>
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
#include <zephyr/dt-bindings/pwm/pwm.h>
|
||||
#include "muziworks_r1neo_nrf52840-pinctrl.dtsi"
|
||||
|
||||
/* Delete default partitions from nrf52840_partition.dtsi */
|
||||
/delete-node/ &boot_partition;
|
||||
/delete-node/ &slot0_partition;
|
||||
/delete-node/ &slot1_partition;
|
||||
/delete-node/ &storage_partition;
|
||||
|
||||
/ {
|
||||
model = "muzi works R1 Neo";
|
||||
compatible = "muziworks,r1-neo";
|
||||
|
||||
chosen {
|
||||
zephyr,code-partition = &code_partition;
|
||||
zephyr,console = &cdc_acm_uart;
|
||||
zephyr,shell-uart = &cdc_acm_uart;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
led_green: led_0 {
|
||||
gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
|
||||
label = "Green LED";
|
||||
};
|
||||
led_blue: led_1 {
|
||||
gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>;
|
||||
label = "Blue LED";
|
||||
};
|
||||
};
|
||||
|
||||
buttons: buttons {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
user_button: button_0 {
|
||||
/* Active HIGH (pressed = VCC), no internal pull — the board
|
||||
* has an external pull-down and the line is passed through
|
||||
* from the I/O controller. Matches Meshtastic's
|
||||
* BUTTON_ACTIVE_LOW 0 / BUTTON_ACTIVE_PULLUP 0 /
|
||||
* BUTTON_SENSE_TYPE INPUT_SENSE_HIGH. The polarity here is
|
||||
* what ui_prepare_for_system_off() reads to pick the
|
||||
* System OFF SENSE level, so it must stay correct. */
|
||||
gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>;
|
||||
zephyr,code = <INPUT_KEY_0>;
|
||||
label = "User Button";
|
||||
};
|
||||
};
|
||||
|
||||
/* Input filter: long-press detection on the single user button.
|
||||
* Short-press → KEY_A (fed to the multi-tap filter below)
|
||||
* Long-press → KEY_F = full power-off (see poweroff_gpios). */
|
||||
user_btn_longpress {
|
||||
compatible = "zephyr,input-longpress";
|
||||
input = <&buttons>;
|
||||
input-codes = <INPUT_KEY_0>;
|
||||
short-codes = <INPUT_KEY_A>;
|
||||
long-codes = <INPUT_KEY_F>;
|
||||
long-delay-ms = <1000>;
|
||||
};
|
||||
|
||||
/* Input filter: multi-tap on the short-press output.
|
||||
* 1 tap (400ms wait) → KEY_1 = page next (no-op, board has no display)
|
||||
* 2 taps (400ms wait) → KEY_B = flood advert
|
||||
* 3 taps (400ms wait) → KEY_D = buzzer mute toggle
|
||||
* 4 taps (400ms wait) → KEY_C = GPS on/off
|
||||
* 5 taps (immediate) → KEY_E = LED heartbeat toggle */
|
||||
user_btn_multitap {
|
||||
compatible = "zephcore,input-multi-tap";
|
||||
/* No 'input' phandle — listen to ALL devices.
|
||||
* KEY_A is emitted by the longpress pseudo-device,
|
||||
* not by &buttons, so we must not filter by device. */
|
||||
input-codes = <INPUT_KEY_A>;
|
||||
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C INPUT_KEY_E>;
|
||||
tap-delay-ms = <400>;
|
||||
};
|
||||
|
||||
/* Full power-off sequence, released as the last act before
|
||||
* sys_poweroff() (helpers/ui/ui_common.c step 6).
|
||||
*
|
||||
* Without this the R1 Neo cannot actually switch off: System OFF puts
|
||||
* the SoC at ~1 uA, but nRF52 GPIO output latches persist across the
|
||||
* transition, so P0.13 would keep holding the DCDC rail up and the
|
||||
* baseboard would keep draining the cell. Order mirrors Arduino
|
||||
* MeshCore's R1NeoBoard::initiateShutdown(): drop the radio rail, tell
|
||||
* the I/O controller the MCU is going away, then release the latch. */
|
||||
poweroff_gpios: poweroff-gpios {
|
||||
compatible = "zephcore,poweroff-gpios";
|
||||
gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>, /* SX1262 module power */
|
||||
<&gpio0 29 GPIO_ACTIVE_HIGH>, /* I/O controller "MCU on" */
|
||||
<&gpio0 13 GPIO_ACTIVE_HIGH>; /* DCDC latch — cuts the rail */
|
||||
};
|
||||
|
||||
/* Battery ADC: P0.31 (AIN7).
|
||||
*
|
||||
* UNVERIFIED — needs a DMM check against a known cell voltage before
|
||||
* this is trusted. ZephCore's convention is
|
||||
* multiplier = divider_ratio * 1.005 * 3600
|
||||
* (3.6 V full scale from ADC_GAIN_1_6 + ADC_REF_INTERNAL, +0.5% for
|
||||
* nRF SAADC gain error). The divider ratio itself is disputed between
|
||||
* the two upstreams: Arduino MeshCore's constant implies ~1.73
|
||||
* (→ 6259, the RAK4631-family value), Meshtastic's r1-neo variant.h
|
||||
* states 1.667 (→ 6031). 6146 splits them and is within ~2% of either;
|
||||
* correct it once the board has been measured. */
|
||||
zephyr,user {
|
||||
io-channels = <&adc 7>;
|
||||
vbat-mv-multiplier = <6146>;
|
||||
};
|
||||
|
||||
/* Buzzer on PWM0 channel 0 */
|
||||
pwmbuzzer {
|
||||
compatible = "pwm-leds";
|
||||
buzzer: buzzer {
|
||||
pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
|
||||
};
|
||||
};
|
||||
|
||||
aliases {
|
||||
led0 = &led_green;
|
||||
led1 = &led_blue;
|
||||
sw0 = &user_button;
|
||||
watchdog0 = &wdt0;
|
||||
buzzer = &buzzer;
|
||||
lora0 = &lora;
|
||||
};
|
||||
};
|
||||
|
||||
®0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
®1 {
|
||||
regulator-initial-mode = <NRF5X_REG_MODE_DCDC>;
|
||||
};
|
||||
|
||||
&adc {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Battery voltage */
|
||||
channel@7 {
|
||||
reg = <7>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10)>;
|
||||
zephyr,input-positive = <NRF_SAADC_AIN7>;
|
||||
zephyr,resolution = <12>;
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
nfct-pins-as-gpios;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
|
||||
/* Latch the DCDC rail on. Must happen as early as possible during boot —
|
||||
* gpio-hog runs at PRE_KERNEL, before any driver. Released again at
|
||||
* shutdown via &poweroff_gpios (a hog is an init-time config, not a
|
||||
* runtime reservation, so both can reference the same pin). */
|
||||
pwr_dcdc_en {
|
||||
gpio-hog;
|
||||
gpios = <13 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
line-name = "pwr-dcdc-en";
|
||||
};
|
||||
|
||||
/* Tell the I/O controller the MCU is up. Enables passthrough of the
|
||||
* user button and the LEDs; also released at shutdown. */
|
||||
pwr_io_en {
|
||||
gpio-hog;
|
||||
gpios = <29 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
line-name = "pwr-io-en";
|
||||
};
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
|
||||
/* SX1262 module power-enable: held HIGH. Hogged rather than declared as
|
||||
* the radio's rx-enable-gpios because it is module power, not a
|
||||
* per-TX/RX antenna switch — the native sx126x driver must not toggle it
|
||||
* per direction. Dropped at shutdown via &poweroff_gpios; during normal
|
||||
* System OFF the radio is instead parked by its reset-gpios (see
|
||||
* ui_prepare_for_system_off step 4). */
|
||||
sx1262_power_en {
|
||||
gpio-hog;
|
||||
gpios = <5 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
line-name = "sx1262-power-en";
|
||||
};
|
||||
|
||||
/* GPS power-enable on P1.01 is NOT hogged here: the pin is driven by
|
||||
* the luatos,air530z driver, which owns this line via on-off-gpios
|
||||
* (see &uart0) so ZephyrGPSManager can power the receiver down. */
|
||||
};
|
||||
|
||||
/* PWM0 for buzzer */
|
||||
&pwm0 {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&pwm0_default>;
|
||||
pinctrl-1 = <&pwm0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
/* GNSS on UART0.
|
||||
*
|
||||
* Module identity is unconfirmed: muzi advertise "dual GPS/BDS", both
|
||||
* upstreams default it to 9600 baud, and muzi sell L76K modules separately —
|
||||
* all consistent with a CASIC part, but nobody has read a part number off the
|
||||
* PCB. luatos,air530z is safe either way: its init script is
|
||||
* MODEM_CHAT_SCRIPT_CMD_RESP_NONE under MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE, so
|
||||
* the CASIC $PCAS03/$PMTK869 sentences are fire-and-forget and a non-CASIC
|
||||
* module simply ignores them and streams plain NMEA. It also gives us
|
||||
* on-off-gpios power gating for free. If a teardown ever shows a non-CASIC
|
||||
* module and the vendor sentences prove harmful, fall back to
|
||||
* gnss-nmea-generic plus a gps-enable alias (see heltec_t114 / thinknode_m9).
|
||||
*
|
||||
* See the pinctrl dtsi for why TX/RX are P0.24/P0.25 and not the reverse. */
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
status = "okay";
|
||||
current-speed = <9600>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-1 = <&uart0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
gnss: gnss {
|
||||
compatible = "luatos,air530z";
|
||||
on-off-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
/* I2C0 */
|
||||
&i2c0 {
|
||||
compatible = "nordic,nrf-twim";
|
||||
status = "okay";
|
||||
clock-frequency = <I2C_BITRATE_FAST>;
|
||||
pinctrl-0 = <&i2c0_default>;
|
||||
pinctrl-1 = <&i2c0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
/* All supported environment & power sensors — auto-detected at runtime */
|
||||
#include "../../common/sensors-i2c.dtsi"
|
||||
/* Battery-backed RX8130CE @ 0x32 — opt in to boot-time time restore
|
||||
* (auto-detected at runtime among the supported chips). */
|
||||
#include "../../common/rtc-i2c.dtsi"
|
||||
};
|
||||
|
||||
/* SPI1 for SX1262 */
|
||||
&spi1 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
|
||||
pinctrl-0 = <&spi1_default>;
|
||||
pinctrl-1 = <&spi1_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1262";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <8000000>;
|
||||
|
||||
/* LoRa control pins. reset-gpios is wired and used — Meshtastic
|
||||
* drives P1.06 as SX126X_RESET; Arduino MeshCore's RADIOLIB_NC is
|
||||
* the outlier. ZephCore needs it: ui_prepare_for_system_off()
|
||||
* parks the radio in HW reset before System OFF. */
|
||||
reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
|
||||
busy-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
|
||||
dio1-gpios = <&gpio1 15 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
|
||||
|
||||
/* DIO2 drives RF switch for TX */
|
||||
dio2-tx-enable;
|
||||
|
||||
/* TCXO voltage 1.8V via DIO3 */
|
||||
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
|
||||
tcxo-power-startup-delay-ms = <10>;
|
||||
|
||||
/* RX boosted mode for better sensitivity */
|
||||
rx-boosted;
|
||||
};
|
||||
};
|
||||
|
||||
zephyr_udc0: &usbd {
|
||||
compatible = "nordic,nrf-usbd";
|
||||
status = "okay";
|
||||
|
||||
cdc_acm_uart: cdc_acm_uart {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
|
||||
/* Arduino MeshCore compatible partition layout (SoftDevice v6) */
|
||||
#include "../../common/nrf52_partitions_sdv6.dtsi"
|
||||
#include "../../common/nrf52_wakeup.dtsi"
|
||||
@@ -0,0 +1,22 @@
|
||||
identifier: muziworks_r1neo
|
||||
name: muzi works R1 Neo
|
||||
type: mcu
|
||||
arch: arm
|
||||
ram: 256
|
||||
flash: 1024
|
||||
toolchain:
|
||||
- zephyr
|
||||
- gnuarmemb
|
||||
- xtools
|
||||
supported:
|
||||
- adc
|
||||
- ble
|
||||
- gpio
|
||||
- gnss
|
||||
- i2c
|
||||
- nvs
|
||||
- pwm
|
||||
- spi
|
||||
- usb_device
|
||||
- watchdog
|
||||
vendor: muziworks
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2026 ZephCore
|
||||
# SPDX-License-Identifier: MIT
|
||||
# muzi works R1 Neo default configuration
|
||||
# (most subsystems also come from zephcore_common.conf + nrf52_common.conf;
|
||||
# these are the board essentials, kept self-contained like other custom boards)
|
||||
|
||||
# MPU + hardware stack protection
|
||||
CONFIG_ARM_MPU=y
|
||||
CONFIG_HW_STACK_PROTECTION=y
|
||||
|
||||
# GPIO + pin control
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_PINCTRL=y
|
||||
|
||||
# Console over USB CDC-ACM (DT node + nrf52_common.conf USB stack)
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# Flash / NVS (LittleFS app data partition)
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_NVS=y
|
||||
|
||||
# SPI for LoRa, ADC for battery sense
|
||||
CONFIG_SPI=y
|
||||
CONFIG_ADC=y
|
||||
|
||||
# Build UF2 by default
|
||||
CONFIG_BUILD_OUTPUT_UF2=y
|
||||
CONFIG_USE_DT_CODE_PARTITION=y
|
||||
@@ -21,6 +21,7 @@ promicro_sx1262
|
||||
heltec_t114
|
||||
heltec_t096
|
||||
gat562_30s
|
||||
muziworks_r1neo
|
||||
```
|
||||
|
||||
> **RAK WisMesh Pocket** (WisBlock pocket): use `-b rak4631` — same board string and firmware as **RAK4631**.
|
||||
|
||||
@@ -11,7 +11,7 @@ description: |
|
||||
The tap-codes array maps tap counts to output key codes:
|
||||
tap-codes[0] = code emitted for 1 tap
|
||||
tap-codes[1] = code emitted for 2 taps
|
||||
...up to 4 entries.
|
||||
...up to 5 entries.
|
||||
|
||||
If the tap count reaches the length of tap-codes, the code is emitted
|
||||
immediately (no need to wait for more taps). Otherwise the tap-delay-ms
|
||||
@@ -47,7 +47,7 @@ properties:
|
||||
description: |
|
||||
Array of output key codes for each tap count (1-based).
|
||||
Entry 0 = 1 tap, entry 1 = 2 taps, etc.
|
||||
Maximum 4 entries.
|
||||
Maximum 5 entries.
|
||||
|
||||
tap-delay-ms:
|
||||
type: int
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright 2026 ZephCore
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: |
|
||||
Board power-latch release list.
|
||||
|
||||
Lists GPIOs that must be driven to their INACTIVE level as the very last
|
||||
step before sys_poweroff(), after every other peripheral has been shut down
|
||||
and after the sw0 wake button has been released.
|
||||
|
||||
This exists for boards whose supply rail is latched on by the MCU rather
|
||||
than by a mechanical switch — typically a soft-power design where a
|
||||
companion I/O controller holds the regulator enabled only while the MCU
|
||||
keeps a "hold" line asserted. On such boards sys_poweroff() alone is not a
|
||||
real power-off: the SoC enters System OFF at ~1 uA, but nRF GPIO output
|
||||
latches persist across the transition, so the hold line stays asserted and
|
||||
the rail (plus whatever else it feeds) keeps draining the battery.
|
||||
|
||||
Order matters and is preserved: list downstream loads first and the rail
|
||||
latch itself last, so the board is quiesced before the supply is cut.
|
||||
|
||||
Boards that do not define a node with this compatible are completely
|
||||
unaffected — the release loop compiles to nothing.
|
||||
|
||||
Note that these are usually the same pins a board asserts at boot via
|
||||
gpio-hog. The hog performs the early boot assert (PRE_KERNEL, before any
|
||||
driver runs); this node performs the shutdown release. Referencing a
|
||||
hogged pin here is intentional and safe — a gpio-hog is an init-time
|
||||
configuration, not a runtime reservation.
|
||||
|
||||
Example (muzi works R1 Neo — I/O controller gated DCDC rail):
|
||||
poweroff_gpios: poweroff-gpios {
|
||||
compatible = "zephcore,poweroff-gpios";
|
||||
gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>, /* SX1262 module power */
|
||||
<&gpio0 29 GPIO_ACTIVE_HIGH>, /* I/O controller "MCU on" */
|
||||
<&gpio0 13 GPIO_ACTIVE_HIGH>; /* DCDC latch - cuts the rail */
|
||||
};
|
||||
|
||||
compatible: "zephcore,poweroff-gpios"
|
||||
|
||||
properties:
|
||||
gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
GPIOs to drive INACTIVE (per each entry's own GPIO_ACTIVE_* flag)
|
||||
immediately before sys_poweroff(). Listed in shutdown order:
|
||||
downstream loads first, supply latch last.
|
||||
@@ -291,6 +291,11 @@ void ui_invalidate_battery_cache(void)
|
||||
*
|
||||
* Non-nRF platforms skip the SENSE block; they rely on Zephyr's wakeup-source
|
||||
* DT property which is honoured by their respective GPIO drivers.
|
||||
*
|
||||
* Boards with a soft-power rail (the MCU latches its own supply on) add a
|
||||
* "zephcore,poweroff-gpios" node; its pins are released last, which cuts the
|
||||
* rail outright instead of leaving it latched through System OFF. Boards
|
||||
* without that node are unaffected — step 6 compiles to nothing.
|
||||
*/
|
||||
void ui_prepare_for_system_off(void)
|
||||
{
|
||||
@@ -353,6 +358,34 @@ void ui_prepare_for_system_off(void)
|
||||
#undef _SW0_FLAGS
|
||||
}
|
||||
#endif /* CONFIG_SOC_FAMILY_NORDIC_NRF && sw0 */
|
||||
|
||||
/* 6. Release the board power latch — must be dead last.
|
||||
*
|
||||
* Only present on soft-power boards (see zephcore,poweroff-gpios). The
|
||||
* rail is cut here rather than in step 3 because the pins are ordered
|
||||
* loads-first/latch-last, and because step 5 must have observed the
|
||||
* button release first: on these boards the button is also the power-on
|
||||
* input, so dropping the latch while it is still held would let the I/O
|
||||
* controller re-latch the rail immediately.
|
||||
*
|
||||
* On battery this does not return — the supply is gone mid-loop, which
|
||||
* is the intended outcome. On USB the rail may be held up externally, in
|
||||
* which case we simply fall through to the caller's sys_poweroff() and
|
||||
* land in System OFF as before. */
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(zephcore_poweroff_gpios)
|
||||
{
|
||||
#define _PWROFF_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephcore_poweroff_gpios)
|
||||
static const struct gpio_dt_spec poweroff_gpios[] = {
|
||||
DT_FOREACH_PROP_ELEM_SEP(_PWROFF_NODE, gpios,
|
||||
GPIO_DT_SPEC_GET_BY_IDX, (,))
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(poweroff_gpios); i++) {
|
||||
gpio_pin_configure_dt(&poweroff_gpios[i], GPIO_OUTPUT_INACTIVE);
|
||||
}
|
||||
#undef _PWROFF_NODE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ========== Low-battery auto-shutdown ==========
|
||||
|
||||
Reference in New Issue
Block a user