Initial import
This commit is contained in:
59
.github/copilot-instructions.md
vendored
Normal file
59
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
# Copilot instructions for loramodem
|
||||
|
||||
Repository at a glance
|
||||
- Project: LoRa KISS modem (Zephyr RTOS)
|
||||
- Purpose: Bridge a KISS serial interface to a LoRa radio (two threads: serial → LoRa, LoRa → serial).
|
||||
- Key components: app/ (Zephyr application), drivers/lora/lr11xx (Semtech LR11xx driver), boards/ (board overlays).
|
||||
|
||||
Quick commands
|
||||
- Build for a board (recommended):
|
||||
west build -b <board> app
|
||||
Example: west build -b heltec_wifi_lora32_v3 app
|
||||
- Flash the built image:
|
||||
west flash
|
||||
- Open Zephyr menuconfig:
|
||||
west build -b <board> app -t menuconfig
|
||||
- CMake alternative (repo root):
|
||||
cmake -S app -B build && cmake --build build
|
||||
|
||||
Testing & lint
|
||||
- Testing strategy (from CLAUDE.md): unit tests run on host (arm64/x86) with a mocked HAL; integration tests run on target hardware. No repo-level test runner or lint targets are present by default — look for a tests/ or scripts/ directory if added.
|
||||
|
||||
High-level architecture (big picture)
|
||||
- app/ contains the Zephyr application (app/src/*.c). main.c starts two cooperative threads:
|
||||
- kiss_rx_thread: reads bytes from the KISS UART, decodes frames (kiss.c), handles SetHardware commands, and calls lora_modem_send().
|
||||
- lora_rx_thread: polls lora_modem_recv() with a finite timeout and writes received payloads as KISS frames to the UART.
|
||||
- KISS protocol implementation: app/src/kiss.c, kiss.h — encoder/decoder and SetHardware sub-command definitions.
|
||||
- Radio abstraction: app/src/lora_modem.c, lora_modem.h — stores runtime params (g_params) protected by a mutex and snapshots params at send/recv time so changes take effect on the next call.
|
||||
- HW driver: drivers/lora/lr11xx implements Zephyr's lora_driver_api. Hardware-specific SPI/GPIO HAL pieces live in lr11xx_hal.c.
|
||||
- Build integration: app/CMakeLists.txt registers drivers/lora/lr11xx as an extra Zephyr module (ZEPHYR_EXTRA_MODULES).
|
||||
|
||||
Key repository conventions and patterns
|
||||
- Chosen nodes and aliases:
|
||||
- The KISS UART is resolved from a chosen node: loramodem_kiss_uart (board overlay must define it).
|
||||
- LoRa device is expected as DT alias lora0 (DEVICE_DT_GET(DT_ALIAS(lora0))).
|
||||
- Kconfig / defaults:
|
||||
- app/prj.conf contains project defaults (frequency, SF, BW, TX power, stack sizes, heap pool setting used by the app).
|
||||
- Runtime radio params are read from Kconfig at init (lora_modem_init) and stored in g_params.
|
||||
- Radio parameter updates:
|
||||
- lora_modem_set_params() validates values and updates g_params under mutex; changes take effect when lora_modem_send() or lora_modem_recv() is next called (thread-safe snapshotting).
|
||||
- KISS SetHardware mapping:
|
||||
- See kiss.h for sub-commands (SET_FREQ, GET_PARAMS, etc.) and main.c for how responses are encoded and sent.
|
||||
- Board overlays and confs:
|
||||
- Overlays live in boards/ or board-specific .overlay files. App-level overrides can live in app.overlay or per-board .conf files.
|
||||
- Memory & testing notes (from CLAUDE.md):
|
||||
- Target memory budget and testing strategy are documented in CLAUDE.md (unit tests on host, integration on target).
|
||||
|
||||
Files to read first when exploring code
|
||||
- app/src/main.c, app/src/kiss.c/h, app/src/lora_modem.c/h, drivers/lora/lr11xx/{*.c,*.h}, app/prj.conf, app/CMakeLists.txt, CLAUDE.md
|
||||
|
||||
AI & editor configs found
|
||||
- CLAUDE.md contains project constraints and test strategy; consult it for memory and testing guidance.
|
||||
- .vscode/settings.json points the CMake source directory to app/ in the original developer setup.
|
||||
- .claude/settings.local.json contains allowed webfetch domains for the Claude assistant.
|
||||
|
||||
If you (the user) want, Copilot sessions should:
|
||||
- Prefer reading the files listed above first.
|
||||
- Respect chosen DT nodes (loramodem_kiss_uart, alias lora0) and Kconfig defaults in app/prj.conf.
|
||||
- When suggesting code changes, keep stack/heap and RAM budgets in CLAUDE.md in mind.
|
||||
|
||||
2
Kconfig
Normal file
2
Kconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
# Root Kconfig — required by zephyr/module.yml (cmake: .)
|
||||
# The application Kconfig is sourced from app/Kconfig via CMake.
|
||||
25
app/CMakeLists.txt
Normal file
25
app/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
# Set BOARD_ROOT and DTS_ROOT to the repo root so that boards/<vendor>/<board>/
|
||||
# is searched for out-of-tree board definitions.
|
||||
# Must be set BEFORE find_package(Zephyr).
|
||||
list(APPEND BOARD_ROOT ${CMAKE_CURRENT_LIST_DIR}/..)
|
||||
list(APPEND DTS_ROOT ${CMAKE_CURRENT_LIST_DIR}/..)
|
||||
|
||||
# Register the LR11xx out-of-tree driver as an extra Zephyr module.
|
||||
# It is only compiled when a semtech,lr11xx DTS node is enabled.
|
||||
list(APPEND ZEPHYR_EXTRA_MODULES
|
||||
${CMAKE_CURRENT_LIST_DIR}/../drivers/lora/lr11xx
|
||||
)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
|
||||
project(loramodem LANGUAGES C)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/kiss.c
|
||||
src/lora_modem.c
|
||||
)
|
||||
|
||||
target_include_directories(app PRIVATE src)
|
||||
71
app/Kconfig
Normal file
71
app/Kconfig
Normal file
@@ -0,0 +1,71 @@
|
||||
mainmenu "LoRa KISS Modem Configuration"
|
||||
|
||||
source "Kconfig.zephyr"
|
||||
|
||||
menu "KISS Interface"
|
||||
|
||||
config LORAMODEM_KISS_IFACE_USB
|
||||
bool "Use USB CDC-ACM as KISS serial interface"
|
||||
depends on USB_DEVICE_STACK || USB_DEVICE_STACK_NEXT
|
||||
help
|
||||
Use USB CDC-ACM virtual serial port for the KISS interface.
|
||||
Requires the host to enumerate the device before framing begins.
|
||||
The board overlay must define the loramodem,kiss-uart chosen node
|
||||
pointing to a cdc_acm_uart instance.
|
||||
|
||||
config LORAMODEM_KISS_IFACE_UART
|
||||
bool "Use hardware UART as KISS serial interface"
|
||||
default y if !LORAMODEM_KISS_IFACE_USB
|
||||
help
|
||||
Use a hardware UART for the KISS interface.
|
||||
The board overlay must define the loramodem,kiss-uart chosen node
|
||||
pointing to the desired uart instance.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "LoRa Radio Parameters"
|
||||
|
||||
config LORAMODEM_LORA_FREQ_HZ
|
||||
int "Center frequency (Hz)"
|
||||
default 915000000
|
||||
help
|
||||
LoRa radio center frequency in Hz.
|
||||
Common values: 915000000 (US/AU), 868000000 (EU), 433000000 (Asia).
|
||||
|
||||
config LORAMODEM_LORA_SF
|
||||
int "Spreading factor"
|
||||
default 9
|
||||
range 7 12
|
||||
help
|
||||
LoRa spreading factor. Higher values increase range and time-on-air.
|
||||
|
||||
config LORAMODEM_LORA_BW
|
||||
int "Bandwidth (kHz)"
|
||||
default 125
|
||||
help
|
||||
LoRa bandwidth in kHz. Valid values: 125, 250, 500.
|
||||
|
||||
config LORAMODEM_LORA_CR
|
||||
int "Coding rate denominator"
|
||||
default 5
|
||||
range 5 8
|
||||
help
|
||||
LoRa coding rate as the denominator N of 4/N.
|
||||
5 = 4/5 (lowest overhead), 8 = 4/8 (highest redundancy).
|
||||
|
||||
config LORAMODEM_LORA_TX_POWER_DBM
|
||||
int "TX power (dBm)"
|
||||
default 14
|
||||
range 2 22
|
||||
|
||||
config LORAMODEM_LORA_PREAMBLE_LEN
|
||||
int "Preamble length (symbols)"
|
||||
default 8
|
||||
range 6 65535
|
||||
|
||||
endmenu
|
||||
|
||||
config LORAMODEM_MAX_PACKET_SIZE
|
||||
int "Maximum LoRa packet payload size (bytes)"
|
||||
default 255
|
||||
range 1 255
|
||||
28
app/prj.conf
Normal file
28
app/prj.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
# LoRa KISS Modem — base project configuration
|
||||
# Board-specific .conf overlays extend or override these settings.
|
||||
|
||||
# Logging
|
||||
CONFIG_LOG=y
|
||||
CONFIG_PRINTK=y
|
||||
|
||||
# LoRa subsystem
|
||||
CONFIG_LORA=y
|
||||
|
||||
# UART (interrupt-driven for non-blocking byte-by-byte processing)
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||
|
||||
# Heap for KISS frame buffers and dynamic allocations
|
||||
CONFIG_HEAP_MEM_POOL_SIZE=4096
|
||||
|
||||
# Zephyr kernel threads
|
||||
CONFIG_MAIN_STACK_SIZE=2048
|
||||
|
||||
# Default radio parameters (US 915 MHz, SF9, BW125, CR 4/5)
|
||||
CONFIG_LORAMODEM_LORA_FREQ_HZ=915000000
|
||||
CONFIG_LORAMODEM_LORA_SF=9
|
||||
CONFIG_LORAMODEM_LORA_BW=125
|
||||
CONFIG_LORAMODEM_LORA_CR=5
|
||||
CONFIG_LORAMODEM_LORA_TX_POWER_DBM=14
|
||||
CONFIG_LORAMODEM_LORA_PREAMBLE_LEN=8
|
||||
CONFIG_LORAMODEM_MAX_PACKET_SIZE=255
|
||||
109
app/src/kiss.c
Normal file
109
app/src/kiss.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* KISS protocol encoder and decoder implementation.
|
||||
*/
|
||||
|
||||
#include "kiss.h"
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
int kiss_encode_cmd(uint8_t cmd, const uint8_t *data, size_t data_len,
|
||||
uint8_t *out_buf, size_t out_size)
|
||||
{
|
||||
size_t out_idx = 0;
|
||||
|
||||
/* Worst-case output: FEND + CMD + (2 * data_len) + FEND */
|
||||
if (out_size < (2 + 2 * data_len + 1)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
out_buf[out_idx++] = KISS_FEND;
|
||||
out_buf[out_idx++] = cmd;
|
||||
|
||||
for (size_t i = 0; i < data_len; i++) {
|
||||
if (data[i] == KISS_FEND) {
|
||||
out_buf[out_idx++] = KISS_FESC;
|
||||
out_buf[out_idx++] = KISS_TFEND;
|
||||
} else if (data[i] == KISS_FESC) {
|
||||
out_buf[out_idx++] = KISS_FESC;
|
||||
out_buf[out_idx++] = KISS_TFESC;
|
||||
} else {
|
||||
out_buf[out_idx++] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
out_buf[out_idx++] = KISS_FEND;
|
||||
return (int)out_idx;
|
||||
}
|
||||
|
||||
int kiss_encode(const uint8_t *data, size_t data_len,
|
||||
uint8_t *out_buf, size_t out_size)
|
||||
{
|
||||
return kiss_encode_cmd(KISS_CMD_DATA, data, data_len, out_buf, out_size);
|
||||
}
|
||||
|
||||
void kiss_decoder_init(struct kiss_decoder_ctx *ctx,
|
||||
uint8_t *buf, size_t size)
|
||||
{
|
||||
ctx->frame_buf = buf;
|
||||
ctx->frame_buf_size = size;
|
||||
ctx->frame_len = 0;
|
||||
ctx->in_frame = false;
|
||||
ctx->in_escape = false;
|
||||
ctx->cmd_received = false;
|
||||
ctx->cmd = 0;
|
||||
}
|
||||
|
||||
bool kiss_decoder_feed(struct kiss_decoder_ctx *ctx, uint8_t byte)
|
||||
{
|
||||
if (byte == KISS_FEND) {
|
||||
if (ctx->in_frame && ctx->frame_len > 0) {
|
||||
/* Closing delimiter — frame complete */
|
||||
ctx->in_frame = false;
|
||||
ctx->in_escape = false;
|
||||
ctx->cmd_received = false;
|
||||
return true;
|
||||
}
|
||||
/* Opening delimiter (or spurious FEND) — start fresh */
|
||||
ctx->in_frame = true;
|
||||
ctx->in_escape = false;
|
||||
ctx->cmd_received = false;
|
||||
ctx->frame_len = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ctx->in_frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Handle escape sequences */
|
||||
if (byte == KISS_FESC) {
|
||||
ctx->in_escape = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx->in_escape) {
|
||||
ctx->in_escape = false;
|
||||
if (byte == KISS_TFEND) {
|
||||
byte = KISS_FEND;
|
||||
} else if (byte == KISS_TFESC) {
|
||||
byte = KISS_FESC;
|
||||
}
|
||||
/* Unknown escape sequences: discard the escape, keep the byte */
|
||||
}
|
||||
|
||||
/* First unescaped byte after opening FEND is the CMD byte */
|
||||
if (!ctx->cmd_received) {
|
||||
ctx->cmd = byte;
|
||||
ctx->cmd_received = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Append to payload buffer (silently drop if full) */
|
||||
if (ctx->frame_len < ctx->frame_buf_size) {
|
||||
ctx->frame_buf[ctx->frame_len++] = byte;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
125
app/src/kiss.h
Normal file
125
app/src/kiss.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* KISS (Keep It Simple Stupid) protocol encoder/decoder.
|
||||
*
|
||||
* Frame format:
|
||||
* FEND | CMD | <data with escaping> | FEND
|
||||
*
|
||||
* Escape sequences (inside frame data only):
|
||||
* 0xC0 → 0xDB 0xDC (FESC TFEND)
|
||||
* 0xDB → 0xDB 0xDD (FESC TFESC)
|
||||
*/
|
||||
#ifndef KISS_H
|
||||
#define KISS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Special bytes */
|
||||
#define KISS_FEND 0xC0u /* Frame delimiter */
|
||||
#define KISS_FESC 0xDBu /* Escape byte */
|
||||
#define KISS_TFEND 0xDCu /* Escaped FEND */
|
||||
#define KISS_TFESC 0xDDu /* Escaped FESC */
|
||||
|
||||
/* Command codes (upper nibble = port 0) */
|
||||
#define KISS_CMD_DATA 0x00u
|
||||
#define KISS_CMD_TXDELAY 0x01u
|
||||
#define KISS_CMD_PERSISTENCE 0x02u
|
||||
#define KISS_CMD_SLOTTIME 0x03u
|
||||
#define KISS_CMD_TXTAIL 0x04u
|
||||
#define KISS_CMD_FULLDUPLEX 0x05u
|
||||
#define KISS_CMD_SETHARDWARE 0x06u
|
||||
#define KISS_CMD_RETURN 0xFFu
|
||||
|
||||
/*
|
||||
* SetHardware (0x06) sub-commands — device-specific radio configuration.
|
||||
*
|
||||
* Frame payload format: [sub-cmd 1 byte] [params...]
|
||||
*
|
||||
* SET commands change a parameter and apply it immediately.
|
||||
* GET commands cause the device to respond with a SetHardware frame
|
||||
* containing the same sub-command byte followed by the current value(s).
|
||||
*
|
||||
* Frequency: uint32_t big-endian, Hz (e.g. 0x36892580 = 915000000)
|
||||
* BW: uint16_t big-endian, kHz (125, 250, or 500)
|
||||
* SF: uint8_t 7..12
|
||||
* CR: uint8_t coding-rate denominator 5..8 (meaning 4/5 .. 4/8)
|
||||
* TX power: int8_t dBm
|
||||
*/
|
||||
#define KISS_HW_SET_FREQ 0x01u /* payload: 4 bytes freq_hz (BE uint32) */
|
||||
#define KISS_HW_GET_FREQ 0x02u /* no params; response: sub-cmd + 4 bytes */
|
||||
#define KISS_HW_SET_SF 0x03u /* payload: 1 byte sf (7-12) */
|
||||
#define KISS_HW_SET_BW 0x04u /* payload: 2 bytes bw_khz (BE uint16: 125/250/500) */
|
||||
#define KISS_HW_SET_CR 0x05u /* payload: 1 byte cr denom (5-8) */
|
||||
#define KISS_HW_SET_TXPWR 0x06u /* payload: 1 byte tx_power_dbm (int8) */
|
||||
#define KISS_HW_GET_PARAMS 0x07u /* no params; response: sub-cmd + all params (10 bytes) */
|
||||
/*
|
||||
* GET_PARAMS response payload (10 bytes after sub-cmd byte):
|
||||
* [0..3] freq_hz BE uint32
|
||||
* [4] sf uint8
|
||||
* [5..6] bw_khz BE uint16
|
||||
* [7] cr uint8 (denominator)
|
||||
* [8] tx_power int8 (dBm)
|
||||
* [9] preamble uint8
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Encode data into a KISS frame with an explicit command byte.
|
||||
*
|
||||
* Writes FEND + cmd + escaped data + FEND into @p out_buf.
|
||||
*
|
||||
* @param cmd KISS command byte (e.g. KISS_CMD_DATA, KISS_CMD_SETHARDWARE).
|
||||
* @param data Payload bytes (may be NULL when data_len == 0).
|
||||
* @param data_len Length of payload.
|
||||
* @param out_buf Output buffer for the encoded frame.
|
||||
* @param out_size Size of the output buffer.
|
||||
* @return Number of bytes written, or -ENOMEM if @p out_buf is too small.
|
||||
*/
|
||||
int kiss_encode_cmd(uint8_t cmd, const uint8_t *data, size_t data_len,
|
||||
uint8_t *out_buf, size_t out_size);
|
||||
|
||||
/**
|
||||
* @brief Encode raw data into a KISS data frame (CMD = 0x00).
|
||||
*
|
||||
* Convenience wrapper around kiss_encode_cmd() using KISS_CMD_DATA.
|
||||
*/
|
||||
int kiss_encode(const uint8_t *data, size_t data_len,
|
||||
uint8_t *out_buf, size_t out_size);
|
||||
|
||||
/**
|
||||
* @brief KISS decoder context. Maintains state between calls to kiss_decoder_feed().
|
||||
*/
|
||||
struct kiss_decoder_ctx {
|
||||
uint8_t *frame_buf; /**< Caller-supplied buffer for decoded payload. */
|
||||
size_t frame_buf_size; /**< Size of frame_buf. */
|
||||
size_t frame_len; /**< Number of payload bytes decoded so far. */
|
||||
bool in_frame; /**< True after the opening FEND has been seen. */
|
||||
bool in_escape; /**< True after FESC has been seen. */
|
||||
bool cmd_received; /**< True once the CMD byte has been consumed. */
|
||||
uint8_t cmd; /**< KISS command byte of the current frame. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize a KISS decoder context.
|
||||
*
|
||||
* @param ctx Context to initialize.
|
||||
* @param buf Buffer for decoded frame payload.
|
||||
* @param size Size of @p buf.
|
||||
*/
|
||||
void kiss_decoder_init(struct kiss_decoder_ctx *ctx,
|
||||
uint8_t *buf, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Feed one byte into the KISS decoder state machine.
|
||||
*
|
||||
* @param ctx Decoder context.
|
||||
* @param byte Next byte from the serial stream.
|
||||
* @return true when a complete frame has been received.
|
||||
* ctx->frame_buf[0..ctx->frame_len-1] holds the payload.
|
||||
* ctx->cmd holds the KISS command byte.
|
||||
*/
|
||||
bool kiss_decoder_feed(struct kiss_decoder_ctx *ctx, uint8_t byte);
|
||||
|
||||
#endif /* KISS_H */
|
||||
218
app/src/lora_modem.c
Normal file
218
app/src/lora_modem.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* LoRa modem abstraction layer.
|
||||
* Maps runtime lora_radio_params to Zephyr's lora_modem_config enums
|
||||
* and provides a simple send/recv API for the KISS bridge.
|
||||
*
|
||||
* Runtime parameters are stored in g_params, protected by g_params_mutex.
|
||||
* send() and recv() snapshot the params under the mutex before configuring
|
||||
* the radio, so a concurrent frequency change takes effect on the next call.
|
||||
*/
|
||||
|
||||
#include "lora_modem.h"
|
||||
|
||||
#include <zephyr/drivers/lora.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <string.h>
|
||||
|
||||
LOG_MODULE_REGISTER(lora_modem, LOG_LEVEL_INF);
|
||||
|
||||
#define LORA_DEV DEVICE_DT_GET(DT_ALIAS(lora0))
|
||||
|
||||
/* ── Runtime parameter state ─────────────────────────────────────────────── */
|
||||
|
||||
static struct lora_radio_params g_params;
|
||||
static K_MUTEX_DEFINE(g_params_mutex);
|
||||
|
||||
/* ── Enum conversion helpers ─────────────────────────────────────────────── */
|
||||
|
||||
static enum lora_signal_bandwidth bw_to_enum(uint16_t bw_khz)
|
||||
{
|
||||
switch (bw_khz) {
|
||||
case 500: return BW_500_KHZ;
|
||||
case 250: return BW_250_KHZ;
|
||||
case 125: /* fall through */
|
||||
default: return BW_125_KHZ;
|
||||
}
|
||||
}
|
||||
|
||||
static enum lora_datarate sf_to_enum(uint8_t sf)
|
||||
{
|
||||
switch (sf) {
|
||||
case 6: return SF_6;
|
||||
case 7: return SF_7;
|
||||
case 8: return SF_8;
|
||||
case 9: return SF_9;
|
||||
case 10: return SF_10;
|
||||
case 11: return SF_11;
|
||||
case 12: /* fall through */
|
||||
default: return SF_12;
|
||||
}
|
||||
}
|
||||
|
||||
static enum lora_coding_rate cr_to_enum(uint8_t cr_denom)
|
||||
{
|
||||
switch (cr_denom) {
|
||||
case 5: return CR_4_5;
|
||||
case 6: return CR_4_6;
|
||||
case 7: return CR_4_7;
|
||||
case 8: /* fall through */
|
||||
default: return CR_4_8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a lora_modem_config from a params snapshot */
|
||||
static void params_to_cfg(const struct lora_radio_params *p,
|
||||
struct lora_modem_config *cfg, bool tx)
|
||||
{
|
||||
cfg->frequency = p->freq_hz;
|
||||
cfg->bandwidth = bw_to_enum(p->bw_khz);
|
||||
cfg->datarate = sf_to_enum(p->sf);
|
||||
cfg->coding_rate = cr_to_enum(p->cr);
|
||||
cfg->preamble_len = p->preamble_len;
|
||||
cfg->tx_power = p->tx_power;
|
||||
cfg->tx = tx;
|
||||
}
|
||||
|
||||
/* ── Public API ──────────────────────────────────────────────────────────── */
|
||||
|
||||
int lora_modem_init(void)
|
||||
{
|
||||
const struct device *lora_dev = LORA_DEV;
|
||||
|
||||
if (!device_is_ready(lora_dev)) {
|
||||
LOG_ERR("LoRa device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Load compile-time defaults into runtime params */
|
||||
g_params.freq_hz = CONFIG_LORAMODEM_LORA_FREQ_HZ;
|
||||
g_params.sf = CONFIG_LORAMODEM_LORA_SF;
|
||||
g_params.bw_khz = CONFIG_LORAMODEM_LORA_BW;
|
||||
g_params.cr = CONFIG_LORAMODEM_LORA_CR;
|
||||
g_params.tx_power = CONFIG_LORAMODEM_LORA_TX_POWER_DBM;
|
||||
g_params.preamble_len = CONFIG_LORAMODEM_LORA_PREAMBLE_LEN;
|
||||
|
||||
LOG_INF("LoRa init: %u Hz SF%u BW%ukHz CR4/%u %d dBm",
|
||||
g_params.freq_hz, g_params.sf, g_params.bw_khz,
|
||||
g_params.cr, g_params.tx_power);
|
||||
|
||||
struct lora_modem_config cfg;
|
||||
|
||||
params_to_cfg(&g_params, &cfg, true);
|
||||
return lora_config(lora_dev, &cfg);
|
||||
}
|
||||
|
||||
void lora_modem_get_params(struct lora_radio_params *params)
|
||||
{
|
||||
k_mutex_lock(&g_params_mutex, K_FOREVER);
|
||||
*params = g_params;
|
||||
k_mutex_unlock(&g_params_mutex);
|
||||
}
|
||||
|
||||
int lora_modem_set_params(const struct lora_radio_params *params)
|
||||
{
|
||||
if (params->sf < 6 || params->sf > 12) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (params->bw_khz != 125 && params->bw_khz != 250 &&
|
||||
params->bw_khz != 500) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (params->cr < 5 || params->cr > 8) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (params->freq_hz == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&g_params_mutex, K_FOREVER);
|
||||
g_params = *params;
|
||||
k_mutex_unlock(&g_params_mutex);
|
||||
|
||||
LOG_INF("Params updated: %u Hz SF%u BW%ukHz CR4/%u %d dBm",
|
||||
params->freq_hz, params->sf, params->bw_khz,
|
||||
params->cr, params->tx_power);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lora_modem_set_freq(uint32_t freq_hz)
|
||||
{
|
||||
if (freq_hz == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&g_params_mutex, K_FOREVER);
|
||||
g_params.freq_hz = freq_hz;
|
||||
k_mutex_unlock(&g_params_mutex);
|
||||
|
||||
LOG_INF("Frequency set to %u Hz", freq_hz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lora_modem_send(const uint8_t *data, size_t len)
|
||||
{
|
||||
const struct device *lora_dev = LORA_DEV;
|
||||
struct lora_radio_params snap;
|
||||
struct lora_modem_config tx_cfg;
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&g_params_mutex, K_FOREVER);
|
||||
snap = g_params;
|
||||
k_mutex_unlock(&g_params_mutex);
|
||||
|
||||
params_to_cfg(&snap, &tx_cfg, true);
|
||||
|
||||
ret = lora_config(lora_dev, &tx_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("lora_config(TX) failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lora_send(lora_dev, (uint8_t *)data, (uint32_t)len);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("lora_send failed: %d", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int lora_modem_recv(uint8_t *buf, size_t buf_size, k_timeout_t timeout,
|
||||
int16_t *rssi_out, int8_t *snr_out)
|
||||
{
|
||||
const struct device *lora_dev = LORA_DEV;
|
||||
struct lora_radio_params snap;
|
||||
struct lora_modem_config rx_cfg;
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&g_params_mutex, K_FOREVER);
|
||||
snap = g_params;
|
||||
k_mutex_unlock(&g_params_mutex);
|
||||
|
||||
params_to_cfg(&snap, &rx_cfg, false);
|
||||
|
||||
ret = lora_config(lora_dev, &rx_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("lora_config(RX) failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int16_t rssi = 0;
|
||||
int8_t snr = 0;
|
||||
|
||||
ret = lora_recv(lora_dev, buf, (uint8_t)buf_size, timeout, &rssi, &snr);
|
||||
|
||||
if (ret >= 0) {
|
||||
LOG_DBG("RX %d bytes RSSI=%d SNR=%d", ret, rssi, snr);
|
||||
if (rssi_out) {
|
||||
*rssi_out = rssi;
|
||||
}
|
||||
if (snr_out) {
|
||||
*snr_out = snr;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
92
app/src/lora_modem.h
Normal file
92
app/src/lora_modem.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* LoRa modem abstraction layer.
|
||||
* Wraps Zephyr's lora_driver_api with application-level configuration.
|
||||
*/
|
||||
#ifndef LORA_MODEM_H
|
||||
#define LORA_MODEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
/**
|
||||
* @brief Runtime radio parameters.
|
||||
*
|
||||
* All fields are in user-friendly units. Modified at runtime via
|
||||
* lora_modem_set_params() / lora_modem_set_freq(); read via
|
||||
* lora_modem_get_params().
|
||||
*/
|
||||
struct lora_radio_params {
|
||||
uint32_t freq_hz; /**< Center frequency in Hz */
|
||||
uint8_t sf; /**< Spreading factor (7-12) */
|
||||
uint16_t bw_khz; /**< Bandwidth in kHz (125, 250, or 500) */
|
||||
uint8_t cr; /**< Coding rate denominator (5..8, meaning 4/5..4/8) */
|
||||
int8_t tx_power; /**< TX power in dBm */
|
||||
uint8_t preamble_len; /**< Preamble length in symbols */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize and configure the LoRa radio.
|
||||
*
|
||||
* Loads compile-time Kconfig defaults into the runtime params struct
|
||||
* and applies them. Call once at startup before any send/recv.
|
||||
*
|
||||
* @return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int lora_modem_init(void);
|
||||
|
||||
/**
|
||||
* @brief Get the current radio parameters.
|
||||
*
|
||||
* @param params Output: filled with current settings.
|
||||
*/
|
||||
void lora_modem_get_params(struct lora_radio_params *params);
|
||||
|
||||
/**
|
||||
* @brief Set all radio parameters at once.
|
||||
*
|
||||
* Parameters are stored for use on the next send/recv call.
|
||||
* The radio is NOT immediately reconfigured; the change takes effect
|
||||
* at the start of the next lora_modem_send() or lora_modem_recv() call.
|
||||
*
|
||||
* @param params New parameter values.
|
||||
* @return 0 on success, -EINVAL if a parameter is out of range.
|
||||
*/
|
||||
int lora_modem_set_params(const struct lora_radio_params *params);
|
||||
|
||||
/**
|
||||
* @brief Change only the center frequency.
|
||||
*
|
||||
* Convenience wrapper around lora_modem_set_params() for the common
|
||||
* case of a frequency-only change.
|
||||
*
|
||||
* @param freq_hz New center frequency in Hz.
|
||||
* @return 0 on success, -EINVAL if out of range.
|
||||
*/
|
||||
int lora_modem_set_freq(uint32_t freq_hz);
|
||||
|
||||
/**
|
||||
* @brief Transmit a packet over LoRa.
|
||||
*
|
||||
* @param data Payload bytes.
|
||||
* @param len Payload length (must be <= CONFIG_LORAMODEM_MAX_PACKET_SIZE).
|
||||
* @return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int lora_modem_send(const uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Receive a packet from LoRa (blocking).
|
||||
*
|
||||
* @param buf Buffer for received payload.
|
||||
* @param buf_size Size of @p buf.
|
||||
* @param timeout How long to wait (use K_FOREVER to wait indefinitely).
|
||||
* @param rssi_out If non-NULL, receives RSSI of the packet (dBm).
|
||||
* @param snr_out If non-NULL, receives SNR of the packet (dB).
|
||||
* @return Number of bytes received (>= 0), or negative errno on error/timeout.
|
||||
*/
|
||||
int lora_modem_recv(uint8_t *buf, size_t buf_size, k_timeout_t timeout,
|
||||
int16_t *rssi_out, int8_t *snr_out);
|
||||
|
||||
#endif /* LORA_MODEM_H */
|
||||
362
app/src/main.c
Normal file
362
app/src/main.c
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* LoRa KISS Modem — main application.
|
||||
*
|
||||
* Two threads bridge a KISS serial interface to a LoRa radio:
|
||||
*
|
||||
* kiss_rx_thread: reads bytes from the KISS UART, decodes KISS frames,
|
||||
* dispatches data frames to lora_modem_send(), and
|
||||
* handles SetHardware (0x06) frames for radio parameter
|
||||
* changes (frequency, SF, BW, CR, TX power).
|
||||
*
|
||||
* lora_rx_thread: loops with a 1-second timeout on lora_modem_recv();
|
||||
* on each successful receive it encodes the payload as
|
||||
* a KISS data frame and writes it to the KISS UART.
|
||||
* The finite timeout ensures frequency changes applied
|
||||
* by kiss_rx_thread take effect within 1 second.
|
||||
*
|
||||
* SetHardware sub-command protocol (see kiss.h for KISS_HW_* defines):
|
||||
*
|
||||
* Host → device: FEND | 0x06 | <sub-cmd> [params...] | FEND
|
||||
* Device → host: FEND | 0x06 | <sub-cmd> [current-value...] | FEND
|
||||
* (only sent in response to GET commands)
|
||||
*
|
||||
* SET_FREQ (0x01): payload = 4 bytes freq_hz big-endian uint32
|
||||
* GET_FREQ (0x02): no params; response = sub-cmd + 4-byte freq
|
||||
* SET_SF (0x03): payload = 1 byte sf (7-12)
|
||||
* SET_BW (0x04): payload = 2 bytes bw_khz big-endian uint16 (125/250/500)
|
||||
* SET_CR (0x05): payload = 1 byte cr denominator (5-8)
|
||||
* SET_TXPWR(0x06): payload = 1 byte tx_power_dbm (int8)
|
||||
* GET_PARAMS(0x07): no params; response = sub-cmd + 10-byte param block
|
||||
*
|
||||
* GET_PARAMS response (10 bytes after sub-cmd):
|
||||
* [0..3] freq_hz BE uint32
|
||||
* [4] sf uint8
|
||||
* [5..6] bw_khz BE uint16
|
||||
* [7] cr uint8 (denominator)
|
||||
* [8] tx_power int8 (dBm)
|
||||
* [9] preamble uint8
|
||||
*/
|
||||
|
||||
#include "kiss.h"
|
||||
#include "lora_modem.h"
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
|
||||
|
||||
/* Resolve the KISS UART from the board overlay */
|
||||
#define KISS_UART_NODE DT_CHOSEN(loramodem_kiss_uart)
|
||||
#define KISS_UART DEVICE_DT_GET(KISS_UART_NODE)
|
||||
|
||||
/* Thread stack sizes */
|
||||
#define KISS_RX_STACK_SIZE 2048
|
||||
#define LORA_RX_STACK_SIZE 2048
|
||||
|
||||
/* lora_rx_fn polls with this timeout so frequency changes take effect promptly */
|
||||
#define LORA_RX_TIMEOUT_MS 1000
|
||||
|
||||
/* Packet buffers */
|
||||
static uint8_t lora_rx_buf[CONFIG_LORAMODEM_MAX_PACKET_SIZE];
|
||||
/* kiss_tx_buf is shared by both threads — protected by uart_tx_mutex */
|
||||
static uint8_t kiss_tx_buf[CONFIG_LORAMODEM_MAX_PACKET_SIZE * 2 + 4];
|
||||
|
||||
/* Mutex protecting writes to the KISS UART (used by both threads) */
|
||||
static K_MUTEX_DEFINE(uart_tx_mutex);
|
||||
|
||||
/* ── UART helpers ─────────────────────────────────────────────────────────── */
|
||||
|
||||
static void uart_write_bytes(const struct device *uart,
|
||||
const uint8_t *buf, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uart_poll_out(uart, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Encode and send a KISS SetHardware response frame to the host */
|
||||
static void send_hw_response(const struct device *uart,
|
||||
const uint8_t *payload, size_t payload_len)
|
||||
{
|
||||
k_mutex_lock(&uart_tx_mutex, K_FOREVER);
|
||||
int enc = kiss_encode_cmd(KISS_CMD_SETHARDWARE,
|
||||
payload, payload_len,
|
||||
kiss_tx_buf, sizeof(kiss_tx_buf));
|
||||
if (enc > 0) {
|
||||
uart_write_bytes(uart, kiss_tx_buf, (size_t)enc);
|
||||
}
|
||||
k_mutex_unlock(&uart_tx_mutex);
|
||||
}
|
||||
|
||||
/* ── SetHardware dispatcher ──────────────────────────────────────────────── */
|
||||
|
||||
static void handle_sethardware(const struct device *uart,
|
||||
const uint8_t *payload, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
LOG_WRN("SetHardware: empty payload");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t sub_cmd = payload[0];
|
||||
const uint8_t *args = payload + 1;
|
||||
size_t args_len = len - 1;
|
||||
|
||||
switch (sub_cmd) {
|
||||
|
||||
case KISS_HW_SET_FREQ: {
|
||||
if (args_len < 4) {
|
||||
LOG_WRN("SET_FREQ: need 4 bytes, got %zu", args_len);
|
||||
return;
|
||||
}
|
||||
uint32_t freq = ((uint32_t)args[0] << 24) |
|
||||
((uint32_t)args[1] << 16) |
|
||||
((uint32_t)args[2] << 8) |
|
||||
((uint32_t)args[3]);
|
||||
if (lora_modem_set_freq(freq) < 0) {
|
||||
LOG_WRN("SET_FREQ: invalid frequency %u Hz", freq);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case KISS_HW_GET_FREQ: {
|
||||
struct lora_radio_params p;
|
||||
|
||||
lora_modem_get_params(&p);
|
||||
uint8_t resp[5];
|
||||
|
||||
resp[0] = KISS_HW_GET_FREQ;
|
||||
resp[1] = (uint8_t)(p.freq_hz >> 24);
|
||||
resp[2] = (uint8_t)(p.freq_hz >> 16);
|
||||
resp[3] = (uint8_t)(p.freq_hz >> 8);
|
||||
resp[4] = (uint8_t)(p.freq_hz);
|
||||
send_hw_response(uart, resp, sizeof(resp));
|
||||
break;
|
||||
}
|
||||
|
||||
case KISS_HW_SET_SF: {
|
||||
if (args_len < 1) {
|
||||
LOG_WRN("SET_SF: need 1 byte");
|
||||
return;
|
||||
}
|
||||
struct lora_radio_params p;
|
||||
|
||||
lora_modem_get_params(&p);
|
||||
p.sf = args[0];
|
||||
if (lora_modem_set_params(&p) < 0) {
|
||||
LOG_WRN("SET_SF: invalid SF %u", args[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case KISS_HW_SET_BW: {
|
||||
if (args_len < 2) {
|
||||
LOG_WRN("SET_BW: need 2 bytes");
|
||||
return;
|
||||
}
|
||||
uint16_t bw = ((uint16_t)args[0] << 8) | args[1];
|
||||
struct lora_radio_params p;
|
||||
|
||||
lora_modem_get_params(&p);
|
||||
p.bw_khz = bw;
|
||||
if (lora_modem_set_params(&p) < 0) {
|
||||
LOG_WRN("SET_BW: invalid BW %u kHz", bw);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case KISS_HW_SET_CR: {
|
||||
if (args_len < 1) {
|
||||
LOG_WRN("SET_CR: need 1 byte");
|
||||
return;
|
||||
}
|
||||
struct lora_radio_params p;
|
||||
|
||||
lora_modem_get_params(&p);
|
||||
p.cr = args[0];
|
||||
if (lora_modem_set_params(&p) < 0) {
|
||||
LOG_WRN("SET_CR: invalid CR %u", args[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case KISS_HW_SET_TXPWR: {
|
||||
if (args_len < 1) {
|
||||
LOG_WRN("SET_TXPWR: need 1 byte");
|
||||
return;
|
||||
}
|
||||
struct lora_radio_params p;
|
||||
|
||||
lora_modem_get_params(&p);
|
||||
p.tx_power = (int8_t)args[0];
|
||||
lora_modem_set_params(&p);
|
||||
break;
|
||||
}
|
||||
|
||||
case KISS_HW_GET_PARAMS: {
|
||||
struct lora_radio_params p;
|
||||
|
||||
lora_modem_get_params(&p);
|
||||
|
||||
uint8_t resp[11];
|
||||
|
||||
resp[0] = KISS_HW_GET_PARAMS;
|
||||
resp[1] = (uint8_t)(p.freq_hz >> 24);
|
||||
resp[2] = (uint8_t)(p.freq_hz >> 16);
|
||||
resp[3] = (uint8_t)(p.freq_hz >> 8);
|
||||
resp[4] = (uint8_t)(p.freq_hz);
|
||||
resp[5] = p.sf;
|
||||
resp[6] = (uint8_t)(p.bw_khz >> 8);
|
||||
resp[7] = (uint8_t)(p.bw_khz);
|
||||
resp[8] = p.cr;
|
||||
resp[9] = (uint8_t)p.tx_power;
|
||||
resp[10] = p.preamble_len;
|
||||
send_hw_response(uart, resp, sizeof(resp));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_WRN("SetHardware: unknown sub-cmd 0x%02x", sub_cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── KISS RX thread: serial → LoRa ────────────────────────────────────────── */
|
||||
|
||||
K_THREAD_STACK_DEFINE(kiss_rx_stack, KISS_RX_STACK_SIZE);
|
||||
static struct k_thread kiss_rx_thread;
|
||||
|
||||
static void kiss_rx_fn(void *p1, void *p2, void *p3)
|
||||
{
|
||||
ARG_UNUSED(p1);
|
||||
ARG_UNUSED(p2);
|
||||
ARG_UNUSED(p3);
|
||||
|
||||
const struct device *uart = KISS_UART;
|
||||
struct kiss_decoder_ctx dec;
|
||||
static uint8_t dec_buf[CONFIG_LORAMODEM_MAX_PACKET_SIZE];
|
||||
|
||||
kiss_decoder_init(&dec, dec_buf, sizeof(dec_buf));
|
||||
|
||||
#if defined(CONFIG_LORAMODEM_KISS_IFACE_USB)
|
||||
uint32_t dtr = 0;
|
||||
|
||||
LOG_INF("Waiting for USB host...");
|
||||
while (!dtr) {
|
||||
uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr);
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
LOG_INF("USB host connected");
|
||||
#endif
|
||||
|
||||
LOG_INF("KISS RX thread started");
|
||||
|
||||
uint8_t byte;
|
||||
|
||||
while (true) {
|
||||
if (uart_poll_in(uart, &byte) == 0) {
|
||||
if (kiss_decoder_feed(&dec, byte)) {
|
||||
if (dec.cmd == KISS_CMD_DATA &&
|
||||
dec.frame_len > 0) {
|
||||
LOG_DBG("TX %zu bytes via LoRa",
|
||||
dec.frame_len);
|
||||
lora_modem_send(dec.frame_buf,
|
||||
dec.frame_len);
|
||||
} else if (dec.cmd == KISS_CMD_SETHARDWARE) {
|
||||
handle_sethardware(uart,
|
||||
dec.frame_buf,
|
||||
dec.frame_len);
|
||||
}
|
||||
kiss_decoder_init(&dec, dec_buf,
|
||||
sizeof(dec_buf));
|
||||
}
|
||||
} else {
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ── LoRa RX thread: LoRa → serial ────────────────────────────────────────── */
|
||||
|
||||
K_THREAD_STACK_DEFINE(lora_rx_stack, LORA_RX_STACK_SIZE);
|
||||
static struct k_thread lora_rx_thread;
|
||||
|
||||
static void lora_rx_fn(void *p1, void *p2, void *p3)
|
||||
{
|
||||
ARG_UNUSED(p1);
|
||||
ARG_UNUSED(p2);
|
||||
ARG_UNUSED(p3);
|
||||
|
||||
const struct device *uart = KISS_UART;
|
||||
int16_t rssi;
|
||||
int8_t snr;
|
||||
|
||||
LOG_INF("LoRa RX thread started");
|
||||
|
||||
while (true) {
|
||||
/*
|
||||
* Use a finite timeout so any frequency change applied by
|
||||
* kiss_rx_thread takes effect within LORA_RX_TIMEOUT_MS.
|
||||
* lora_modem_recv() snapshots g_params at the start of each
|
||||
* call, so the new frequency is picked up automatically on
|
||||
* the next iteration.
|
||||
*/
|
||||
int len = lora_modem_recv(lora_rx_buf, sizeof(lora_rx_buf),
|
||||
K_MSEC(LORA_RX_TIMEOUT_MS),
|
||||
&rssi, &snr);
|
||||
|
||||
if (len == -ETIMEDOUT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
LOG_WRN("lora_modem_recv error: %d", len);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DBG("RX %d bytes RSSI=%d SNR=%d", len, rssi, snr);
|
||||
|
||||
k_mutex_lock(&uart_tx_mutex, K_FOREVER);
|
||||
int enc_len = kiss_encode(lora_rx_buf, (size_t)len,
|
||||
kiss_tx_buf, sizeof(kiss_tx_buf));
|
||||
if (enc_len > 0) {
|
||||
uart_write_bytes(uart, kiss_tx_buf, (size_t)enc_len);
|
||||
} else {
|
||||
LOG_WRN("kiss_encode error: %d", enc_len);
|
||||
}
|
||||
k_mutex_unlock(&uart_tx_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Entry point ──────────────────────────────────────────────────────────── */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
LOG_INF("LoRa KISS Modem starting");
|
||||
|
||||
ret = lora_modem_init();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("LoRa modem init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_thread_create(&kiss_rx_thread, kiss_rx_stack,
|
||||
K_THREAD_STACK_SIZEOF(kiss_rx_stack),
|
||||
kiss_rx_fn, NULL, NULL, NULL,
|
||||
K_PRIO_COOP(5), 0, K_NO_WAIT);
|
||||
k_thread_name_set(&kiss_rx_thread, "kiss_rx");
|
||||
|
||||
k_thread_create(&lora_rx_thread, lora_rx_stack,
|
||||
K_THREAD_STACK_SIZEOF(lora_rx_stack),
|
||||
lora_rx_fn, NULL, NULL, NULL,
|
||||
K_PRIO_COOP(5), 0, K_NO_WAIT);
|
||||
k_thread_name_set(&lora_rx_thread, "lora_rx");
|
||||
|
||||
return 0;
|
||||
}
|
||||
6
boards/heltec/heltec_meshpocket/Kconfig.defconfig
Normal file
6
boards/heltec/heltec_meshpocket/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_HELTEC_MESHPOCKET
|
||||
|
||||
config BOARD
|
||||
default "heltec_meshpocket"
|
||||
|
||||
endif # BOARD_HELTEC_MESHPOCKET
|
||||
@@ -0,0 +1,2 @@
|
||||
config BOARD_HELTEC_MESHPOCKET
|
||||
select SOC_NRF52840_QIAA
|
||||
2
boards/heltec/heltec_meshpocket/board.cmake
Normal file
2
boards/heltec/heltec_meshpocket/board.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
# Heltec MeshPocket — board.cmake
|
||||
include(${ZEPHYR_BASE}/boards/nordic/common/board.cmake)
|
||||
7
boards/heltec/heltec_meshpocket/board.yml
Normal file
7
boards/heltec/heltec_meshpocket/board.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
board:
|
||||
name: heltec_meshpocket
|
||||
full_name: "Heltec MeshPocket"
|
||||
vendor: heltec
|
||||
url: https://heltec.org/project/mesh-pocket/
|
||||
socs:
|
||||
- name: nrf52840
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec MeshPocket — pin control definitions
|
||||
* SoC: nRF52840-QIAA
|
||||
*/
|
||||
|
||||
#include <nordic/nrf52840.dtsi>
|
||||
#include <dt-bindings/pinctrl/nrf-pinctrl.h>
|
||||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 6)>;
|
||||
};
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 0, 8)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 6)>,
|
||||
<NRF_PSEL(UART_RX, 0, 8)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI1 for SX1262 */
|
||||
spi1_default: spi1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 26)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 27)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 28)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1_sleep: spi1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 26)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 27)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 28)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
96
boards/heltec/heltec_meshpocket/heltec_meshpocket.dts
Normal file
96
boards/heltec/heltec_meshpocket/heltec_meshpocket.dts
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec MeshPocket — Device Tree Source
|
||||
* SoC: nRF52840-QIAA
|
||||
* Radio: SX1262 (crystal oscillator, no TCXO)
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include "heltec_meshpocket-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Heltec MeshPocket";
|
||||
compatible = "heltec,meshpocket";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,console = &uart0;
|
||||
zephyr,shell-uart = &uart0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-1 = <&uart0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi1_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1262";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
|
||||
busy-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
|
||||
dio1-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
|
||||
dio2-tx-enable;
|
||||
};
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x0000C000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x00067000>;
|
||||
};
|
||||
slot1_partition: partition@73000 {
|
||||
label = "image-1";
|
||||
reg = <0x00073000 0x00067000>;
|
||||
};
|
||||
storage_partition: partition@da000 {
|
||||
label = "storage";
|
||||
reg = <0x000da000 0x00006000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
CONFIG_SOC_SERIES_NRF52X=y
|
||||
CONFIG_BOARD_HELTEC_MESHPOCKET=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_GPIO=y
|
||||
6
boards/heltec/heltec_t114/Kconfig.defconfig
Normal file
6
boards/heltec/heltec_t114/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_HELTEC_T114
|
||||
|
||||
config BOARD
|
||||
default "heltec_t114"
|
||||
|
||||
endif # BOARD_HELTEC_T114
|
||||
2
boards/heltec/heltec_t114/Kconfig.heltec_t114
Normal file
2
boards/heltec/heltec_t114/Kconfig.heltec_t114
Normal file
@@ -0,0 +1,2 @@
|
||||
config BOARD_HELTEC_T114
|
||||
select SOC_NRF52840_QIAA
|
||||
7
boards/heltec/heltec_t114/board.cmake
Normal file
7
boards/heltec/heltec_t114/board.cmake
Normal file
@@ -0,0 +1,7 @@
|
||||
# Heltec T114 — board.cmake
|
||||
# Uses nrfjprog or pyocd for flashing via SWD.
|
||||
# The board exposes a SWD header for programming.
|
||||
board_runner_args(nrfjprog "--nrf-family=NRF52")
|
||||
board_runner_args(pyocd "--target=nrf52840")
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
7
boards/heltec/heltec_t114/board.yml
Normal file
7
boards/heltec/heltec_t114/board.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
board:
|
||||
name: heltec_t114
|
||||
full_name: "Heltec Mesh Node T114"
|
||||
vendor: heltec
|
||||
url: https://heltec.org/project/mesh-node-t114/
|
||||
socs:
|
||||
- name: nrf52840
|
||||
46
boards/heltec/heltec_t114/heltec_t114-pinctrl.dtsi
Normal file
46
boards/heltec/heltec_t114/heltec_t114-pinctrl.dtsi
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec T114 — pin control definitions
|
||||
*/
|
||||
|
||||
#include <nordic/nrf52840.dtsi>
|
||||
#include <dt-bindings/pinctrl/nrf-pinctrl.h>
|
||||
|
||||
&pinctrl {
|
||||
uart1_default: uart1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 10)>;
|
||||
};
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 0, 9)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
uart1_sleep: uart1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 10)>,
|
||||
<NRF_PSEL(UART_RX, 0, 9)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI1 for LR1110 */
|
||||
spi1_default: spi1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 19)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 22)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 23)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1_sleep: spi1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 19)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 22)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 23)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
115
boards/heltec/heltec_t114/heltec_t114.dts
Normal file
115
boards/heltec/heltec_t114/heltec_t114.dts
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec Mesh Node T114 — Device Tree Source
|
||||
* SoC: nRF52840 QIAAVariant
|
||||
* Radio: SX1262
|
||||
*
|
||||
* NOTE: GPIO pin numbers are placeholders. Verify against the T114
|
||||
* schematic/documentation before using with real hardware.
|
||||
* The T114 uses the nRF52840 SoC in a similar form factor to the
|
||||
* RAK4631, but with different GPIO assignments.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include "heltec_t114-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Heltec Mesh Node T114";
|
||||
compatible = "heltec,mesh-node-t114";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
led0 = &led0;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led0: led_0 {
|
||||
/* TODO: verify LED GPIO from T114 schematic */
|
||||
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
label = "LED 0";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart1 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart1_default>;
|
||||
pinctrl-1 = <&uart1_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi1_default>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1262";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
reset-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
|
||||
busy-gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>;
|
||||
dio1-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
|
||||
dio2-tx-enable;
|
||||
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_3V3>;
|
||||
tcxo-power-startup-delay-ms = <5>;
|
||||
};
|
||||
};
|
||||
|
||||
zephyr_udc0: &usbd {
|
||||
compatible = "nordic,nrf-usbd";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x0000C000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x00067000>;
|
||||
};
|
||||
slot1_partition: partition@73000 {
|
||||
label = "image-1";
|
||||
reg = <0x00073000 0x00067000>;
|
||||
};
|
||||
storage_partition: partition@da000 {
|
||||
label = "storage";
|
||||
reg = <0x000da000 0x00006000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
8
boards/heltec/heltec_t114/heltec_t114_defconfig
Normal file
8
boards/heltec/heltec_t114/heltec_t114_defconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_HELTEC_T114=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_NRFX_POWER=y
|
||||
6
boards/heltec/heltec_wifi_lora32_v3/Kconfig.defconfig
Normal file
6
boards/heltec/heltec_wifi_lora32_v3/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_HELTEC_WIFI_LORA32_V3
|
||||
|
||||
config BOARD
|
||||
default "heltec_wifi_lora32_v3"
|
||||
|
||||
endif # BOARD_HELTEC_WIFI_LORA32_V3
|
||||
@@ -0,0 +1,3 @@
|
||||
config BOARD_HELTEC_WIFI_LORA32_V3
|
||||
select SOC_ESP32S3_PROCPU if BOARD_HELTEC_WIFI_LORA32_V3_PROCPU
|
||||
select SOC_ESP32S3_APPCPU if BOARD_HELTEC_WIFI_LORA32_V3_APPCPU
|
||||
3
boards/heltec/heltec_wifi_lora32_v3/board.cmake
Normal file
3
boards/heltec/heltec_wifi_lora32_v3/board.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
# Heltec WiFi LoRa 32 V3 — board.cmake
|
||||
# Uses esptool for flashing over CP2102 USB-UART bridge
|
||||
include(${ZEPHYR_BASE}/boards/espressif/common/board.cmake)
|
||||
10
boards/heltec/heltec_wifi_lora32_v3/board.yml
Normal file
10
boards/heltec/heltec_wifi_lora32_v3/board.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
board:
|
||||
name: heltec_wifi_lora32_v3
|
||||
full_name: "Heltec WiFi LoRa 32 V3"
|
||||
vendor: heltec
|
||||
url: https://heltec.org/project/wifi-lora-32-v3/
|
||||
socs:
|
||||
- name: esp32s3
|
||||
variants:
|
||||
- name: procpu
|
||||
- name: appcpu
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec WiFi LoRa 32 V3 — pin control definitions
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/pinctrl/esp-pinctrl-common.h>
|
||||
#include <dt-bindings/pinctrl/esp32s3-pinctrl.h>
|
||||
#include <zephyr/dt-bindings/pinctrl/esp32s3-gpio-sigmap.h>
|
||||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
pinmux = <UART0_TX_GPIO43>;
|
||||
output-enable;
|
||||
};
|
||||
group2 {
|
||||
pinmux = <UART0_RX_GPIO44>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
spim2_default: spim2_default {
|
||||
group1 {
|
||||
pinmux = <SPIM2_MISO_GPIO11>,
|
||||
<SPIM2_SCLK_GPIO9>,
|
||||
<SPIM2_CSEL_GPIO8>;
|
||||
};
|
||||
group2 {
|
||||
pinmux = <SPIM2_MOSI_GPIO10>;
|
||||
output-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec WiFi LoRa 32 V3 — Device Tree Source (PROCPU)
|
||||
*
|
||||
* SoC: ESP32-S3
|
||||
* Radio: SX1262
|
||||
* Console: UART0 via CP2102 USB-UART bridge (GPIO43=TX, GPIO44=RX)
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <espressif/esp32s3/esp32s3_fn8r2.dtsi>
|
||||
#include "heltec_wifi_lora32_v3-pinctrl.dtsi"
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
model = "Heltec WiFi LoRa 32 V3 PROCPU";
|
||||
compatible = "heltec,wifi-lora-32-v3";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,console = &uart0;
|
||||
zephyr,shell-uart = &uart0;
|
||||
zephyr,flash-controller = &flash;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
led0 = &led_white;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led_white: led_0 {
|
||||
gpios = <&gpio0 35 GPIO_ACTIVE_HIGH>;
|
||||
label = "White LED";
|
||||
};
|
||||
};
|
||||
|
||||
buttons {
|
||||
compatible = "gpio-keys";
|
||||
boot_button: button_0 {
|
||||
gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
label = "Boot Button";
|
||||
zephyr,code = <INPUT_KEY_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&spi2 {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pinctrl-0 = <&spim2_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1262";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4000000>;
|
||||
reset-gpios = <&gpio0 12 (GPIO_OPEN_DRAIN | GPIO_ACTIVE_LOW)>;
|
||||
busy-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
||||
dio1-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
|
||||
dio2-tx-enable;
|
||||
tcxo-power-startup-delay-ms = <5>;
|
||||
};
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
status = "okay";
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x00010000>;
|
||||
};
|
||||
slot0_partition: partition@10000 {
|
||||
label = "image-0";
|
||||
reg = <0x00010000 0x00300000>;
|
||||
};
|
||||
slot1_partition: partition@310000 {
|
||||
label = "image-1";
|
||||
reg = <0x00310000 0x00300000>;
|
||||
};
|
||||
storage_partition: partition@610000 {
|
||||
label = "storage";
|
||||
reg = <0x00610000 0x001f0000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
CONFIG_SOC_SERIES_ESP32S3=y
|
||||
CONFIG_BOARD_HELTEC_WIFI_LORA32_V3=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_GPIO=y
|
||||
6
boards/heltec/heltec_wifi_lora32_v4/Kconfig.defconfig
Normal file
6
boards/heltec/heltec_wifi_lora32_v4/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_HELTEC_WIFI_LORA32_V4
|
||||
|
||||
config BOARD
|
||||
default "heltec_wifi_lora32_v4"
|
||||
|
||||
endif # BOARD_HELTEC_WIFI_LORA32_V4
|
||||
@@ -0,0 +1,3 @@
|
||||
config BOARD_HELTEC_WIFI_LORA32_V4
|
||||
select SOC_ESP32S3_PROCPU if BOARD_HELTEC_WIFI_LORA32_V4_PROCPU
|
||||
select SOC_ESP32S3_APPCPU if BOARD_HELTEC_WIFI_LORA32_V4_APPCPU
|
||||
3
boards/heltec/heltec_wifi_lora32_v4/board.cmake
Normal file
3
boards/heltec/heltec_wifi_lora32_v4/board.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
# Heltec WiFi LoRa 32 V4 — board.cmake
|
||||
# Uses esptool for flashing over USB-C (direct ESP32-S3 USB)
|
||||
include(${ZEPHYR_BASE}/boards/espressif/common/board.cmake)
|
||||
10
boards/heltec/heltec_wifi_lora32_v4/board.yml
Normal file
10
boards/heltec/heltec_wifi_lora32_v4/board.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
board:
|
||||
name: heltec_wifi_lora32_v4
|
||||
full_name: "Heltec WiFi LoRa 32 V4"
|
||||
vendor: heltec
|
||||
url: https://heltec.org/project/wifi-lora-32-v4/
|
||||
socs:
|
||||
- name: esp32s3
|
||||
variants:
|
||||
- name: procpu
|
||||
- name: appcpu
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec WiFi LoRa 32 V4 — pin control definitions
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/pinctrl/esp-pinctrl-common.h>
|
||||
#include <dt-bindings/pinctrl/esp32s3-pinctrl.h>
|
||||
#include <zephyr/dt-bindings/pinctrl/esp32s3-gpio-sigmap.h>
|
||||
|
||||
&pinctrl {
|
||||
spim2_default: spim2_default {
|
||||
group1 {
|
||||
pinmux = <SPIM2_MISO_GPIO11>,
|
||||
<SPIM2_SCLK_GPIO9>,
|
||||
<SPIM2_CSEL_GPIO8>;
|
||||
};
|
||||
group2 {
|
||||
pinmux = <SPIM2_MOSI_GPIO10>;
|
||||
output-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Heltec WiFi LoRa 32 V4 — Device Tree Source (PROCPU)
|
||||
*
|
||||
* Based on Heltec WiFi LoRa 32 V3 with the following differences:
|
||||
* - No CP2102 USB-UART bridge; direct ESP32-S3 USB-C
|
||||
* - Console routed to usb_serial instead of uart0
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <espressif/esp32s3/esp32s3_fn8r2.dtsi>
|
||||
#include "heltec_wifi_lora32_v4-pinctrl.dtsi"
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
model = "Heltec WiFi LoRa 32 V4 PROCPU";
|
||||
compatible = "heltec,wifi-lora-32-v4";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,console = &usb_serial;
|
||||
zephyr,shell-uart = &usb_serial;
|
||||
zephyr,flash-controller = &flash;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
led0 = &led_white;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led_white: led_0 {
|
||||
/* V4 LED pin — verify from schematic */
|
||||
gpios = <&gpio0 35 GPIO_ACTIVE_HIGH>;
|
||||
label = "White LED";
|
||||
};
|
||||
};
|
||||
|
||||
buttons {
|
||||
compatible = "gpio-keys";
|
||||
boot_button: button_0 {
|
||||
gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
label = "Boot Button";
|
||||
zephyr,code = <INPUT_KEY_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&usb_serial {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&spi2 {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pinctrl-0 = <&spim2_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1262";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4000000>;
|
||||
reset-gpios = <&gpio0 12 (GPIO_OPEN_DRAIN | GPIO_ACTIVE_LOW)>;
|
||||
busy-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
||||
dio1-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
|
||||
dio2-tx-enable;
|
||||
tcxo-power-startup-delay-ms = <5>;
|
||||
};
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
status = "okay";
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x00010000>;
|
||||
};
|
||||
slot0_partition: partition@10000 {
|
||||
label = "image-0";
|
||||
reg = <0x00010000 0x00300000>;
|
||||
};
|
||||
slot1_partition: partition@310000 {
|
||||
label = "image-1";
|
||||
reg = <0x00310000 0x00300000>;
|
||||
};
|
||||
storage_partition: partition@610000 {
|
||||
label = "storage";
|
||||
reg = <0x00610000 0x001f0000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
CONFIG_SOC_SERIES_ESP32S3=y
|
||||
CONFIG_BOARD_HELTEC_WIFI_LORA32_V4=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_GPIO=y
|
||||
10
boards/heltec_t114.conf
Normal file
10
boards/heltec_t114.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# Heltec T114 (Mesh Node T114) — nRF52840 + SX1262
|
||||
# Uses USB CDC-ACM
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# SX1262 driver
|
||||
CONFIG_LORA_SX12XX=y
|
||||
17
boards/heltec_t114.overlay
Normal file
17
boards/heltec_t114.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Heltec T114 (Mesh Node T114) — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: SX1262 on SPI1 (defined in custom board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
7
boards/heltec_wifi_lora32_v2.conf
Normal file
7
boards/heltec_wifi_lora32_v2.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
# Heltec WiFi LoRa 32 V2 — ESP32 + SX1276
|
||||
# Uses hardware UART over CP2102 USB-Serial bridge
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_UART=y
|
||||
|
||||
# SX1276 driver (via loramac-node)
|
||||
CONFIG_LORA_SX12XX=y
|
||||
11
boards/heltec_wifi_lora32_v2.overlay
Normal file
11
boards/heltec_wifi_lora32_v2.overlay
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Heltec WiFi LoRa 32 V2 — application DTS overlay
|
||||
* KISS interface: UART0 (via CP2102 USB-Serial bridge)
|
||||
* LoRa: SX1276 on SPI2 (already defined in upstream board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &uart0;
|
||||
};
|
||||
};
|
||||
10
boards/heltec_wifi_lora32_v3_procpu.conf
Normal file
10
boards/heltec_wifi_lora32_v3_procpu.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# Heltec WiFi LoRa 32 V3 (procpu) — ESP32-S3 + SX1262
|
||||
# Uses USB CDC-ACM (direct ESP32-S3 USB, no CP2102)
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# SX1262 driver
|
||||
CONFIG_LORA_SX12XX=y
|
||||
17
boards/heltec_wifi_lora32_v3_procpu.overlay
Normal file
17
boards/heltec_wifi_lora32_v3_procpu.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Heltec WiFi LoRa 32 V3 (procpu) — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: SX1262 on SPI2 (already defined in upstream board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
10
boards/heltec_wifi_lora32_v4_procpu.conf
Normal file
10
boards/heltec_wifi_lora32_v4_procpu.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# Heltec WiFi LoRa 32 V4 (procpu) — ESP32-S3 + SX1262
|
||||
# Uses USB CDC-ACM (direct USB-C, no CP2102)
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# SX1262 driver
|
||||
CONFIG_LORA_SX12XX=y
|
||||
17
boards/heltec_wifi_lora32_v4_procpu.overlay
Normal file
17
boards/heltec_wifi_lora32_v4_procpu.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Heltec WiFi LoRa 32 V4 (procpu) — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: SX1262 on SPI2 (defined in custom board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
6
boards/lilygo/lilygo_tbeam/Kconfig.defconfig
Normal file
6
boards/lilygo/lilygo_tbeam/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_LILYGO_TBEAM
|
||||
|
||||
config BOARD
|
||||
default "lilygo_tbeam"
|
||||
|
||||
endif # BOARD_LILYGO_TBEAM
|
||||
3
boards/lilygo/lilygo_tbeam/Kconfig.lilygo_tbeam
Normal file
3
boards/lilygo/lilygo_tbeam/Kconfig.lilygo_tbeam
Normal file
@@ -0,0 +1,3 @@
|
||||
config BOARD_LILYGO_TBEAM
|
||||
select SOC_ESP32_PROCPU if BOARD_LILYGO_TBEAM_PROCPU
|
||||
select SOC_ESP32_APPCPU if BOARD_LILYGO_TBEAM_APPCPU
|
||||
3
boards/lilygo/lilygo_tbeam/board.cmake
Normal file
3
boards/lilygo/lilygo_tbeam/board.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
# LilyGo T-Beam — board.cmake
|
||||
# Uses esptool for flashing over USB-to-serial (CP2104)
|
||||
include(${ZEPHYR_BASE}/boards/espressif/common/board.cmake)
|
||||
10
boards/lilygo/lilygo_tbeam/board.yml
Normal file
10
boards/lilygo/lilygo_tbeam/board.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
board:
|
||||
name: lilygo_tbeam
|
||||
full_name: "LilyGo T-Beam"
|
||||
vendor: lilygo
|
||||
url: https://www.lilygo.cc/products/t-beam-v1-1
|
||||
socs:
|
||||
- name: esp32
|
||||
variants:
|
||||
- name: procpu
|
||||
- name: appcpu
|
||||
36
boards/lilygo/lilygo_tbeam/lilygo_tbeam-pinctrl.dtsi
Normal file
36
boards/lilygo/lilygo_tbeam/lilygo_tbeam-pinctrl.dtsi
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* LilyGo T-Beam V1.1 — pin control definitions
|
||||
* SoC: ESP32-D0WD
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/pinctrl/esp-pinctrl-common.h>
|
||||
#include <dt-bindings/pinctrl/esp32-pinctrl.h>
|
||||
#include <zephyr/dt-bindings/pinctrl/esp32-gpio-sigmap.h>
|
||||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
pinmux = <UART0_TX_GPIO1>;
|
||||
output-enable;
|
||||
};
|
||||
group2 {
|
||||
pinmux = <UART0_RX_GPIO3>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI3 (VSPI) for SX1276/SX1278 */
|
||||
spim3_default: spim3_default {
|
||||
group1 {
|
||||
pinmux = <SPIM3_MISO_GPIO19>,
|
||||
<SPIM3_SCLK_GPIO5>,
|
||||
<SPIM3_CSEL_GPIO18>;
|
||||
};
|
||||
group2 {
|
||||
pinmux = <SPIM3_MOSI_GPIO27>;
|
||||
output-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
102
boards/lilygo/lilygo_tbeam/lilygo_tbeam_procpu.dts
Normal file
102
boards/lilygo/lilygo_tbeam/lilygo_tbeam_procpu.dts
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* LilyGo T-Beam V1.1 — Device Tree Source (PROCPU)
|
||||
*
|
||||
* SoC: ESP32-D0WD
|
||||
* Radio: SX1276 / SX1278 (LORA32 module)
|
||||
* Flash: 4 MB
|
||||
*
|
||||
* LoRa: MOSI=27 MISO=19 SCK=5 NSS=18 RST=23 DIO0=26
|
||||
* UART0: TX=1 RX=3 (via CP2104 USB-serial)
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <espressif/esp32/esp32_d0wd_v3.dtsi>
|
||||
#include "lilygo_tbeam-pinctrl.dtsi"
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
model = "LilyGo T-Beam V1.1 PROCPU";
|
||||
compatible = "lilygo,t-beam";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,console = &uart0;
|
||||
zephyr,shell-uart = &uart0;
|
||||
zephyr,flash-controller = &flash;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
};
|
||||
|
||||
buttons {
|
||||
compatible = "gpio-keys";
|
||||
boot_button: button_0 {
|
||||
gpios = <&gpio0 38 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
label = "Boot Button";
|
||||
zephyr,code = <INPUT_KEY_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&spi3 {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pinctrl-0 = <&spim3_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1276";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <8000000>;
|
||||
reset-gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
|
||||
dio1-gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>;
|
||||
power-amplifier-output = "pa-boost";
|
||||
};
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
status = "okay";
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x00010000>;
|
||||
};
|
||||
slot0_partition: partition@10000 {
|
||||
label = "image-0";
|
||||
reg = <0x00010000 0x00180000>;
|
||||
};
|
||||
slot1_partition: partition@190000 {
|
||||
label = "image-1";
|
||||
reg = <0x00190000 0x00180000>;
|
||||
};
|
||||
storage_partition: partition@310000 {
|
||||
label = "storage";
|
||||
reg = <0x00310000 0x000F0000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
7
boards/lilygo/lilygo_tbeam/lilygo_tbeam_procpu_defconfig
Normal file
7
boards/lilygo/lilygo_tbeam/lilygo_tbeam_procpu_defconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
CONFIG_SOC_SERIES_ESP32=y
|
||||
CONFIG_BOARD_LILYGO_TBEAM=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_GPIO=y
|
||||
6
boards/rak/rak_wismesh_pocket/Kconfig.defconfig
Normal file
6
boards/rak/rak_wismesh_pocket/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_RAK_WISMESH_POCKET
|
||||
|
||||
config BOARD
|
||||
default "rak_wismesh_pocket"
|
||||
|
||||
endif # BOARD_RAK_WISMESH_POCKET
|
||||
2
boards/rak/rak_wismesh_pocket/Kconfig.rak_wismesh_pocket
Normal file
2
boards/rak/rak_wismesh_pocket/Kconfig.rak_wismesh_pocket
Normal file
@@ -0,0 +1,2 @@
|
||||
config BOARD_RAK_WISMESH_POCKET
|
||||
select SOC_NRF52840_QIAA
|
||||
9
boards/rak/rak_wismesh_pocket/board.cmake
Normal file
9
boards/rak/rak_wismesh_pocket/board.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
# RAK WisMesh Pocket — board.cmake
|
||||
# Uses nrfjprog or pyocd for flashing via SWD.
|
||||
# J-Link is the recommended debugger for RAK WisBlock-family boards.
|
||||
board_runner_args(nrfjprog "--nrf-family=NRF52")
|
||||
board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000" "--reset-after-load")
|
||||
board_runner_args(pyocd "--target=nrf52840")
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
7
boards/rak/rak_wismesh_pocket/board.yml
Normal file
7
boards/rak/rak_wismesh_pocket/board.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
board:
|
||||
name: rak_wismesh_pocket
|
||||
full_name: "RAK WisMesh Pocket"
|
||||
vendor: rak
|
||||
url: https://docs.rakwireless.com/Product-Categories/WisMesh/
|
||||
socs:
|
||||
- name: nrf52840
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* RAK WisMesh Pocket — pin control definitions
|
||||
* Matches RAK4631 GPIO assignments. Verify from WisMesh Pocket schematic.
|
||||
*/
|
||||
|
||||
#include <nordic/nrf52840.dtsi>
|
||||
#include <dt-bindings/pinctrl/nrf-pinctrl.h>
|
||||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 19)>;
|
||||
};
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 0, 18)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 19)>,
|
||||
<NRF_PSEL(UART_RX, 0, 18)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI1 for SX1262 — RAK4631 pin assignments */
|
||||
spi1_default: spi1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 1, 3)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 1, 2)>,
|
||||
<NRF_PSEL(SPIM_MISO, 1, 1)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1_sleep: spi1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 1, 3)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 1, 2)>,
|
||||
<NRF_PSEL(SPIM_MISO, 1, 1)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
119
boards/rak/rak_wismesh_pocket/rak_wismesh_pocket_nrf52840.dts
Normal file
119
boards/rak/rak_wismesh_pocket/rak_wismesh_pocket_nrf52840.dts
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* RAK WisMesh Pocket — Device Tree Source
|
||||
* SoC: nRF52840 (same as RAK4631 WisBlock Core)
|
||||
* Radio: SX1262
|
||||
*
|
||||
* The WisMesh Pocket is derived from the RAK4631 with a different
|
||||
* form factor optimized for portable deployment. GPIO assignments
|
||||
* are assumed identical to RAK4631; verify against the WisMesh
|
||||
* Pocket schematic before using with real hardware.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include "rak_wismesh_pocket-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "RAK WisMesh Pocket";
|
||||
compatible = "rak,wismesh-pocket";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
led0 = &led_green;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led_green: led_0 {
|
||||
/* RAK4631 green LED — verify for WisMesh Pocket */
|
||||
gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
|
||||
label = "Green LED";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
/*
|
||||
* SX1262 on SPI1
|
||||
* GPIO assignments match RAK4631 — verify for WisMesh Pocket.
|
||||
*/
|
||||
&spi1 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi1_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1262";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
|
||||
busy-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
|
||||
dio1-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
||||
dio2-tx-enable;
|
||||
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_3V3>;
|
||||
tcxo-power-startup-delay-ms = <5>;
|
||||
};
|
||||
};
|
||||
|
||||
zephyr_udc0: &usbd {
|
||||
compatible = "nordic,nrf-usbd";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x0000C000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x00067000>;
|
||||
};
|
||||
slot1_partition: partition@73000 {
|
||||
label = "image-1";
|
||||
reg = <0x00073000 0x00067000>;
|
||||
};
|
||||
storage_partition: partition@da000 {
|
||||
label = "storage";
|
||||
reg = <0x000da000 0x00006000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_RAK_WISMESH_POCKET=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_NRFX_POWER=y
|
||||
6
boards/rak/rak_wismesh_tag/Kconfig.defconfig
Normal file
6
boards/rak/rak_wismesh_tag/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_RAK_WISMESH_TAG
|
||||
|
||||
config BOARD
|
||||
default "rak_wismesh_tag"
|
||||
|
||||
endif # BOARD_RAK_WISMESH_TAG
|
||||
2
boards/rak/rak_wismesh_tag/Kconfig.rak_wismesh_tag
Normal file
2
boards/rak/rak_wismesh_tag/Kconfig.rak_wismesh_tag
Normal file
@@ -0,0 +1,2 @@
|
||||
config BOARD_RAK_WISMESH_TAG
|
||||
select SOC_NRF52840_QIAA
|
||||
9
boards/rak/rak_wismesh_tag/board.cmake
Normal file
9
boards/rak/rak_wismesh_tag/board.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
# RAK WisMesh Tag — board.cmake
|
||||
# Uses nrfjprog or pyocd for flashing via SWD.
|
||||
# J-Link is the recommended debugger for RAK WisBlock-family boards.
|
||||
board_runner_args(nrfjprog "--nrf-family=NRF52")
|
||||
board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000" "--reset-after-load")
|
||||
board_runner_args(pyocd "--target=nrf52840")
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
7
boards/rak/rak_wismesh_tag/board.yml
Normal file
7
boards/rak/rak_wismesh_tag/board.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
board:
|
||||
name: rak_wismesh_tag
|
||||
full_name: "RAK WisMesh Tag"
|
||||
vendor: rak
|
||||
url: https://docs.rakwireless.com/Product-Categories/WisMesh/
|
||||
socs:
|
||||
- name: nrf52840
|
||||
47
boards/rak/rak_wismesh_tag/rak_wismesh_tag-pinctrl.dtsi
Normal file
47
boards/rak/rak_wismesh_tag/rak_wismesh_tag-pinctrl.dtsi
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* RAK WisMesh Tag — pin control definitions
|
||||
* Matches RAK4631 GPIO assignments. Verify from WisMesh Tag schematic.
|
||||
*/
|
||||
|
||||
#include <nordic/nrf52840.dtsi>
|
||||
#include <dt-bindings/pinctrl/nrf-pinctrl.h>
|
||||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 19)>;
|
||||
};
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 0, 18)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 19)>,
|
||||
<NRF_PSEL(UART_RX, 0, 18)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI1 for SX1262 — RAK4631 pin assignments */
|
||||
spi1_default: spi1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 1, 3)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 1, 2)>,
|
||||
<NRF_PSEL(SPIM_MISO, 1, 1)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1_sleep: spi1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 1, 3)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 1, 2)>,
|
||||
<NRF_PSEL(SPIM_MISO, 1, 1)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
119
boards/rak/rak_wismesh_tag/rak_wismesh_tag_nrf52840.dts
Normal file
119
boards/rak/rak_wismesh_tag/rak_wismesh_tag_nrf52840.dts
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* RAK WisMesh Tag — Device Tree Source
|
||||
* SoC: nRF52840 (same as RAK4631 WisBlock Core)
|
||||
* Radio: SX1262
|
||||
*
|
||||
* The WisMesh Tag is derived from the RAK4631 with a different
|
||||
* form factor. GPIO assignments are assumed identical to RAK4631;
|
||||
* verify against the WisMesh Tag schematic before using with
|
||||
* real hardware.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include "rak_wismesh_tag-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "RAK WisMesh Tag";
|
||||
compatible = "rak,wismesh-tag";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
led0 = &led_green;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led_green: led_0 {
|
||||
/* RAK4631 green LED — verify for WisMesh Tag */
|
||||
gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
|
||||
label = "Green LED";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
/*
|
||||
* SX1262 on SPI1
|
||||
* GPIO assignments match RAK4631 — verify for WisMesh Tag.
|
||||
*/
|
||||
&spi1 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi1_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,sx1262";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
|
||||
busy-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
|
||||
dio1-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
||||
dio2-tx-enable;
|
||||
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_3V3>;
|
||||
tcxo-power-startup-delay-ms = <5>;
|
||||
};
|
||||
};
|
||||
|
||||
zephyr_udc0: &usbd {
|
||||
compatible = "nordic,nrf-usbd";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x0000C000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x00067000>;
|
||||
};
|
||||
slot1_partition: partition@73000 {
|
||||
label = "image-1";
|
||||
reg = <0x00073000 0x00067000>;
|
||||
};
|
||||
storage_partition: partition@da000 {
|
||||
label = "storage";
|
||||
reg = <0x000da000 0x00006000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_RAK_WISMESH_TAG=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_NRFX_POWER=y
|
||||
10
boards/rak4631_nrf52840.conf
Normal file
10
boards/rak4631_nrf52840.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# RAK WisBlock (RAK4631) — nRF52840 + SX1262
|
||||
# Uses USB CDC-ACM
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# SX1262 driver
|
||||
CONFIG_LORA_SX12XX=y
|
||||
17
boards/rak4631_nrf52840.overlay
Normal file
17
boards/rak4631_nrf52840.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* RAK WisBlock (RAK4631 / nRF52840) — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: SX1262 on SPI1 (already defined in upstream board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
10
boards/rak_wismesh_pocket.conf
Normal file
10
boards/rak_wismesh_pocket.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# RAK WisMesh Pocket — nRF52840 + SX1262
|
||||
# Uses USB CDC-ACM
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# SX1262 driver
|
||||
CONFIG_LORA_SX12XX=y
|
||||
17
boards/rak_wismesh_pocket.overlay
Normal file
17
boards/rak_wismesh_pocket.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* RAK WisMesh Pocket — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: SX1262 on SPI1 (defined in custom board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
10
boards/rak_wismesh_tag.conf
Normal file
10
boards/rak_wismesh_tag.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# RAK WisMesh Tag — nRF52840 + SX1262
|
||||
# Uses USB CDC-ACM
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# SX1262 driver
|
||||
CONFIG_LORA_SX12XX=y
|
||||
17
boards/rak_wismesh_tag.overlay
Normal file
17
boards/rak_wismesh_tag.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* RAK WisMesh Tag — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: SX1262 on SPI1 (defined in custom board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
6
boards/seeed/seeed_wio_tracker/Kconfig.defconfig
Normal file
6
boards/seeed/seeed_wio_tracker/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_SEEED_WIO_TRACKER
|
||||
|
||||
config BOARD
|
||||
default "seeed_wio_tracker"
|
||||
|
||||
endif # BOARD_SEEED_WIO_TRACKER
|
||||
2
boards/seeed/seeed_wio_tracker/Kconfig.seeed_wio_tracker
Normal file
2
boards/seeed/seeed_wio_tracker/Kconfig.seeed_wio_tracker
Normal file
@@ -0,0 +1,2 @@
|
||||
config BOARD_SEEED_WIO_TRACKER
|
||||
select SOC_NRF52840_QIAA
|
||||
6
boards/seeed/seeed_wio_tracker/board.cmake
Normal file
6
boards/seeed/seeed_wio_tracker/board.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
# Seeed Wio Tracker 1110 — board.cmake
|
||||
# Uses nrfjprog or pyocd for flashing via SWD.
|
||||
board_runner_args(nrfjprog "--nrf-family=NRF52")
|
||||
board_runner_args(pyocd "--target=nrf52840")
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
7
boards/seeed/seeed_wio_tracker/board.yml
Normal file
7
boards/seeed/seeed_wio_tracker/board.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
board:
|
||||
name: seeed_wio_tracker
|
||||
full_name: "Seeed Wio Tracker 1110"
|
||||
vendor: seeed
|
||||
url: https://wiki.seeedstudio.com/wio_tracker_1110_intro/
|
||||
socs:
|
||||
- name: nrf52840
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Seeed Wio Tracker 1110 — pin control definitions
|
||||
* NOTE: GPIO numbers are placeholders. Verify from schematic.
|
||||
*/
|
||||
|
||||
#include <nordic/nrf52840.dtsi>
|
||||
#include <dt-bindings/pinctrl/nrf-pinctrl.h>
|
||||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>;
|
||||
};
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 0, 24)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>,
|
||||
<NRF_PSEL(UART_RX, 0, 24)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI1 for LR1110 — verify pins from Wio Tracker 1110 schematic */
|
||||
spi1_default: spi1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 21)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 22)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 23)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1_sleep: spi1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 21)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 22)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 23)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
117
boards/seeed/seeed_wio_tracker/seeed_wio_tracker.dts
Normal file
117
boards/seeed/seeed_wio_tracker/seeed_wio_tracker.dts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Seeed Wio Tracker 1110 — Device Tree Source
|
||||
* SoC: nRF52840
|
||||
* Radio: Semtech LR1110 (LoRa + GNSS multi-protocol transceiver)
|
||||
*
|
||||
* NOTE: GPIO pin numbers are placeholders. Verify against the
|
||||
* Wio Tracker 1110 schematic before using with real hardware.
|
||||
* The LR1110 SPI interface differs from SX126x: it uses 2-byte
|
||||
* opcodes and requires the BUSY pin to be checked between transactions.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include "seeed_wio_tracker-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Seeed Wio Tracker 1110";
|
||||
compatible = "seeed,wio-tracker-1110";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
led0 = &led_green;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led_green: led_0 {
|
||||
/* TODO: verify LED GPIO from Wio Tracker 1110 schematic */
|
||||
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
label = "Green LED";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
/*
|
||||
* LR1110 on SPI1
|
||||
* Pin assignments are placeholders — must be verified from schematic.
|
||||
* The LR1110 BUSY and EVENT (DIO9) pins are both required by the driver.
|
||||
*/
|
||||
&spi1 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi1_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,lr11xx";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4000000>;
|
||||
reset-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
|
||||
busy-gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>;
|
||||
event-gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
zephyr_udc0: &usbd {
|
||||
compatible = "nordic,nrf-usbd";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x0000C000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x00067000>;
|
||||
};
|
||||
slot1_partition: partition@73000 {
|
||||
label = "image-1";
|
||||
reg = <0x00073000 0x00067000>;
|
||||
};
|
||||
storage_partition: partition@da000 {
|
||||
label = "storage";
|
||||
reg = <0x000da000 0x00006000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_SEEED_WIO_TRACKER=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_NRFX_POWER=y
|
||||
10
boards/seeed_wio_tracker.conf
Normal file
10
boards/seeed_wio_tracker.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# Seeed Wio Tracker 1110 — nRF52840 + LR1110
|
||||
# Uses USB CDC-ACM
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# LR11xx driver (out-of-tree)
|
||||
CONFIG_LORA_LR11XX=y
|
||||
17
boards/seeed_wio_tracker.overlay
Normal file
17
boards/seeed_wio_tracker.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Seeed Wio Tracker 1110 — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: LR1110 on SPI1 (defined in custom board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
6
boards/sensecap/sensecap_t1000e/Kconfig.defconfig
Normal file
6
boards/sensecap/sensecap_t1000e/Kconfig.defconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
if BOARD_SENSECAP_T1000E
|
||||
|
||||
config BOARD
|
||||
default "sensecap_t1000e"
|
||||
|
||||
endif # BOARD_SENSECAP_T1000E
|
||||
2
boards/sensecap/sensecap_t1000e/Kconfig.sensecap_t1000e
Normal file
2
boards/sensecap/sensecap_t1000e/Kconfig.sensecap_t1000e
Normal file
@@ -0,0 +1,2 @@
|
||||
config BOARD_SENSECAP_T1000E
|
||||
select SOC_NRF52840_QIAA
|
||||
6
boards/sensecap/sensecap_t1000e/board.cmake
Normal file
6
boards/sensecap/sensecap_t1000e/board.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
# SenseCAP T1000-E — board.cmake
|
||||
# Uses nrfjprog or pyocd for flashing via SWD.
|
||||
board_runner_args(nrfjprog "--nrf-family=NRF52")
|
||||
board_runner_args(pyocd "--target=nrf52840")
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
7
boards/sensecap/sensecap_t1000e/board.yml
Normal file
7
boards/sensecap/sensecap_t1000e/board.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
board:
|
||||
name: sensecap_t1000e
|
||||
full_name: "SenseCAP T1000-E"
|
||||
vendor: sensecap
|
||||
url: https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-p-5913.html
|
||||
socs:
|
||||
- name: nrf52840
|
||||
47
boards/sensecap/sensecap_t1000e/sensecap_t1000e-pinctrl.dtsi
Normal file
47
boards/sensecap/sensecap_t1000e/sensecap_t1000e-pinctrl.dtsi
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* SenseCAP T1000-E — pin control definitions
|
||||
* NOTE: GPIO numbers are placeholders. Verify from schematic.
|
||||
*/
|
||||
|
||||
#include <nordic/nrf52840.dtsi>
|
||||
#include <dt-bindings/pinctrl/nrf-pinctrl.h>
|
||||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>;
|
||||
};
|
||||
group2 {
|
||||
psels = <NRF_PSEL(UART_RX, 0, 24)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>,
|
||||
<NRF_PSEL(UART_RX, 0, 24)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* SPI1 for LR1110 — verify pins from T1000-E schematic */
|
||||
spi1_default: spi1_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 17)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 18)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 19)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi1_sleep: spi1_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_SCK, 0, 17)>,
|
||||
<NRF_PSEL(SPIM_MOSI, 0, 18)>,
|
||||
<NRF_PSEL(SPIM_MISO, 0, 19)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
116
boards/sensecap/sensecap_t1000e/sensecap_t1000e.dts
Normal file
116
boards/sensecap/sensecap_t1000e/sensecap_t1000e.dts
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* SenseCAP T1000-E — Device Tree Source
|
||||
* SoC: nRF52840
|
||||
* Radio: Semtech LR1110 (LoRa + GNSS multi-protocol transceiver)
|
||||
*
|
||||
* NOTE: GPIO pin numbers are placeholders. Verify against the
|
||||
* SenseCAP T1000-E schematic before using with real hardware.
|
||||
* The T1000-E is a different product from the Seeed Wio Tracker 1110,
|
||||
* despite sharing the same SoC and radio chip.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include "sensecap_t1000e-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "SenseCAP T1000-E";
|
||||
compatible = "sensecap,t1000e";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
};
|
||||
|
||||
aliases {
|
||||
lora0 = &lora;
|
||||
led0 = &led_green;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led_green: led_0 {
|
||||
/* TODO: verify LED GPIO from T1000-E schematic */
|
||||
gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
|
||||
label = "Green LED";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
/*
|
||||
* LR1110 on SPI1
|
||||
* Pin assignments are placeholders — must be verified from schematic.
|
||||
*/
|
||||
&spi1 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi1_default>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,lr11xx";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4000000>;
|
||||
reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
|
||||
busy-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
|
||||
event-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
zephyr_udc0: &usbd {
|
||||
compatible = "nordic,nrf-usbd";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x0000C000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x00067000>;
|
||||
};
|
||||
slot1_partition: partition@73000 {
|
||||
label = "image-1";
|
||||
reg = <0x00073000 0x00067000>;
|
||||
};
|
||||
storage_partition: partition@da000 {
|
||||
label = "storage";
|
||||
reg = <0x000da000 0x00006000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_SENSECAP_T1000E=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_NRFX_POWER=y
|
||||
10
boards/sensecap_t1000e.conf
Normal file
10
boards/sensecap_t1000e.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
# SenseCAP T1000-E — nRF52840 + LR1110
|
||||
# Uses USB CDC-ACM
|
||||
|
||||
CONFIG_LORAMODEM_KISS_IFACE_USB=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_CDC_ACM=y
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
|
||||
# LR11xx driver (out-of-tree)
|
||||
CONFIG_LORA_LR11XX=y
|
||||
17
boards/sensecap_t1000e.overlay
Normal file
17
boards/sensecap_t1000e.overlay
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SenseCAP T1000-E — application DTS overlay
|
||||
* KISS interface: USB CDC-ACM
|
||||
* LoRa: LR1110 on SPI1 (defined in custom board DTS)
|
||||
*/
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
loramodem,kiss-uart = &cdc_acm_uart0;
|
||||
};
|
||||
};
|
||||
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
};
|
||||
};
|
||||
11
drivers/lora/lr11xx/CMakeLists.txt
Normal file
11
drivers/lora/lr11xx/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2024 loramodem contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_LORA_LR11XX
|
||||
lr11xx.c
|
||||
lr11xx_hal.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(.)
|
||||
16
drivers/lora/lr11xx/Kconfig
Normal file
16
drivers/lora/lr11xx/Kconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2024 loramodem contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config LORA_LR11XX
|
||||
bool "Semtech LR11xx LoRa driver"
|
||||
default y
|
||||
depends on DT_HAS_SEMTECH_LR11XX_ENABLED
|
||||
depends on SPI
|
||||
select LORA
|
||||
help
|
||||
Enable driver for Semtech LR1110/LR1120 multi-protocol radio
|
||||
transceivers. This driver implements Zephyr's LoRa subsystem API
|
||||
for LoRa packet mode operation only.
|
||||
|
||||
The LR11xx uses a 2-byte SPI opcode protocol. BUSY pin must be
|
||||
polled between transactions.
|
||||
479
drivers/lora/lr11xx/lr11xx.c
Normal file
479
drivers/lora/lr11xx/lr11xx.c
Normal file
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Semtech LR11xx LoRa driver for Zephyr RTOS.
|
||||
*
|
||||
* Implements Zephyr's lora_driver_api for LoRa packet mode operation.
|
||||
* Supports: LR1110, LR1120.
|
||||
*
|
||||
* The LR11xx SPI protocol uses 2-byte opcodes and requires BUSY polling
|
||||
* between transactions. See lr11xx.h for opcode definitions.
|
||||
*
|
||||
* Only LoRa packet mode is implemented. LoRaWAN and GNSS functions
|
||||
* of the LR11xx are not used by the KISS modem application.
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT semtech_lr11xx
|
||||
|
||||
#include "lr11xx.h"
|
||||
|
||||
#include <zephyr/drivers/lora.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(lr11xx, CONFIG_LORA_LOG_LEVEL);
|
||||
|
||||
/* ── Helpers to map Zephyr lora_modem_config to LR11xx codes ─────────────── */
|
||||
|
||||
static uint8_t zephyr_sf_to_lr11xx(enum lora_datarate dr)
|
||||
{
|
||||
switch (dr) {
|
||||
case SF_5: return LR11XX_LORA_SF5;
|
||||
case SF_6: return LR11XX_LORA_SF6;
|
||||
case SF_7: return LR11XX_LORA_SF7;
|
||||
case SF_8: return LR11XX_LORA_SF8;
|
||||
case SF_9: return LR11XX_LORA_SF9;
|
||||
case SF_10: return LR11XX_LORA_SF10;
|
||||
case SF_11: return LR11XX_LORA_SF11;
|
||||
case SF_12: /* fall through */
|
||||
default: return LR11XX_LORA_SF12;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t zephyr_bw_to_lr11xx(enum lora_signal_bandwidth bw)
|
||||
{
|
||||
switch (bw) {
|
||||
case BW_500_KHZ: return LR11XX_LORA_BW_500;
|
||||
case BW_250_KHZ: return LR11XX_LORA_BW_250;
|
||||
case BW_125_KHZ: /* fall through */
|
||||
default: return LR11XX_LORA_BW_125;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t zephyr_cr_to_lr11xx(enum lora_coding_rate cr)
|
||||
{
|
||||
switch (cr) {
|
||||
case CR_4_5: return LR11XX_LORA_CR_4_5;
|
||||
case CR_4_6: return LR11XX_LORA_CR_4_6;
|
||||
case CR_4_7: return LR11XX_LORA_CR_4_7;
|
||||
case CR_4_8: /* fall through */
|
||||
default: return LR11XX_LORA_CR_4_8;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Radio configuration ─────────────────────────────────────────────────── */
|
||||
|
||||
static int lr11xx_configure(const struct device *dev,
|
||||
struct lora_modem_config *cfg)
|
||||
{
|
||||
int ret;
|
||||
uint8_t params[8];
|
||||
|
||||
/* Enter standby (RC oscillator) */
|
||||
params[0] = LR11XX_STANDBY_RC;
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_STANDBY, params, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set packet type to LoRa */
|
||||
params[0] = LR11XX_PKT_TYPE_LORA;
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_PACKET_TYPE, params, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set RF frequency */
|
||||
uint32_t freq = (uint32_t)cfg->frequency;
|
||||
|
||||
params[0] = (freq >> 24) & 0xFF;
|
||||
params[1] = (freq >> 16) & 0xFF;
|
||||
params[2] = (freq >> 8) & 0xFF;
|
||||
params[3] = (freq >> 0) & 0xFF;
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_RF_FREQUENCY, params, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set TX power and ramp time (ramp 40 us = 0x04) */
|
||||
params[0] = (uint8_t)cfg->tx_power;
|
||||
params[1] = 0x04; /* Ramp time */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_TX_PARAMS, params, 2);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set modulation parameters (LoRa):
|
||||
* [0] SF, [1] BW, [2] CR, [3] LDRO (low data rate optimize)
|
||||
*/
|
||||
params[0] = zephyr_sf_to_lr11xx(cfg->datarate);
|
||||
params[1] = zephyr_bw_to_lr11xx(cfg->bandwidth);
|
||||
params[2] = zephyr_cr_to_lr11xx(cfg->coding_rate);
|
||||
params[3] = 0x00; /* LDRO: auto would require SF/BW calculation */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_MODULATION_PARAMS,
|
||||
params, 4);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set packet parameters (LoRa):
|
||||
* [0:1] preamble length, [2] header type (0=explicit),
|
||||
* [3] payload length, [4] CRC mode (1=on),
|
||||
* [5] IQ inversion (0=standard)
|
||||
*/
|
||||
uint16_t preamble = (uint16_t)cfg->preamble_len;
|
||||
|
||||
params[0] = (preamble >> 8) & 0xFF;
|
||||
params[1] = (preamble >> 0) & 0xFF;
|
||||
params[2] = 0x00; /* explicit header */
|
||||
params[3] = 0xFF; /* max payload (will be overridden per-packet) */
|
||||
params[4] = 0x01; /* CRC on */
|
||||
params[5] = 0x00; /* standard IQ */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_PACKET_PARAMS,
|
||||
params, 6);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set buffer base addresses (TX=0, RX=0) */
|
||||
params[0] = 0x00;
|
||||
params[1] = 0x00;
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_BUFFER_BASE_ADDR,
|
||||
params, 2);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure DIO IRQ routing to EVENT/DIO9 */
|
||||
uint32_t irq_mask = LR11XX_DIO9_MASK;
|
||||
|
||||
params[0] = (irq_mask >> 24) & 0xFF;
|
||||
params[1] = (irq_mask >> 16) & 0xFF;
|
||||
params[2] = (irq_mask >> 8) & 0xFF;
|
||||
params[3] = (irq_mask >> 0) & 0xFF;
|
||||
/* DIO9 enable mask = same as IRQ mask */
|
||||
params[4] = params[0];
|
||||
params[5] = params[1];
|
||||
params[6] = params[2];
|
||||
params[7] = params[3];
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_DIO_IRQ_PARAMS,
|
||||
params, 8);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ── Send ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
static int lr11xx_send(const struct device *dev,
|
||||
uint8_t *data, uint32_t data_len)
|
||||
{
|
||||
struct lr11xx_data *drv = dev->data;
|
||||
int ret;
|
||||
uint8_t params[3];
|
||||
|
||||
/* Clear pending IRQ flags */
|
||||
uint32_t clr = LR11XX_IRQ_ALL;
|
||||
|
||||
params[0] = (clr >> 24) & 0xFF;
|
||||
params[1] = (clr >> 16) & 0xFF;
|
||||
params[2] = (clr >> 8) & 0xFF;
|
||||
params[3] = (clr >> 0) & 0xFF;
|
||||
lr11xx_hal_write_cmd(dev, LR11XX_CMD_CLEAR_IRQ_STATUS, params, 4);
|
||||
|
||||
/* Write payload to TX buffer at offset 0 */
|
||||
uint8_t *tx_buf = k_malloc(data_len + 1);
|
||||
|
||||
if (!tx_buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
tx_buf[0] = 0x00; /* buffer offset */
|
||||
memcpy(&tx_buf[1], data, data_len);
|
||||
|
||||
/* Use opcode 0x010A (WriteBuffer8) to write payload */
|
||||
ret = lr11xx_hal_write_cmd(dev, 0x010Au, tx_buf, data_len + 1);
|
||||
k_free(tx_buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Update packet params with actual payload length */
|
||||
uint8_t pkt_params[6];
|
||||
|
||||
pkt_params[0] = 0x00;
|
||||
pkt_params[1] = 0x08; /* preamble length high byte */
|
||||
pkt_params[2] = 0x00; /* explicit header */
|
||||
pkt_params[3] = (uint8_t)data_len;
|
||||
pkt_params[4] = 0x01; /* CRC on */
|
||||
pkt_params[5] = 0x00; /* standard IQ */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_PACKET_PARAMS,
|
||||
pkt_params, 6);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset the event semaphore and trigger TX */
|
||||
k_sem_reset(&drv->event_sem);
|
||||
|
||||
params[0] = 0x00; /* TX timeout = 0 (no timeout) */
|
||||
params[1] = 0x00;
|
||||
params[2] = 0x00;
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_TX, params, 3);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for TX_DONE IRQ */
|
||||
ret = k_sem_take(&drv->event_sem, K_MSEC(5000));
|
||||
if (ret < 0) {
|
||||
LOG_WRN("TX timeout");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Read and check IRQ status */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_GET_IRQ_STATUS, NULL, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t irq_buf[4];
|
||||
|
||||
ret = lr11xx_hal_read_resp(dev, irq_buf, sizeof(irq_buf));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t irq = ((uint32_t)irq_buf[0] << 24) |
|
||||
((uint32_t)irq_buf[1] << 16) |
|
||||
((uint32_t)irq_buf[2] << 8) |
|
||||
((uint32_t)irq_buf[3]);
|
||||
|
||||
if (!(irq & LR11XX_IRQ_TX_DONE)) {
|
||||
LOG_WRN("TX: unexpected IRQ 0x%08x", irq);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Clear IRQ flags */
|
||||
params[0] = (irq >> 24) & 0xFF;
|
||||
params[1] = (irq >> 16) & 0xFF;
|
||||
params[2] = (irq >> 8) & 0xFF;
|
||||
params[3] = (irq >> 0) & 0xFF;
|
||||
lr11xx_hal_write_cmd(dev, LR11XX_CMD_CLEAR_IRQ_STATUS, params, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── Receive ─────────────────────────────────────────────────────────────── */
|
||||
|
||||
static int lr11xx_recv(const struct device *dev,
|
||||
uint8_t *data, uint8_t size,
|
||||
k_timeout_t timeout,
|
||||
int16_t *rssi, int8_t *snr)
|
||||
{
|
||||
struct lr11xx_data *drv = dev->data;
|
||||
int ret;
|
||||
uint8_t params[3];
|
||||
|
||||
/* Clear pending IRQs */
|
||||
uint32_t clr = LR11XX_IRQ_ALL;
|
||||
|
||||
params[0] = (clr >> 24) & 0xFF;
|
||||
params[1] = (clr >> 16) & 0xFF;
|
||||
params[2] = (clr >> 8) & 0xFF;
|
||||
params[3] = (clr >> 0) & 0xFF;
|
||||
lr11xx_hal_write_cmd(dev, LR11XX_CMD_CLEAR_IRQ_STATUS, params, 4);
|
||||
|
||||
/* Enter continuous RX */
|
||||
k_sem_reset(&drv->event_sem);
|
||||
|
||||
params[0] = 0xFF; /* RX timeout: 0xFFFFFF = single with preamble timeout */
|
||||
params[1] = 0xFF;
|
||||
params[2] = 0xFF;
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_SET_RX, params, 3);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for RX_DONE (or timeout) */
|
||||
ret = k_sem_take(&drv->event_sem, timeout);
|
||||
if (ret < 0) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Read IRQ status */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_GET_IRQ_STATUS, NULL, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t irq_buf[4];
|
||||
|
||||
ret = lr11xx_hal_read_resp(dev, irq_buf, sizeof(irq_buf));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t irq = ((uint32_t)irq_buf[0] << 24) |
|
||||
((uint32_t)irq_buf[1] << 16) |
|
||||
((uint32_t)irq_buf[2] << 8) |
|
||||
((uint32_t)irq_buf[3]);
|
||||
|
||||
/* Clear IRQ flags */
|
||||
params[0] = (irq >> 24) & 0xFF;
|
||||
params[1] = (irq >> 16) & 0xFF;
|
||||
params[2] = (irq >> 8) & 0xFF;
|
||||
params[3] = (irq >> 0) & 0xFF;
|
||||
lr11xx_hal_write_cmd(dev, LR11XX_CMD_CLEAR_IRQ_STATUS, params, 4);
|
||||
|
||||
if (irq & (LR11XX_IRQ_TIMEOUT | LR11XX_IRQ_CRC_ERROR)) {
|
||||
LOG_DBG("RX error IRQ=0x%08x", irq);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!(irq & LR11XX_IRQ_RX_DONE)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get RX buffer status: payload_len, rx_start_ptr */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_GET_RX_BUFFER_STATUS, NULL, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t rxbuf_status[2];
|
||||
|
||||
ret = lr11xx_hal_read_resp(dev, rxbuf_status, sizeof(rxbuf_status));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t payload_len = rxbuf_status[0];
|
||||
uint8_t offset = rxbuf_status[1];
|
||||
|
||||
if (payload_len > size) {
|
||||
payload_len = size;
|
||||
}
|
||||
|
||||
/* Read payload from RX buffer */
|
||||
uint8_t read_params[2] = { offset, payload_len };
|
||||
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_READ_BUFFER,
|
||||
read_params, sizeof(read_params));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lr11xx_hal_read_resp(dev, data, payload_len);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get packet status (RSSI, SNR) */
|
||||
ret = lr11xx_hal_write_cmd(dev, LR11XX_CMD_GET_PACKET_STATUS, NULL, 0);
|
||||
if (ret < 0) {
|
||||
goto skip_status;
|
||||
}
|
||||
|
||||
uint8_t pkt_status[3];
|
||||
|
||||
ret = lr11xx_hal_read_resp(dev, pkt_status, sizeof(pkt_status));
|
||||
if (ret == 0) {
|
||||
/* rssi_pkt = -pkt_status[0] / 2 dBm */
|
||||
if (rssi) {
|
||||
*rssi = -(int16_t)pkt_status[0] / 2;
|
||||
}
|
||||
/* snr_pkt = pkt_status[1] / 4 dB (signed) */
|
||||
if (snr) {
|
||||
*snr = (int8_t)pkt_status[1] / 4;
|
||||
}
|
||||
}
|
||||
|
||||
skip_status:
|
||||
return (int)payload_len;
|
||||
}
|
||||
|
||||
/* ── Initialization ──────────────────────────────────────────────────────── */
|
||||
|
||||
static int lr11xx_init(const struct device *dev)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
struct lr11xx_data *drv = dev->data;
|
||||
int ret;
|
||||
|
||||
if (!spi_is_ready_dt(&cfg->spi)) {
|
||||
LOG_ERR("SPI bus not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&cfg->reset, GPIO_OUTPUT_ACTIVE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&cfg->busy, GPIO_INPUT);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&cfg->event, GPIO_INPUT);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_sem_init(&drv->event_sem, 0, 1);
|
||||
|
||||
/* Set up interrupt on EVENT/DIO9 pin */
|
||||
ret = gpio_pin_interrupt_configure_dt(&cfg->event,
|
||||
GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_init_callback(&drv->event_cb, lr11xx_hal_event_handler,
|
||||
BIT(cfg->event.pin));
|
||||
ret = gpio_add_callback(cfg->event.port, &drv->event_cb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Hardware reset */
|
||||
ret = lr11xx_hal_reset(dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Reset failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("LR11xx initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── Driver API ──────────────────────────────────────────────────────────── */
|
||||
|
||||
static const struct lora_driver_api lr11xx_api = {
|
||||
.config = lr11xx_configure,
|
||||
.send = lr11xx_send,
|
||||
.recv = lr11xx_recv,
|
||||
};
|
||||
|
||||
/* ── Device instantiation macro ─────────────────────────────────────────── */
|
||||
|
||||
#define LR11XX_DEFINE(n) \
|
||||
static struct lr11xx_data lr11xx_data_##n; \
|
||||
\
|
||||
static const struct lr11xx_config lr11xx_config_##n = { \
|
||||
.spi = SPI_DT_SPEC_INST_GET(n, \
|
||||
SPI_WORD_SET(8) | SPI_TRANSFER_MSB, 0), \
|
||||
.reset = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \
|
||||
.busy = GPIO_DT_SPEC_INST_GET(n, busy_gpios), \
|
||||
.event = GPIO_DT_SPEC_INST_GET(n, event_gpios), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, \
|
||||
lr11xx_init, NULL, \
|
||||
&lr11xx_data_##n, &lr11xx_config_##n, \
|
||||
POST_KERNEL, CONFIG_LORA_INIT_PRIORITY, \
|
||||
&lr11xx_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(LR11XX_DEFINE)
|
||||
107
drivers/lora/lr11xx/lr11xx.h
Normal file
107
drivers/lora/lr11xx/lr11xx.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Semtech LR11xx private driver header.
|
||||
* Defines SPI opcodes, register constants, and internal types.
|
||||
*/
|
||||
|
||||
#ifndef LR11XX_H
|
||||
#define LR11XX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
/* ── SPI opcode definitions ──────────────────────────────────────────────── */
|
||||
|
||||
/* System commands */
|
||||
#define LR11XX_CMD_RESET_STATS 0x0200u
|
||||
#define LR11XX_CMD_GET_STATUS 0x0100u
|
||||
#define LR11XX_CMD_SET_STANDBY 0x011Bu /* Note: opcode varies by fw */
|
||||
#define LR11XX_CMD_SET_PACKET_TYPE 0x010Au
|
||||
#define LR11XX_CMD_SET_RF_FREQUENCY 0x010Eu
|
||||
#define LR11XX_CMD_SET_MODULATION_PARAMS 0x0115u
|
||||
#define LR11XX_CMD_SET_PACKET_PARAMS 0x0116u
|
||||
#define LR11XX_CMD_SET_TX_PARAMS 0x0110u
|
||||
#define LR11XX_CMD_SET_DIO_IRQ_PARAMS 0x0113u
|
||||
#define LR11XX_CMD_CLEAR_IRQ_STATUS 0x0114u
|
||||
#define LR11XX_CMD_GET_IRQ_STATUS 0x0112u
|
||||
#define LR11XX_CMD_SET_RX 0x0107u
|
||||
#define LR11XX_CMD_SET_TX 0x0108u
|
||||
#define LR11XX_CMD_GET_RX_BUFFER_STATUS 0x011Cu
|
||||
#define LR11XX_CMD_READ_BUFFER 0x011Eu
|
||||
#define LR11XX_CMD_GET_PACKET_STATUS 0x011Du
|
||||
#define LR11XX_CMD_SET_BUFFER_BASE_ADDR 0x010Fu
|
||||
|
||||
/* Packet type: LoRa = 0x01 */
|
||||
#define LR11XX_PKT_TYPE_LORA 0x01u
|
||||
|
||||
/* Standby mode: RC oscillator = 0x00, XOSC = 0x01 */
|
||||
#define LR11XX_STANDBY_RC 0x00u
|
||||
#define LR11XX_STANDBY_XOSC 0x01u
|
||||
|
||||
/* IRQ bitmasks */
|
||||
#define LR11XX_IRQ_TX_DONE BIT(2)
|
||||
#define LR11XX_IRQ_RX_DONE BIT(3)
|
||||
#define LR11XX_IRQ_TIMEOUT BIT(8)
|
||||
#define LR11XX_IRQ_CRC_ERROR BIT(10)
|
||||
#define LR11XX_IRQ_ALL 0xFFFFFFFFu
|
||||
|
||||
/* DIO mask for routing IRQs to EVENT/DIO9 */
|
||||
#define LR11XX_DIO9_MASK (LR11XX_IRQ_TX_DONE | LR11XX_IRQ_RX_DONE | \
|
||||
LR11XX_IRQ_TIMEOUT | LR11XX_IRQ_CRC_ERROR)
|
||||
|
||||
/* LoRa spreading factor codes */
|
||||
#define LR11XX_LORA_SF5 0x05u
|
||||
#define LR11XX_LORA_SF6 0x06u
|
||||
#define LR11XX_LORA_SF7 0x07u
|
||||
#define LR11XX_LORA_SF8 0x08u
|
||||
#define LR11XX_LORA_SF9 0x09u
|
||||
#define LR11XX_LORA_SF10 0x0Au
|
||||
#define LR11XX_LORA_SF11 0x0Bu
|
||||
#define LR11XX_LORA_SF12 0x0Cu
|
||||
|
||||
/* LoRa bandwidth codes */
|
||||
#define LR11XX_LORA_BW_125 0x04u
|
||||
#define LR11XX_LORA_BW_250 0x05u
|
||||
#define LR11XX_LORA_BW_500 0x06u
|
||||
|
||||
/* LoRa coding rate codes */
|
||||
#define LR11XX_LORA_CR_4_5 0x01u
|
||||
#define LR11XX_LORA_CR_4_6 0x02u
|
||||
#define LR11XX_LORA_CR_4_7 0x03u
|
||||
#define LR11XX_LORA_CR_4_8 0x04u
|
||||
|
||||
/* RX timeout: 0 = continuous, 0xFFFFFF = single (timeout on preamble) */
|
||||
#define LR11XX_RX_CONTINUOUS 0x000000u
|
||||
|
||||
/* ── Driver config/data structures ───────────────────────────────────────── */
|
||||
|
||||
struct lr11xx_config {
|
||||
struct spi_dt_spec spi;
|
||||
struct gpio_dt_spec reset;
|
||||
struct gpio_dt_spec busy;
|
||||
struct gpio_dt_spec event;
|
||||
};
|
||||
|
||||
struct lr11xx_data {
|
||||
struct gpio_callback event_cb;
|
||||
struct k_sem event_sem;
|
||||
uint32_t last_irq;
|
||||
};
|
||||
|
||||
/* ── HAL function prototypes (implemented in lr11xx_hal.c) ───────────────── */
|
||||
|
||||
int lr11xx_hal_reset(const struct device *dev);
|
||||
int lr11xx_hal_wait_busy(const struct device *dev, k_timeout_t timeout);
|
||||
int lr11xx_hal_write_cmd(const struct device *dev,
|
||||
uint16_t opcode,
|
||||
const uint8_t *params, size_t params_len);
|
||||
int lr11xx_hal_read_resp(const struct device *dev,
|
||||
uint8_t *buf, size_t len);
|
||||
void lr11xx_hal_event_handler(const struct device *port,
|
||||
struct gpio_callback *cb, uint32_t pins);
|
||||
|
||||
#endif /* LR11XX_H */
|
||||
129
drivers/lora/lr11xx/lr11xx_hal.c
Normal file
129
drivers/lora/lr11xx/lr11xx_hal.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* LR11xx Zephyr HAL — SPI and GPIO low-level operations.
|
||||
*
|
||||
* The LR11xx SPI protocol:
|
||||
* WRITE: Assert NSS → send 2-byte opcode + param bytes → deassert NSS
|
||||
* READ: Assert NSS → send 2-byte opcode → deassert NSS
|
||||
* Wait for BUSY low
|
||||
* Assert NSS → read STAT1 + STAT2 + N response bytes → deassert NSS
|
||||
*
|
||||
* All transactions must wait for BUSY=low before asserting NSS.
|
||||
*/
|
||||
|
||||
#include "lr11xx.h"
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(lr11xx, LOG_LEVEL_DBG);
|
||||
|
||||
#define BUSY_POLL_INTERVAL_US 100
|
||||
#define BUSY_TIMEOUT_MS 1000
|
||||
|
||||
int lr11xx_hal_reset(const struct device *dev)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = gpio_pin_set_dt(&cfg->reset, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
k_sleep(K_USEC(200));
|
||||
ret = gpio_pin_set_dt(&cfg->reset, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
k_sleep(K_MSEC(10));
|
||||
|
||||
return lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
int lr11xx_hal_wait_busy(const struct device *dev, k_timeout_t timeout)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
uint32_t start = k_uptime_get_32();
|
||||
uint32_t timeout_ms = k_ticks_to_ms_floor32(timeout.ticks);
|
||||
|
||||
while (gpio_pin_get_dt(&cfg->busy)) {
|
||||
if (timeout_ms > 0 &&
|
||||
(k_uptime_get_32() - start) > timeout_ms) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
k_busy_wait(BUSY_POLL_INTERVAL_US);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lr11xx_hal_write_cmd(const struct device *dev,
|
||||
uint16_t opcode,
|
||||
const uint8_t *params, size_t params_len)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t opcode_buf[2] = {
|
||||
(uint8_t)(opcode >> 8),
|
||||
(uint8_t)(opcode & 0xFF),
|
||||
};
|
||||
|
||||
const struct spi_buf tx_bufs[] = {
|
||||
{ .buf = opcode_buf, .len = sizeof(opcode_buf) },
|
||||
{ .buf = (void *)params, .len = params_len },
|
||||
};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = tx_bufs,
|
||||
.count = (params_len > 0) ? 2 : 1,
|
||||
};
|
||||
|
||||
return spi_write_dt(&cfg->spi, &tx);
|
||||
}
|
||||
|
||||
int lr11xx_hal_read_resp(const struct device *dev,
|
||||
uint8_t *buf, size_t len)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* First two bytes are STAT1 and STAT2 — discard for now */
|
||||
uint8_t status[2];
|
||||
uint8_t dummy_opcode[2] = {0x00, 0x00};
|
||||
|
||||
const struct spi_buf tx_bufs[] = {
|
||||
{ .buf = dummy_opcode, .len = sizeof(dummy_opcode) },
|
||||
};
|
||||
struct spi_buf rx_bufs[] = {
|
||||
{ .buf = status, .len = sizeof(status) },
|
||||
{ .buf = buf, .len = len },
|
||||
};
|
||||
const struct spi_buf_set tx = { .buffers = tx_bufs, .count = 1 };
|
||||
const struct spi_buf_set rx = { .buffers = rx_bufs, .count = (len > 0) ? 2 : 1 };
|
||||
|
||||
return spi_transceive_dt(&cfg->spi, &tx, &rx);
|
||||
}
|
||||
|
||||
void lr11xx_hal_event_handler(const struct device *port,
|
||||
struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
ARG_UNUSED(port);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
struct lr11xx_data *data =
|
||||
CONTAINER_OF(cb, struct lr11xx_data, event_cb);
|
||||
|
||||
k_sem_give(&data->event_sem);
|
||||
}
|
||||
46
dts/bindings/lora/semtech,lr11xx.yaml
Normal file
46
dts/bindings/lora/semtech,lr11xx.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
# Copyright (c) 2024 loramodem contributors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Semtech LR1110/LR1120 multi-protocol radio transceiver.
|
||||
|
||||
This driver implements Zephyr's LoRa subsystem API for LoRa packet mode
|
||||
only. LoRaWAN and GNSS capabilities of the LR11xx are not used.
|
||||
|
||||
The LR11xx uses a 2-byte SPI opcode protocol that differs from the
|
||||
SX126x family: commands consist of a 2-byte opcode followed by
|
||||
parameters. Responses are read in a separate transaction after
|
||||
asserting NSS, beginning with STAT1 and STAT2 status bytes.
|
||||
|
||||
compatible: "semtech,lr11xx"
|
||||
|
||||
include: spi-device.yaml
|
||||
|
||||
properties:
|
||||
reset-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
GPIO connected to NRESET (active low). Drive low for >= 100 us
|
||||
to reset the chip.
|
||||
|
||||
busy-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
GPIO connected to the BUSY pin (active high).
|
||||
The host must wait for BUSY to go low before starting a new
|
||||
SPI transaction.
|
||||
|
||||
event-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
GPIO connected to the DIO9 / EVENT pin (active high).
|
||||
Used for RX packet-ready and TX-done interrupts.
|
||||
|
||||
lf-clk-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
Optional: GPIO to enable/disable the LF clock output from LR11xx.
|
||||
Some board designs use this to drive an external 32.768 kHz crystal.
|
||||
18
west.yml
Normal file
18
west.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
manifest:
|
||||
self:
|
||||
path: loramodem
|
||||
|
||||
remotes:
|
||||
- name: zephyrproject-rtos
|
||||
url-base: https://github.com/zephyrproject-rtos
|
||||
|
||||
projects:
|
||||
- name: zephyr
|
||||
remote: zephyrproject-rtos
|
||||
revision: v3.7.0
|
||||
import:
|
||||
name-allowlist:
|
||||
- cmsis
|
||||
- hal_nordic
|
||||
- hal_espressif
|
||||
- loramac-node
|
||||
9
zephyr/module.yml
Normal file
9
zephyr/module.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
name: loramodem
|
||||
|
||||
build:
|
||||
cmake: .
|
||||
kconfig: Kconfig
|
||||
|
||||
settings:
|
||||
board_root: .
|
||||
dts_root: .
|
||||
Reference in New Issue
Block a user