add thinknode m1 board + tcxo fixes

This commit is contained in:
liquidraver committed 2026-03-01 16:20:17 +01:00
1 parent b62214ad3e
commit 7081e19b3d
19 files changed
+659 -22

No files matched your search

+1 -1
View File
@@ -171,7 +171,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 "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e")
if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "thinknode_m1")
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")
@@ -12,6 +12,8 @@
* Console: USB-Serial/JTAG (usb_serial)
*/
#include <zephyr/dt-bindings/lora/sx126x.h>
/ {
aliases {
lora0 = &lora;
@@ -51,7 +53,7 @@
dio2-tx-enable;
/* TCXO 1.8V via DIO3 */
dio3-tcxo-voltage = <2>;
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
tcxo-power-startup-delay-ms = <10>;
/* RX boosted mode for better sensitivity */
@@ -12,6 +12,8 @@
* Button: GPIO38
*/
#include <zephyr/dt-bindings/lora/sx126x.h>
/ {
aliases {
lora0 = &lora;
@@ -49,8 +51,8 @@
/* DIO2 controls RF switch for TX */
dio2-tx-enable;
/* TCXO 1.8 V via DIO3 (value 2 = 1.8 V in Zephyr SX126x driver) */
dio3-tcxo-voltage = <2>;
/* TCXO 1.8V via DIO3 */
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
tcxo-power-startup-delay-ms = <10>;
rx-boosted;
@@ -13,6 +13,8 @@
* Console: USB-Serial/JTAG (usb_serial)
*/
#include <zephyr/dt-bindings/lora/sx126x.h>
/ {
aliases {
lora0 = &lora;
@@ -43,8 +45,8 @@
rx-enable-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; /* D5 = GPIO7 RXEN */
dio2-tx-enable;
/* TCXO 1.8V via DIO3 (SX126X_DIO3_TCXO_1V8 = 0x02) */
dio3-tcxo-voltage = <2>;
/* TCXO 1.8V via DIO3 */
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
tcxo-power-startup-delay-ms = <10>;
/* RX boosted mode for better sensitivity */
@@ -13,6 +13,8 @@
* Console: USB-Serial/JTAG (usb_serial)
*/
#include <zephyr/dt-bindings/lora/sx126x.h>
/ {
aliases {
lora0 = &lora;
@@ -43,8 +45,8 @@
rx-enable-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>; /* D5 = GPIO23 RXEN */
dio2-tx-enable;
/* TCXO 1.8V via DIO3 (SX126X_DIO3_TCXO_1V8 = 0x02) */
dio3-tcxo-voltage = <2>;
/* TCXO 1.8V via DIO3 */
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
tcxo-power-startup-delay-ms = <10>;
/* RX boosted mode for better sensitivity */
+5 -7
View File
@@ -150,10 +150,8 @@
* rx-enable-gpios — Separate RXEN pin (Wio-SX1262 needs both)
*
* TCXO (if your module has one):
* dio3-tcxo-voltage — DIO3 provides TCXO power
* Values: 0=1.6V, 1=1.7V, 2=1.8V, 3=2.2V, 4=2.4V, 5=2.7V, 6=3.0V, 7=3.3V
* Or use named constants from sx126x.h:
* SX126X_DIO3_TCXO_1V6 .. SX126X_DIO3_TCXO_3V3
* dio3-tcxo-voltage — DIO3 provides TCXO power (use 1.8V)
* Use: SX126X_DIO3_TCXO_1V8 (from sx126x.h)
*
* rx-boosted — Better RX sensitivity (slight power increase)
*
@@ -205,7 +203,7 @@
* dio1-gpios = <&gpio1 5 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
* rx-enable-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>;
* dio2-tx-enable;
* dio3-tcxo-voltage = <6>;
* dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
* tcxo-power-startup-delay-ms = <10>;
* rx-boosted;
* };
@@ -231,7 +229,7 @@
* dio1-gpios = <&gpioc 1 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
* rx-enable-gpios = <&gpioc 5 GPIO_ACTIVE_HIGH>;
* dio2-tx-enable;
* dio3-tcxo-voltage = <6>;
* dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
* tcxo-power-startup-delay-ms = <10>;
* rx-boosted;
* };
@@ -255,7 +253,7 @@
* dio1-gpios = <&gpio0 3 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
* rx-enable-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
* dio2-tx-enable;
* dio3-tcxo-voltage = <6>;
* dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
* tcxo-power-startup-delay-ms = <10>;
* rx-boosted;
* };
+4 -2
View File
@@ -14,6 +14,8 @@
* WARNING: Default I2C0 uses PC4/PC5 (D4/D5) — must be disabled for LoRa!
*/
#include <zephyr/dt-bindings/lora/sx126x.h>
/ {
aliases {
lora0 = &lora;
@@ -48,8 +50,8 @@
rx-enable-gpios = <&gpioc 5 GPIO_ACTIVE_HIGH>; /* D5 = PC05 RXEN */
dio2-tx-enable;
/* TCXO 1.8V via DIO3 (SX126X_DIO3_TCXO_1V8 = 0x02) */
dio3-tcxo-voltage = <2>;
/* TCXO 1.8V via DIO3 */
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
tcxo-power-startup-delay-ms = <10>;
/* RX boosted mode for better sensitivity */
@@ -22,6 +22,7 @@
#include <nordic/nrf52840_qiaa.dtsi>
#include "ikoka_nano_30dbm-pinctrl.dtsi"
#include <zephyr/dt-bindings/adc/nrf-saadc.h>
#include <zephyr/dt-bindings/lora/sx126x.h>
/ {
model = "Ikoka Nano 30dBm";
@@ -152,7 +153,7 @@
dio2-tx-enable;
/* TCXO voltage 1.8V */
dio3-tcxo-voltage = <6>;
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
tcxo-power-startup-delay-ms = <10>;
/* Enable RX boosted mode for better sensitivity */
@@ -0,0 +1,6 @@
# Elecrow ThinkNode M1 board configuration
# Copyright (c) 2025 ZephCore
# SPDX-License-Identifier: Apache-2.0
config BOARD_THINKNODE_M1
select SOC_NRF52840_QIAA
@@ -0,0 +1,13 @@
# Copyright (c) 2025 ZephCore
# SPDX-License-Identifier: Apache-2.0
board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000")
board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000")
set(OPENOCD_NRF5_SUBFAMILY "nrf52")
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake)
@@ -0,0 +1,28 @@
# Elecrow ThinkNode M1 (nRF52840 + SX1262 + SSD1681 EPD)
# Board-specific configuration
#
# Hardware:
# - nRF52840 SoC, SX1262 LoRa (22dBm), 1.54" SSD1681 e-paper (200x200)
# - GPS module on UART0, MX25R1635F 2MB QSPI flash
# - Battery ADC on AIN2 (P0.04), 150K+150K divider
# - Buzzer on P0.06, two buttons, GPS hardware switch
# - LEDs: GREEN=P1.04, BLUE=P0.14
# Board identification (matches Arduino MeshCore variant name)
CONFIG_ZEPHCORE_BOARD_NAME="ThinkNode M1"
# Device Information Service model name
CONFIG_BT_DIS_MODEL_NUMBER_STR="Elecrow ThinkNode-M1"
# SoftDevice firmware ID (ThinkNode M1 bootloader uses S140 v6)
CONFIG_ZEPHCORE_SD_FWID=0x00B6
# QSPI flash present
CONFIG_NORDIC_QSPI_NOR=y
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
# Contacts on QSPI (2MB) — override nrf52_common.conf limit
CONFIG_ZEPHCORE_MAX_CONTACTS=510
# PWM for buzzer
CONFIG_PWM=y
@@ -0,0 +1,9 @@
# Copyright (c) 2025 ZephCore
# SPDX-License-Identifier: Apache-2.0
board:
name: thinknode_m1
full_name: Elecrow ThinkNode M1 (nRF52840 + SX1262 + SSD1681 EPD)
vendor: elecrow
socs:
- name: nrf52840
@@ -0,0 +1,134 @@
/*
* Elecrow ThinkNode M1 pin control definitions
* Copyright (c) 2025 ZephCore
*
* SPDX-License-Identifier: Apache-2.0
*
* Pin mappings (from Arduino MeshCore variant.h):
*
* SPI2 (LoRa SX1262): SCK=P0.19, MISO=P0.23, MOSI=P0.22, CS=P0.24
* SPI3 (EPD SSD1681): SCK=P0.31, MISO=P1.06, MOSI=P0.29
* (MISO shared with LED_RED — unused by write-only EPD)
* I2C0 (RTC/sensors): SDA=P0.26, SCL=P0.27
* UART0 (GPS 9600): TX=P1.08 (→GPS RX), RX=P1.09 (←GPS TX)
* PWM0 (buzzer): P0.06
* QSPI (MX25R1635F): SCK=P1.14, CS=P1.15, IO0=P1.12, IO1=P1.13,
* IO2=P0.07, IO3=P0.05
*/
&pinctrl {
/* UART0 for GPS module at 9600 baud */
uart0_default: uart0_default {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 8)>; /* P1.08 → GPS RX */
};
group2 {
psels = <NRF_PSEL(UART_RX, 1, 9)>; /* P1.09 ← GPS TX */
bias-pull-up;
};
};
uart0_sleep: uart0_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 8)>,
<NRF_PSEL(UART_RX, 1, 9)>;
low-power-enable;
};
};
/* I2C0 for external sensors / RTC */
i2c0_default: i2c0_default {
group1 {
psels = <NRF_PSEL(TWIM_SDA, 0, 26)>, /* P0.26 */
<NRF_PSEL(TWIM_SCL, 0, 27)>; /* P0.27 */
};
};
i2c0_sleep: i2c0_sleep {
group1 {
psels = <NRF_PSEL(TWIM_SDA, 0, 26)>,
<NRF_PSEL(TWIM_SCL, 0, 27)>;
low-power-enable;
};
};
/* SPI2 for LoRa SX1262 */
spi2_default: spi2_default {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 19)>, /* P0.19 */
<NRF_PSEL(SPIM_MOSI, 0, 22)>, /* P0.22 */
<NRF_PSEL(SPIM_MISO, 0, 23)>; /* P0.23 */
};
};
spi2_sleep: spi2_sleep {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 19)>,
<NRF_PSEL(SPIM_MOSI, 0, 22)>,
<NRF_PSEL(SPIM_MISO, 0, 23)>;
low-power-enable;
};
};
/* SPI3 for E-Paper display SSD1681
* MISO on P1.06 is shared with LED_RED — e-ink is write-only so
* MISO is never driven, but nRF SPIM requires all three pins. */
spi3_default: spi3_default {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 31)>, /* P0.31 */
<NRF_PSEL(SPIM_MOSI, 0, 29)>, /* P0.29 */
<NRF_PSEL(SPIM_MISO, 1, 6)>; /* P1.06 (shared LED_RED) */
};
};
spi3_sleep: spi3_sleep {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 31)>,
<NRF_PSEL(SPIM_MOSI, 0, 29)>,
<NRF_PSEL(SPIM_MISO, 1, 6)>;
low-power-enable;
};
};
/* PWM0 for buzzer on P0.06 */
pwm0_default: pwm0_default {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 6)>;
};
};
pwm0_sleep: pwm0_sleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 6)>;
low-power-enable;
};
};
/* QSPI for MX25R1635F 2MB external flash */
qspi_default: qspi_default {
group1 {
psels = <NRF_PSEL(QSPI_SCK, 1, 14)>,
<NRF_PSEL(QSPI_IO0, 1, 12)>,
<NRF_PSEL(QSPI_IO1, 1, 13)>,
<NRF_PSEL(QSPI_IO2, 0, 7)>,
<NRF_PSEL(QSPI_IO3, 0, 5)>,
<NRF_PSEL(QSPI_CSN, 1, 15)>;
};
};
qspi_sleep: qspi_sleep {
group1 {
psels = <NRF_PSEL(QSPI_SCK, 1, 14)>,
<NRF_PSEL(QSPI_IO0, 1, 12)>,
<NRF_PSEL(QSPI_IO1, 1, 13)>,
<NRF_PSEL(QSPI_IO2, 0, 7)>,
<NRF_PSEL(QSPI_IO3, 0, 5)>;
low-power-enable;
};
group2 {
psels = <NRF_PSEL(QSPI_CSN, 1, 15)>;
low-power-enable;
bias-pull-up;
};
};
};
@@ -0,0 +1,370 @@
/*
* Elecrow ThinkNode M1 — nRF52840 + SX1262 + SSD1681 1.54" EPD
* Copyright (c) 2025 ZephCore
*
* SPDX-License-Identifier: Apache-2.0
*
* Hardware:
* - nRF52840 SoC with BLE 5
* - SX1262 LoRa radio (DIO2 as RF switch, external TCXO 1.8V)
* - SSD1681 1.54" 200x200 monochrome e-paper display (SPI)
* - MX25R1635F 2MB QSPI flash
* - GPS module on UART0 (9600 baud, multi-constellation)
* - 1200 mAh battery with ADC on AIN2 (150K+150K divider)
* - Buzzer on PWM0, two user buttons, GPS hardware switch
* - LEDs: GREEN=P1.04, BLUE=P0.14 (RED=P1.06 shared with display SPI)
*
* Pin conflicts (shared by hardware design):
* - P1.06: LED_RED + display SPI MISO (EPD is write-only, MISO unused)
* - P1.05: LoRa power enable + GPS reset (shared power rail)
* - P1.04: LED_GREEN + GPS PPS (GPS PPS blinks the green LED)
*/
/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include "thinknode_m1-pinctrl.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>
/ {
model = "Elecrow ThinkNode M1";
compatible = "elecrow,thinknode-m1";
chosen {
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,code-partition = &code_partition;
zephyr,console = &cdc_acm_uart;
zephyr,shell-uart = &cdc_acm_uart;
zephyr,display = &epd;
};
aliases {
lora0 = &lora;
led0 = &led_green;
led1 = &led_blue;
sw0 = &user_button;
watchdog0 = &wdt0;
buzzer = &buzzer;
gps-enable = &gps_enable_pin;
};
/* ---- LEDs (active-low) ---- */
leds {
compatible = "gpio-leds";
led_green: led_0 {
gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
label = "Green LED";
};
led_blue: led_1 {
gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
label = "Blue LED";
};
/* LED_RED (P1.06) omitted — shared with display SPI MISO */
};
/* ---- Buttons ---- */
buttons: buttons {
compatible = "gpio-keys";
user_button: button_0 {
gpios = <&gpio1 10 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
zephyr,code = <INPUT_KEY_0>;
label = "User Button";
};
page_button: button_1 {
gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
zephyr,code = <INPUT_KEY_1>;
label = "Page Button";
};
};
/* Input filter: user button long-press (1000ms) */
user_btn_longpress {
compatible = "zephyr,input-longpress";
input = <&buttons>;
input-codes = <INPUT_KEY_0>;
short-codes = <INPUT_KEY_A>;
long-codes = <INPUT_KEY_POWER>;
long-delay-ms = <1000>;
};
/* Input filter: user button multi-tap
* 1 tap → KEY_1 = next page on EPD
* 2 taps → KEY_B = flood advert
* 3 taps → KEY_D = buzzer mute toggle
* 4 taps → KEY_C = GPS on/off */
user_btn_multitap {
compatible = "zephcore,input-multi-tap";
input-codes = <INPUT_KEY_A>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C>;
tap-delay-ms = <400>;
};
/* ---- Power control ---- */
/* Board power enable — must be HIGH for peripherals to function */
pwr_enable: pwr-enable {
compatible = "regulator-fixed";
regulator-name = "pwr-enable";
enable-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
};
/* LoRa radio power enable — must be HIGH before SX1262 can communicate */
lora_pwr_enable: lora-pwr-enable {
compatible = "regulator-fixed";
regulator-name = "lora-pwr-enable";
enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
startup-delay-us = <10000>;
};
/* External TCXO power enable — SX1262 DIO3 provides reference voltage,
* but the TCXO itself is powered through this separate GPIO */
tcxo_enable: tcxo-enable {
compatible = "regulator-fixed";
regulator-name = "tcxo-enable";
enable-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
};
/* ---- GPS hardware switch (P1.01) ----
* Physical toggle switch on the board.
* HIGH = GPS ON, LOW = GPS OFF.
* Separate from the buttons node so it doesn't feed
* the longpress/multi-tap filter chain. */
gps_switch: gps-switch {
compatible = "gpio-keys";
gps_sw: gps_sw {
gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
zephyr,code = <INPUT_KEY_G>;
label = "GPS Switch";
};
};
/* GPS power control */
gps_en: gps-enable {
compatible = "gpio-leds";
gps_enable_pin: gps_enable {
gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
label = "GPS Enable";
};
};
/* ---- Battery ADC ---- */
/* 150K+150K voltage divider (2:1) on AIN2 (P0.04) */
zephyr,user {
io-channels = <&adc 2>;
vbat-mv-multiplier = <7200>;
};
/* ---- Buzzer on PWM0 ---- */
pwmbuzzer {
compatible = "pwm-leds";
buzzer: buzzer {
pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
};
};
/* ---- E-Paper display (MIPI DBI SPI) ----
* SSD1681 1.54" 200x200 monochrome EPD on SPI3.
* The MIPI DBI node wraps the SPI bus with DC and RST GPIOs.
* Display is auto-detected by ZephCore display.c via the
* zephyr,display chosen node — EPD flag set automatically. */
mipi_dbi {
compatible = "zephyr,mipi-dbi-spi";
spi-dev = <&spi3>;
dc-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
#address-cells = <1>;
#size-cells = <0>;
epd: ssd16xxfb@0 {
compatible = "solomon,ssd1681";
mipi-max-frequency = <4000000>;
reg = <0>;
width = <200>;
height = <200>;
busy-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
};
};
};
/* ---- SoC configuration ---- */
&reg0 {
status = "okay";
};
&reg1 {
regulator-initial-mode = <NRF5X_REG_MODE_DCDC>;
};
&adc {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
/* Battery voltage on P0.04 (AIN2) */
channel@2 {
reg = <2>;
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_AIN2>;
zephyr,resolution = <12>;
};
};
&uicr {
nfct-pins-as-gpios;
};
&gpiote {
status = "okay";
};
&gpio0 {
status = "okay";
};
&gpio1 {
status = "okay";
};
/* ---- PWM0 for buzzer ---- */
&pwm0 {
status = "okay";
pinctrl-0 = <&pwm0_default>;
pinctrl-1 = <&pwm0_sleep>;
pinctrl-names = "default", "sleep";
};
/* ---- GPS on UART0 (9600 baud) ----
* Uses luatos,air530z driver (PCAS/NMEA protocol).
* GPS power controlled via on-off-gpios (P1.02 = GPS_EN). */
&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 2 GPIO_ACTIVE_HIGH>;
};
};
/* ---- I2C0 for RTC and external sensors ---- */
&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"
};
/* ---- SPI2 for LoRa SX1262 ---- */
&spi2 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi2_default>;
pinctrl-1 = <&spi2_sleep>;
pinctrl-names = "default", "sleep";
cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; /* P0.24 LORA_CS */
lora: lora@0 {
compatible = "semtech,sx1262";
reg = <0>;
spi-max-frequency = <8000000>;
/* LoRa control pins */
reset-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>; /* P0.25 */
busy-gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>; /* P0.17 */
dio1-gpios = <&gpio0 20 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; /* P0.20 */
/* 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;
};
};
/* ---- SPI3 for E-Paper display ---- */
&spi3 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi3_default>;
pinctrl-1 = <&spi3_sleep>;
pinctrl-names = "default", "sleep";
cs-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; /* P0.30 DISP_CS */
};
/* ---- QSPI Flash — MX25R1635F 2MB ---- */
&qspi {
status = "okay";
pinctrl-0 = <&qspi_default>;
pinctrl-1 = <&qspi_sleep>;
pinctrl-names = "default", "sleep";
qspi_flash: qspi_flash@0 {
compatible = "nordic,qspi-nor";
reg = <0>;
writeoc = "pp";
readoc = "fastread";
sck-frequency = <16000000>;
jedec-id = [c2 28 15]; /* MX25R1635F */
size = <16777216>; /* 2MB = 16Mbit */
has-dpd;
t-enter-dpd = <10000>;
t-exit-dpd = <35000>;
};
};
/* ---- USB CDC for console/CLI ---- */
zephyr_udc0: &usbd {
compatible = "nordic,nrf-usbd";
status = "okay";
cdc_acm_uart: cdc_acm_uart {
compatible = "zephyr,cdc-acm-uart";
};
};
/* ---- Flash partitions (SoftDevice v6, app@0x26000) ---- */
#include "../../common/nrf52_partitions_sdv6.dtsi"
/* ---- External QSPI flash partitions ---- */
&qspi_flash {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Full 2MB for LittleFS storage */
qspi_storage_partition: partition@0 {
label = "qspi_storage";
reg = <0x00000000 0x00200000>;
};
};
};
/* ---- External QSPI /ext automount ---- */
#include "../../common/qspi-ext.dtsi"
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: Apache-2.0
# Elecrow ThinkNode M1 default configuration
# Enable MPU
CONFIG_ARM_MPU=y
CONFIG_HW_STACK_PROTECTION=y
# Enable GPIO
CONFIG_GPIO=y
# Enable console on USB CDC
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART_LINE_CTRL=y
# Enable flash/NVS storage
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
# Pinctrl
CONFIG_PINCTRL=y
# SPI for LoRa + EPD display
CONFIG_SPI=y
# ADC for battery
CONFIG_ADC=y
@@ -14,6 +14,8 @@
* Flash via: SWD through SAMD11 CMSIS-DAP (west flash / openocd)
*/
#include <zephyr/dt-bindings/lora/sx126x.h>
/ {
aliases {
lora0 = &lora;
@@ -47,8 +49,8 @@
rx-enable-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; /* D5 = P1.11 RXEN */
dio2-tx-enable;
/* TCXO 1.8V via DIO3 (SX126X_DIO3_TCXO_1V8 = 0x02) */
dio3-tcxo-voltage = <2>;
/* TCXO 1.8V via DIO3 */
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_1V8>;
tcxo-power-startup-delay-ms = <10>;
/* RX boosted mode for better sensitivity */
+10
View File
@@ -50,6 +50,11 @@ static void auto_off_handler(struct k_work *work)
if (doom_game_is_running()) {
return;
}
/* E-paper uses zero power when static — blanking wastes a full
* refresh cycle (~2s) and leaves the screen blank for no benefit. */
if (is_epd) {
return;
}
if (disp_on) {
mc_display_off();
}
@@ -227,6 +232,11 @@ bool mc_display_is_on(void)
return disp_on;
}
bool mc_display_is_epd(void)
{
return is_epd;
}
void mc_display_clear(void)
{
if (!disp_initialized) {
+7
View File
@@ -75,6 +75,13 @@ void mc_display_off(void);
*/
bool mc_display_is_on(void);
/**
* @return true if the display is an e-paper (EPD) type.
* EPD displays have slow refresh (~2s) and use zero power when static,
* so callers should use longer update intervals and skip blanking.
*/
bool mc_display_is_epd(void);
/**
* Clear the framebuffer (fill with black).
* Call before rendering a new frame.
+22 -2
View File
@@ -11,6 +11,7 @@
* KEY_B → action_flood_advert() (2 taps, 400ms delayed)
* KEY_D → action_buzzer_toggle() (3 taps, 400ms delayed)
* KEY_C → action_gps_toggle() (4 taps, immediate)
* KEY_G → GPS switch on/off (hardware toggle, ThinkNode M1)
* KEY_POWER → action_deep_sleep() (long press ≥1s)
* KEY_RIGHT → action_page_next() (joystick, Wio Tracker)
* KEY_LEFT → action_page_prev() (joystick, Wio Tracker)
@@ -109,7 +110,8 @@ static struct ui_state local_ui_state;
/* ========== Constants ========== */
#define SPLASH_DURATION_MS 3000
#define RENDER_DEBOUNCE_MS 50
#define RENDER_DEBOUNCE_MS 50
#define RENDER_DEBOUNCE_EPD_MS 2500 /* e-paper: coalesce updates across full refresh (~2s) */
/* ========== State ========== */
static bool ui_initialized;
@@ -243,7 +245,9 @@ static void advert_defer_handler(struct k_work *work)
static void schedule_render(void)
{
#ifdef CONFIG_ZEPHCORE_UI_DISPLAY
k_work_reschedule(&render_work, K_MSEC(RENDER_DEBOUNCE_MS));
uint32_t debounce = mc_display_is_epd() ? RENDER_DEBOUNCE_EPD_MS
: RENDER_DEBOUNCE_MS;
k_work_reschedule(&render_work, K_MSEC(debounce));
#endif
}
@@ -591,6 +595,22 @@ static void ui_input_cb(struct input_event *evt, void *user_data)
}
#endif
/* GPS hardware switch (toggle switch, not momentary button).
* Needs both press (ON) and release (OFF) events,
* so handle before the release-event filter below. */
if (evt->code == INPUT_KEY_G) {
bool gps_on = (evt->value != 0);
LOG_INF("GPS switch → %s", gps_on ? "on" : "off");
if (gps_is_available()) {
mesh_gps_set_enabled(gps_on);
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
buzzer_play(gps_on ? MELODY_GPS_ON : MELODY_GPS_OFF);
#endif
schedule_render();
}
return;
}
/* Only handle key press events (value=1), not releases (value=0) */
if (!evt->value) {
return;