diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index 345d269..6b24e42 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -1,44 +1,22 @@ # SPDX-License-Identifier: Apache-2.0 -# ZephCore Zephyr - full port cmake_minimum_required(VERSION 3.20.0) # ============================================================================ -# Zephyr Patch Auto-Apply (unified diffs via git apply) +# Zephyr Patch Auto-Apply # ============================================================================ -# Patches are applied at configure time using `git apply`. This means: -# - If upstream changed in a conflicting area → build FAILS with clear error -# - If upstream changed in non-conflicting areas → changes are preserved -# - Idempotent: re-running cmake without west update detects already-applied patches +# patches/zephyr/*.patch — unified diffs applied via `git apply` at configure time +# patches/zephyr-new/ — new files copied into the Zephyr tree (no upstream) # -# Directory layout: -# patches/zephyr/*.patch - unified diffs applied to the Zephyr tree -# patches/zephyr-new/ - new files copied to the Zephyr tree (no upstream) +# Idempotent: stamp file tracks (patch hashes + target HEAD). Conflicts are fatal. # -# Zephyr patches: -# 0001-lora-lr11xx-build - CMakeLists.txt + Kconfig (lr11xx subdirectory) -# 0003-lora-sx126x-native - native driver: DIO1 WQ, duty cycle, CRC fix, -# extension API, RF switch fix -# 0005-gnss-air530z-easy - EASY ephemeris prediction (PMTK869) + Kconfig -# 0006-blobs-py - west blobs command fix -# New files (copied, not patched): -# drivers/lora/lr11xx/* - LR11xx Zephyr LoRa driver -# drivers/lora/native/sx126x/sx126x_ext.h - SX126x extension API header -# dts/bindings/lora/semtech,lr1110.yaml - LR1110 DTS binding -# - -# --- Helper: apply unified diff patches via git apply --- function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL) file(GLOB PATCH_FILES "${PATCH_DIR}/*.patch") list(SORT PATCH_FILES) if(NOT PATCH_FILES) return() endif() - # Stamp-based idempotency: hash of (patch contents + target git HEAD). - # If the stamp matches we already applied these exact patches to this exact - # tree state — skip. A per-patch git apply --reverse --check fails for - # chained patches that touch the same file (N's reverse can't apply once - # N+1 is also present), so reverse-checking is not reliable here. + # Stamp = hash(patch contents + target HEAD). Skip if unchanged. execute_process( COMMAND git rev-parse HEAD WORKING_DIRECTORY "${TARGET_DIR}" @@ -64,26 +42,23 @@ function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL) endif() foreach(PATCH_FILE ${PATCH_FILES}) get_filename_component(PATCH_NAME ${PATCH_FILE} NAME) - # Verify patch applies cleanly + # Dry-run check execute_process( COMMAND git apply --check "${PATCH_FILE}" WORKING_DIRECTORY "${TARGET_DIR}" RESULT_VARIABLE PATCH_CHECK ERROR_VARIABLE PATCH_ERR ) - # If forward-apply fails, the tree may have stale patches from a - # previous build (west update doesn't reset dirty files). Extract - # the file list from the patch and git-checkout those paths to - # restore them to the clean upstream state, then re-check. + # Stale patches from previous build: reset affected files, retry if(NOT PATCH_CHECK EQUAL 0) - # git apply --numstat lists affected files (one per line) + # Extract affected file paths from numstat execute_process( COMMAND git apply --numstat "${PATCH_FILE}" WORKING_DIRECTORY "${TARGET_DIR}" OUTPUT_VARIABLE PATCH_NUMSTAT ERROR_QUIET ) - # Parse file paths from numstat (format: "adds\tdels\tpath") + # numstat format: "adds\tdels\tpath" string(REGEX MATCHALL "[^\t\n]+\t[^\t\n]+\t[^\t\n]+" NUMSTAT_LINES "${PATCH_NUMSTAT}") set(PATCH_PATHS "") foreach(_line ${NUMSTAT_LINES}) @@ -97,7 +72,7 @@ function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL) WORKING_DIRECTORY "${TARGET_DIR}" ERROR_QUIET ) - # Re-check after reset + # Retry dry-run execute_process( COMMAND git apply --check "${PATCH_FILE}" WORKING_DIRECTORY "${TARGET_DIR}" @@ -117,7 +92,7 @@ function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL) " # Regenerate: git diff -- > ${PATCH_FILE}\n" ) endif() - # Apply the patch + # Apply execute_process( COMMAND git apply "${PATCH_FILE}" WORKING_DIRECTORY "${TARGET_DIR}" @@ -132,11 +107,11 @@ function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL) file(WRITE "${_stamp}" "${_stamp_hash}") endfunction() -# Resolve target directories (before find_package(Zephyr) sets ZEPHYR_BASE) +# Resolve target directories before find_package(Zephyr) get_filename_component(ZEPHYR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr ABSOLUTE) get_filename_component(MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../modules ABSOLUTE) -# Apply unified diff patches to Zephyr tree +# Apply patches if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr) message(STATUS "Applying ZephCore patches to Zephyr...") zephcore_apply_patches( @@ -146,7 +121,7 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr) ) endif() -# Copy new files (no upstream equivalent) to Zephyr tree +# Copy new files into Zephyr tree if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new) file(GLOB_RECURSE ZEPHCORE_NEW_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new @@ -159,29 +134,13 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new) endforeach() endif() -# Add custom boards directory (for Wio Tracker L1, etc.) -# Zephyr expects: /boards/// -# Our structure: zephcore/boards/// -# So we add zephcore/ as BOARD_ROOT, and Zephyr finds boards/nrf52840/wio_tracker_l1/ +# Custom board root: boards/// discovered via BOARD_ROOT list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) # ========== Board Configuration Hierarchy ========== -# Include order: prj.conf → zephcore_common.conf → _common.conf → /board.conf -# -# Structure: -# boards/common/zephcore_common.conf - ALL boards (crypto, sensors, LoRa) -# boards/common/nrf52_common.conf - nRF52 boards (BLE, flash, GNSS) -# boards//board.conf - Board-specific (pins, features) -# boards//prod.conf - Production overrides (optional) -# -# To add a new board: -# 1. Create boards//board.conf with pins and unique features -# 2. Create boards//board.overlay with hardware definitions -# 3. Build: west build -b zephcore --pristine +# prj.conf → zephcore_common.conf → _common.conf → /board.conf -# Under sysbuild, BOARD and EXTRA_CONF_FILE are stored in a cache file that -# find_package(Zephyr) loads later. We need them NOW for config hierarchy -# detection. Read them directly from the sysbuild cache file. +# Recover BOARD and EXTRA_CONF_FILE from sysbuild cache (needed before find_package) if(NOT BOARD AND DEFINED SYSBUILD_CACHE AND EXISTS "${SYSBUILD_CACHE}") file(STRINGS "${SYSBUILD_CACHE}" _sysbuild_strings ENCODING UTF-8) foreach(_str ${_sysbuild_strings}) diff --git a/zephcore/adapters/ble/ZephyrBLE.h b/zephcore/adapters/ble/ZephyrBLE.h index ac3b3a2..d58c5fc 100644 --- a/zephcore/adapters/ble/ZephyrBLE.h +++ b/zephcore/adapters/ble/ZephyrBLE.h @@ -13,96 +13,57 @@ extern "C" { #endif -/* Callbacks from BLE adapter → main */ +/* Callbacks from BLE adapter to main */ struct ble_callbacks { - /* Called when a complete frame is received from the remote (RX). - * Called from system work queue context — must not block. */ + /* RX frame received; runs on system work queue — must not block */ void (*on_rx_frame)(const uint8_t *data, uint16_t len); - /* Called when TX queue is empty (can continue contact iteration) */ + /* TX queue drained */ void (*on_tx_idle)(void); - /* Called on BLE connect (for UI notify + USB state clearing) */ + /* BLE connected */ void (*on_connected)(void); - /* Called on BLE disconnect */ + /* BLE disconnected */ void (*on_disconnected)(void); }; -/* Interface type for BLE/USB coexistence */ enum zephcore_iface { ZEPHCORE_IFACE_NONE, ZEPHCORE_IFACE_BLE, ZEPHCORE_IFACE_USB, }; -/** - * Register callbacks and auth handlers. Call before bt_enable(). - */ +/** Register callbacks and auth handlers. Call before bt_enable(). */ void zephcore_ble_init(const struct ble_callbacks *cbs); -/** - * Called from bt_ready() after bt_enable succeeds. - * Loads settings, builds advertising data, starts advertising. - * @param device_name Name to advertise (NULL = "MeshCore") - */ +/** Load settings, build adv data, start advertising. Call from bt_ready(). */ void zephcore_ble_start(const char *device_name); -/** - * Queue a frame for BLE TX. - * @return number of bytes queued, or 0 on failure - */ +/** Queue a frame for BLE TX. Returns bytes queued, or 0 on failure. */ size_t zephcore_ble_send(const uint8_t *data, uint16_t len); -/** - * Enable/disable BLE (advertising + connections). - * When disabled, disconnects any active connection and stops advertising. - */ +/** Enable/disable BLE. Disabling disconnects and stops advertising. */ void zephcore_ble_set_enabled(bool enable); -/** - * Check if BLE is the active transport and ready to send. - */ +/** True if BLE is the active transport and ready to send. */ bool zephcore_ble_is_active(void); -/** - * Check if BLE has an active connection (regardless of interface state). - */ +/** True if BLE has an active connection (regardless of interface state). */ bool zephcore_ble_is_connected(void); -/** - * Check if BLE TX is congested (queue full, overflow retrying). - * Callers should stop sending until this clears. - */ +/** True if TX queue is full and overflow retry is active. */ bool zephcore_ble_is_congested(void); -/** - * Set the BLE passkey at runtime. - */ void zephcore_ble_set_passkey(uint32_t passkey); - -/** - * Get the current BLE passkey. - */ uint32_t zephcore_ble_get_passkey(void); -/** - * Get/set active interface (for USB switching in main). - */ +/** Get/set active interface (BLE/USB coexistence). */ enum zephcore_iface zephcore_ble_get_active_iface(void); void zephcore_ble_set_active_iface(enum zephcore_iface iface); -/** - * Get pointer to the recv_queue (k_msgq) for USB RX path sharing. - * USB code in main needs to put frames directly into this queue. - */ +/** Get recv/send queues for USB path sharing. */ struct k_msgq *zephcore_ble_get_recv_queue(void); - -/** - * Get pointer to the send_queue for USB TX path. - */ struct k_msgq *zephcore_ble_get_send_queue(void); -/** - * Kick the TX drain work — call after putting frames in the send queue. - */ +/** Kick the TX drain work. Call after putting frames in the send queue. */ void zephcore_ble_kick_tx(void); /** diff --git a/zephcore/adapters/radio/LoRaRadioBase.cpp b/zephcore/adapters/radio/LoRaRadioBase.cpp index 53b285e..d92dd72 100644 --- a/zephcore/adapters/radio/LoRaRadioBase.cpp +++ b/zephcore/adapters/radio/LoRaRadioBase.cpp @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * LoRa radio base class — all shared algorithms. + * LoRa radio base class — shared algorithms for all radio adapters. */ #include "LoRaRadioBase.h" @@ -17,7 +17,7 @@ LOG_MODULE_REGISTER(lora_radio_base, CONFIG_ZEPHCORE_LORA_LOG_LEVEL); namespace mesh { -/* ── Constructor ──────────────────────────────────────────────────────── */ +/* ── Constructor ─────────────────────────────────────────────── */ LoRaRadioBase::LoRaRadioBase(const struct device *lora_dev, MainBoard &board, NodePrefs *prefs) @@ -40,7 +40,7 @@ LoRaRadioBase::LoRaRadioBase(const struct device *lora_dev, MainBoard &board, memset(_rx_ring, 0, sizeof(_rx_ring)); } -/* ── TX wait thread ───────────────────────────────────────────────────── */ +/* ── TX wait thread ──────────────────────────────────────────── */ void LoRaRadioBase::txWaitThreadFn(void *p1, void *p2, void *p3) { @@ -65,7 +65,6 @@ void LoRaRadioBase::txWaitThreadFn(void *p1, void *p2, void *p3) &self->_tx_signal), }; - /* Check if signal was already raised */ unsigned int signaled; int result; k_poll_signal_check(&self->_tx_signal, &signaled, &result); diff --git a/zephcore/adapters/radio/LoRaRadioBase.h b/zephcore/adapters/radio/LoRaRadioBase.h index 5d77caa..6c367ab 100644 --- a/zephcore/adapters/radio/LoRaRadioBase.h +++ b/zephcore/adapters/radio/LoRaRadioBase.h @@ -1,9 +1,7 @@ /* * SPDX-License-Identifier: Apache-2.0 - * LoRa radio base class — all shared state and algorithms. - * - * Subclasses (SX126xRadio, LR1110Radio, …) only implement the hw*() - * primitives that talk to real hardware. Everything else lives here. + * LoRa radio base class — shared state and algorithms. + * Subclasses implement hw*() primitives only. */ #pragma once @@ -80,30 +78,16 @@ public: protected: /* ── Hardware primitives — subclass MUST implement ─────────── */ - /** Apply lora_modem_config + any chip-specific extras (image cal, etc.) */ virtual void hwConfigure(const struct lora_modem_config &cfg) = 0; - - /** Cancel current async receive */ virtual void hwCancelReceive() = 0; - - /** Start async send — returns 0 on success */ virtual int hwSendAsync(uint8_t *buf, uint32_t len, struct k_poll_signal *sig) = 0; - - /** Read instantaneous RSSI from hardware */ virtual int16_t hwGetCurrentRSSI() = 0; - - /** Check IRQ flags for preamble/header detection */ virtual bool hwIsPreambleDetected() = 0; - - /** Set RX LNA boost on/off */ virtual void hwSetRxBoost(bool enable) = 0; - - /** Reset AGC (chip-specific, may be no-op) */ virtual void hwResetAGC() = 0; - /** Check if chip BUSY pin is high — no SPI, safe to call any time. - * Returns false on chips without a duty-cycle sleep phase (LR1110). */ + /** GPIO-only BUSY check (no SPI). Default false for chips without duty-cycle sleep. */ virtual bool hwIsChipBusy() { return false; } /* ── Shared helpers available to subclasses ────────────────── */ @@ -113,7 +97,6 @@ protected: void configureTx(); void startReceive(); - /* Subclass begin() should call this to start TX wait thread */ void startTxThread(k_thread_stack_t *stack, size_t stack_size); const struct device *_dev; @@ -121,8 +104,8 @@ protected: MainBoard *_board; atomic_t _in_recv_mode; atomic_t _tx_active; - volatile float _last_rssi; /* word-aligned float — atomic on ARM */ - volatile float _last_snr; /* word-aligned float — atomic on ARM */ + volatile float _last_rssi; /* word-aligned: atomic on ARM */ + volatile float _last_snr; /* word-aligned: atomic on ARM */ /* RX ring buffer */ struct RxPacket { @@ -142,18 +125,18 @@ protected: /* Noise floor calibration state */ int _noise_floor; int _calibration_threshold; - uint8_t _ema_unguarded; /* ticks until next unfiltered sample */ + uint8_t _ema_unguarded; /* tick counter for warmup + periodic bypass */ /* Power saving */ bool _rx_duty_cycle_enabled; bool _rx_boost_enabled; int8_t _tx_power_reduction_db; - /* Config cache — skip redundant hwConfigure() on TX↔RX transitions */ + /* Config cache — skip redundant hwConfigure() */ struct lora_modem_config _last_cfg; bool _config_cached; - /* Static RX callback — passed to lora_recv_async() / lora_recv_duty_cycle() */ + /* ISR RX callback — passed to lora_recv_async() / lora_recv_duty_cycle() */ static void rxCallbackStatic(const struct device *dev, uint8_t *data, uint16_t size, int16_t rssi, int8_t snr, void *user_data); diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_hal.h b/zephcore/adapters/radio/lr11xx/lr11xx_hal.h index 0c90695..f96a5c5 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_hal.h +++ b/zephcore/adapters/radio/lr11xx/lr11xx_hal.h @@ -57,9 +57,7 @@ extern "C" { * --- PUBLIC CONSTANTS -------------------------------------------------------- */ -/** - * @brief Write this to SPI bus while reading data, or as a dummy/placeholder - */ +/** @brief NOP byte sent on MOSI during read transactions (LR11XX SPI protocol) */ #define LR11XX_NOP ( 0x00 ) /* @@ -73,7 +71,7 @@ extern "C" { typedef enum lr11xx_hal_status_e { LR11XX_HAL_STATUS_OK = 0, - LR11XX_HAL_STATUS_ERROR = 3, + LR11XX_HAL_STATUS_ERROR = 3, /* value 3 is cast directly to lr11xx_status_t ERROR */ } lr11xx_hal_status_t; /* @@ -181,7 +179,7 @@ inline static uint8_t lr11xx_hal_compute_crc( const uint8_t initial_value, const if( sum != 0 ) { - crc ^= 0x65; + crc ^= 0x65; /* CRC-8/NRSC-5 reflected polynomial (0xA6 >> 1) */ } extract >>= 1; diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c index bbdeb24..0d84d87 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c +++ b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c @@ -1,8 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * LR11xx HAL implementation for Zephyr - ZephCore - * - * Based on Semtech SWDR001 lr11xx_driver and IRNAS/SWDR001-Zephyr port. + * LR11xx HAL implementation for Zephyr — ZephCore */ #include "lr11xx_hal_zephyr.h" @@ -11,19 +9,15 @@ #include LOG_MODULE_REGISTER(lr11xx_hal, CONFIG_LORA_LOG_LEVEL); -/* Timeout for busy wait in milliseconds. - * LR1110 should respond within a few ms after most commands. - * After reset, firmware boot can take up to 273ms (datasheet). - * Use 3000ms to match RadioLib's timeout. */ +/* Datasheet: firmware boot ≤273ms after reset; 3000ms gives margin for slow startup */ #define LR11XX_BUSY_TIMEOUT_MS 3000 -/* Static state for DIO1 interrupt handling */ static struct gpio_callback dio1_gpio_cb; static lr11xx_dio1_callback_t dio1_user_cb = NULL; static void *dio1_user_data = NULL; static struct lr11xx_hal_context *current_ctx = NULL; -/* BUSY pin interrupt — wakes wait_on_busy() via semaphore instead of polling */ +/* BUSY falling-edge interrupt wakes wait_on_busy() via semaphore */ static struct gpio_callback busy_gpio_cb; static K_SEM_DEFINE(busy_sem, 0, 1); @@ -36,21 +30,18 @@ static void busy_isr_callback(const struct device *dev, struct gpio_callback *cb k_sem_give(&busy_sem); } -/* Track the last SPI opcode for debugging BUSY stuck */ +/* Last SPI opcode and timestamp — reported on BUSY timeout for diagnosis */ static uint16_t last_opcode; static int64_t last_cmd_time; /** - * @brief Wait until BUSY pin goes low or timeout. + * @brief Block until BUSY low or timeout; uses GPIO interrupt + semaphore (not polling). * - * Uses GPIO interrupt + semaphore instead of polling. The CPU sleeps - * while waiting, saving power during long BUSY periods (reset, sleep - * wake, firmware commands). For sub-microsecond waits the fast-path - * check returns immediately without touching the interrupt at all. + * Double-checked locking: sample BUSY before and after enabling the interrupt + * to avoid missing the falling edge between the two checks. */ static lr11xx_hal_status_t wait_on_busy(struct lr11xx_hal_context *ctx) { - /* Fast path: already ready */ if (!gpio_pin_get_dt(&ctx->busy)) { return LR11XX_HAL_STATUS_OK; } @@ -58,8 +49,6 @@ static lr11xx_hal_status_t wait_on_busy(struct lr11xx_hal_context *ctx) k_sem_reset(&busy_sem); gpio_pin_interrupt_configure_dt(&ctx->busy, GPIO_INT_EDGE_TO_INACTIVE); - /* Re-check after enabling interrupt to close the race window where - * BUSY dropped between our first check and the interrupt enable. */ if (!gpio_pin_get_dt(&ctx->busy)) { gpio_pin_interrupt_configure_dt(&ctx->busy, GPIO_INT_DISABLE); return LR11XX_HAL_STATUS_OK; @@ -80,7 +69,9 @@ static lr11xx_hal_status_t wait_on_busy(struct lr11xx_hal_context *ctx) } /** - * @brief Check device ready, wake from sleep if needed + * @brief Ensure chip is ready; wake from sleep via NSS pulse if needed. + * + * Wakeup procedure per datasheet: assert NSS, hold ≥10µs, deassert, then wait BUSY low. */ static lr11xx_hal_status_t check_device_ready(struct lr11xx_hal_context *ctx) { @@ -88,24 +79,15 @@ static lr11xx_hal_status_t check_device_ready(struct lr11xx_hal_context *ctx) return wait_on_busy(ctx); } - /* Radio is sleeping - wake it with NSS pulse - * NSS is ACTIVE_LOW: logical 1 = physical LOW = asserted - */ - gpio_pin_set_dt(&ctx->nss, 1); /* Assert NSS (pull LOW) */ - k_busy_wait(10); - gpio_pin_set_dt(&ctx->nss, 0); /* Deassert NSS (release HIGH) */ + gpio_pin_set_dt(&ctx->nss, 1); + k_busy_wait(10); /* ≥10µs NSS hold for wakeup (datasheet §5.1) */ + gpio_pin_set_dt(&ctx->nss, 0); ctx->radio_is_sleeping = false; return wait_on_busy(ctx); } -/** - * @brief DIO1 GPIO interrupt callback (ISR context) - * - * Calls the user callback directly from ISR. The caller - * (lr11xx_lora.c) submits to its own dedicated work queue, - * which is ISR-safe via k_work_submit_to_queue(). - */ +/* DIO1 rising-edge ISR; forwards to user callback (must be ISR-safe) */ static void dio1_isr_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { @@ -118,8 +100,6 @@ static void dio1_isr_callback(const struct device *dev, struct gpio_callback *cb } } -/* Public HAL API - called by Semtech driver */ - int lr11xx_hal_init(struct lr11xx_hal_context *ctx) { int ret; @@ -127,32 +107,27 @@ int lr11xx_hal_init(struct lr11xx_hal_context *ctx) current_ctx = ctx; ctx->radio_is_sleeping = false; - /* Configure NSS as output, inactive (deselected). - * GPIO_OUTPUT_INACTIVE respects GPIO_ACTIVE_LOW from DTS: - * inactive = logical 0 = physical HIGH = chip deselected. */ + /* NSS idle = deselected; ACTIVE_LOW polarity handled by GPIO_OUTPUT_INACTIVE */ ret = gpio_pin_configure_dt(&ctx->nss, GPIO_OUTPUT_INACTIVE); if (ret < 0) { LOG_ERR("Failed to configure NSS: %d", ret); return ret; } - /* Configure RESET as output, inactive (not in reset). - * GPIO_OUTPUT_INACTIVE with GPIO_ACTIVE_LOW: - * inactive = logical 0 = physical HIGH = reset released. */ + /* RESET idle = released; ACTIVE_LOW polarity handled by GPIO_OUTPUT_INACTIVE */ ret = gpio_pin_configure_dt(&ctx->reset, GPIO_OUTPUT_INACTIVE); if (ret < 0) { LOG_ERR("Failed to configure RESET: %d", ret); return ret; } - /* Configure BUSY as input with interrupt support */ ret = gpio_pin_configure_dt(&ctx->busy, GPIO_INPUT); if (ret < 0) { LOG_ERR("Failed to configure BUSY: %d", ret); return ret; } - /* Set up BUSY interrupt callback (interrupt enabled on-demand by wait_on_busy) */ + /* BUSY interrupt enabled on-demand inside wait_on_busy() */ gpio_init_callback(&busy_gpio_cb, busy_isr_callback, BIT(ctx->busy.pin)); ret = gpio_add_callback(ctx->busy.port, &busy_gpio_cb); if (ret < 0) { @@ -160,14 +135,12 @@ int lr11xx_hal_init(struct lr11xx_hal_context *ctx) return ret; } - /* Configure DIO1 as input with interrupt */ ret = gpio_pin_configure_dt(&ctx->dio1, GPIO_INPUT); if (ret < 0) { LOG_ERR("Failed to configure DIO1: %d", ret); return ret; } - /* Set up DIO1 interrupt callback */ gpio_init_callback(&dio1_gpio_cb, dio1_isr_callback, BIT(ctx->dio1.pin)); ret = gpio_add_callback(ctx->dio1.port, &dio1_gpio_cb); if (ret < 0) { @@ -197,8 +170,6 @@ void lr11xx_hal_disable_dio1_irq(struct lr11xx_hal_context *ctx) gpio_pin_interrupt_configure_dt(&ctx->dio1, GPIO_INT_DISABLE); } -/* Semtech HAL interface implementation */ - lr11xx_hal_status_t lr11xx_hal_write(const void *context, const uint8_t *command, const uint16_t command_length, const uint8_t *data, const uint16_t data_length) @@ -217,7 +188,6 @@ lr11xx_hal_status_t lr11xx_hal_write(const void *context, const uint8_t *command return LR11XX_HAL_STATUS_ERROR; } - /* Build SPI transaction */ const struct spi_buf tx_bufs[] = { { .buf = (uint8_t *)command, .len = command_length }, { .buf = (uint8_t *)data, .len = data_length }, @@ -227,12 +197,8 @@ lr11xx_hal_status_t lr11xx_hal_write(const void *context, const uint8_t *command .count = (data_length > 0) ? 2 : 1, }; - /* Assert NSS (active LOW in DTS, so logical 1 = physical LOW = chip selected) */ gpio_pin_set_dt(&ctx->nss, 1); - ret = spi_write(ctx->spi_dev, &ctx->spi_cfg, &tx); - - /* Deassert NSS (logical 0 = physical HIGH = chip deselected) */ gpio_pin_set_dt(&ctx->nss, 0); if (ret < 0) { @@ -240,10 +206,10 @@ lr11xx_hal_status_t lr11xx_hal_write(const void *context, const uint8_t *command return LR11XX_HAL_STATUS_ERROR; } - /* Check for sleep command: opcode 0x011B */ + /* SetSleep opcode 0x011B — chip won't drive BUSY; track state and wait for entry */ if (command_length >= 2 && command[0] == 0x01 && command[1] == 0x1B) { ctx->radio_is_sleeping = true; - k_busy_wait(1000); /* 1ms for sleep transition */ + k_busy_wait(1000); /* ≥500µs for sleep entry per datasheet; 1ms for margin */ return LR11XX_HAL_STATUS_OK; } @@ -263,9 +229,9 @@ lr11xx_hal_status_t lr11xx_hal_read(const void *context, const uint8_t *command, } last_cmd_time = k_uptime_get(); - /* Special case: crypto restore command needs delay */ + /* Crypto engine restore (opcode 0x050B) requires delay before issuing command */ if (command_length >= 2 && command[0] == 0x05 && command[1] == 0x0B) { - k_busy_wait(1000); + k_busy_wait(1000); /* TODO: document required delay from datasheet */ } if (check_device_ready(ctx) != LR11XX_HAL_STATUS_OK) { @@ -273,14 +239,12 @@ lr11xx_hal_status_t lr11xx_hal_read(const void *context, const uint8_t *command, return LR11XX_HAL_STATUS_ERROR; } - /* Step 1: Write command */ + /* Phase 1: send command opcode */ const struct spi_buf tx_buf = { .buf = (uint8_t *)command, .len = command_length }; const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 }; - /* Assert NSS (active LOW in DTS, so logical 1 = physical LOW = chip selected) */ gpio_pin_set_dt(&ctx->nss, 1); ret = spi_write(ctx->spi_dev, &ctx->spi_cfg, &tx); - /* Deassert NSS */ gpio_pin_set_dt(&ctx->nss, 0); if (ret < 0) { @@ -292,23 +256,20 @@ lr11xx_hal_status_t lr11xx_hal_read(const void *context, const uint8_t *command, return wait_on_busy(ctx); } - /* Step 2: Wait for device ready, then read response */ + /* Phase 2: wait BUSY, then read response — LR11XX prepends one dummy status byte */ if (check_device_ready(ctx) != LR11XX_HAL_STATUS_OK) { return LR11XX_HAL_STATUS_ERROR; } - /* LR11xx returns 1 dummy byte + data */ uint8_t dummy; const struct spi_buf rx_bufs[] = { - { .buf = &dummy, .len = 1 }, + { .buf = &dummy, .len = 1 }, /* discard leading status byte */ { .buf = data, .len = data_length }, }; const struct spi_buf_set rx = { .buffers = rx_bufs, .count = 2 }; - /* Assert NSS */ gpio_pin_set_dt(&ctx->nss, 1); ret = spi_read(ctx->spi_dev, &ctx->spi_cfg, &rx); - /* Deassert NSS */ gpio_pin_set_dt(&ctx->nss, 0); if (ret < 0) { @@ -332,10 +293,8 @@ lr11xx_hal_status_t lr11xx_hal_direct_read(const void *context, uint8_t *data, const struct spi_buf rx_buf = { .buf = data, .len = data_length }; const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 }; - /* Assert NSS (active LOW in DTS, so logical 1 = physical LOW = chip selected) */ gpio_pin_set_dt(&ctx->nss, 1); ret = spi_read(ctx->spi_dev, &ctx->spi_cfg, &rx); - /* Deassert NSS */ gpio_pin_set_dt(&ctx->nss, 0); if (ret < 0) { @@ -350,32 +309,17 @@ lr11xx_hal_status_t lr11xx_hal_reset(const void *context) { struct lr11xx_hal_context *ctx = (struct lr11xx_hal_context *)context; - LOG_INF("LR11xx reset: assert reset, hold 10ms"); + LOG_INF("LR11xx reset"); - /* Reset pin is ACTIVE_LOW in DTS: - * gpio_pin_set_dt(..., 1) = logical assert = physical LOW = reset active - * gpio_pin_set_dt(..., 0) = logical deassert = physical HIGH = reset released - * - * RadioLib sequence (LR_common.cpp): - * 1. digitalWrite(rst, LOW) - assert reset - * 2. delay(10) - 10ms hold - * 3. digitalWrite(rst, HIGH) - release reset - * 4. delay(300) - wait for firmware (datasheet: 273ms typical) - * 5. wait for BUSY low - */ - gpio_pin_set_dt(&ctx->reset, 1); /* Assert reset (pull LOW) */ - k_msleep(10); /* 10ms hold in reset (matches RadioLib) */ + gpio_pin_set_dt(&ctx->reset, 1); + k_msleep(10); /* ≥100µs reset pulse required (datasheet §5.1); 10ms for margin */ - gpio_pin_set_dt(&ctx->reset, 0); /* Deassert reset (release to HIGH) */ - - /* Wait 300ms for internal LR11xx firmware (RadioLib uses 300ms, datasheet 273ms) */ - k_msleep(300); + gpio_pin_set_dt(&ctx->reset, 0); + k_msleep(300); /* Firmware boot ≤273ms (datasheet); 300ms for margin */ LOG_INF("LR11xx reset complete, BUSY=%d", gpio_pin_get_dt(&ctx->busy)); ctx->radio_is_sleeping = false; - - /* Wait for BUSY to go low - chip is ready */ return wait_on_busy(ctx); } diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h index 66ea98f..72a6701 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h +++ b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h @@ -1,9 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * LR11xx HAL implementation for Zephyr - ZephCore - * - * Simplified HAL that uses direct SPI/GPIO access without Zephyr device model. - * Based on IRNAS/SWDR001-Zephyr but adapted for ZephCore's simpler needs. + * LR11xx HAL for Zephyr — ZephCore */ #ifndef LR11XX_HAL_ZEPHYR_H @@ -21,76 +18,53 @@ extern "C" { #include "lr11xx_hal.h" /** - * @brief LR11xx HAL context for Zephyr + * @brief HAL context passed as 'context' to all lr11xx_hal_* functions. * - * This structure is passed as the 'context' parameter to all lr11xx_hal_* functions. - * It contains all the hardware configuration needed to communicate with the radio. + * NSS is software-controlled (not via SPI CS) to allow the two-phase read + * protocol (write command / deassert / reassert / read response). * - * CRITICAL: All SPI operations are protected by spi_mutex. The LR1110 radio is - * accessed from two threads: - * 1. Main thread: mesh event loop (noise floor calibration, TX, reconfigure) - * 2. Dedicated DIO1 work queue: interrupt handler (RX packet processing) - * Without the mutex, concurrent SPI access corrupts the LR1110 command/response - * protocol, causing the BUSY pin to get stuck HIGH permanently. + * The LR1110 is accessed from two threads (main mesh loop and DIO1 work queue); + * callers must serialize access externally. Concurrent SPI access corrupts the + * command/response protocol and stalls BUSY HIGH permanently. */ struct lr11xx_hal_context { - /* SPI device */ const struct device *spi_dev; struct spi_config spi_cfg; - /* GPIO pins */ - struct gpio_dt_spec nss; /* Chip select (directly controlled, not via SPI CS) */ - struct gpio_dt_spec reset; /* Reset pin (active low) */ - struct gpio_dt_spec busy; /* Busy pin (high = busy) */ - struct gpio_dt_spec dio1; /* DIO1 interrupt pin */ + struct gpio_dt_spec nss; /* chip select, software-controlled, ACTIVE_LOW in DTS */ + struct gpio_dt_spec reset; /* hardware reset, ACTIVE_LOW in DTS */ + struct gpio_dt_spec busy; /* busy indicator, high = chip processing */ + struct gpio_dt_spec dio1; /* IRQ output from chip */ - /* Optional pins */ - struct gpio_dt_spec dio2; /* DIO2 (often RF switch control) */ - struct gpio_dt_spec rxen; /* RX enable (for external PA/LNA) */ - struct gpio_dt_spec txen; /* TX enable (for external PA/LNA) */ + struct gpio_dt_spec dio2; /* optional: RF switch or second IRQ */ + struct gpio_dt_spec rxen; /* optional: external LNA enable */ + struct gpio_dt_spec txen; /* optional: external PA enable */ - /* TCXO config */ - uint16_t tcxo_voltage_mv; /* 0 = no TCXO, else voltage in mV (e.g., 1600 = 1.6V) */ - uint32_t tcxo_startup_us; /* TCXO startup time in microseconds */ + uint16_t tcxo_voltage_mv; /* 0 = XTAL, non-zero = TCXO supply in mV */ + uint32_t tcxo_startup_us; /* TCXO startup time; passed to SetTcxoMode timeout */ - /* State tracking */ volatile bool radio_is_sleeping; }; /** - * @brief Initialize the HAL context GPIOs + * @brief Configure GPIOs and register interrupt callbacks. Call before any HAL function. * - * Must be called before any other HAL functions. - * - * @param ctx HAL context with gpio specs already filled in + * @param ctx HAL context with gpio specs filled in * @return 0 on success, negative errno on failure */ int lr11xx_hal_init(struct lr11xx_hal_context *ctx); -/** - * @brief GPIO callback type for DIO1 interrupt - */ +/** @brief DIO1 interrupt callback type; invoked directly from GPIO ISR — must be ISR-safe */ typedef void (*lr11xx_dio1_callback_t)(void *user_data); /** - * @brief Set DIO1 interrupt callback - * - * @param ctx HAL context - * @param cb Callback function (called directly from GPIO ISR context — - * must be ISR-safe, e.g. k_work_submit_to_queue()) - * @param user_data User data passed to callback + * @brief Register DIO1 rising-edge callback. + * @param cb Must be ISR-safe (e.g., submit to a k_work_q, not block) */ void lr11xx_hal_set_dio1_callback(struct lr11xx_hal_context *ctx, lr11xx_dio1_callback_t cb, void *user_data); -/** - * @brief Enable DIO1 interrupt - */ void lr11xx_hal_enable_dio1_irq(struct lr11xx_hal_context *ctx); - -/** - * @brief Disable DIO1 interrupt - */ void lr11xx_hal_disable_dio1_irq(struct lr11xx_hal_context *ctx); #ifdef __cplusplus diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_radio.c b/zephcore/adapters/radio/lr11xx/lr11xx_radio.c index e191510..3337a65 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_radio.c +++ b/zephcore/adapters/radio/lr11xx/lr11xx_radio.c @@ -95,9 +95,7 @@ #define LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH ( 2 + 1 ) #define LR11XX_RADIO_GET_LORA_RX_INFO_CMD_LENGTH ( 2 ) -/** - * @brief Internal RTC frequency - */ +/** @brief RTC clock frequency in Hz; timeout_ms = steps / 32.768 (SWDR001 §3.3) */ #define LR11XX_RTC_FREQ_IN_HZ 32768UL /* @@ -274,9 +272,11 @@ lr11xx_status_t lr11xx_radio_get_gfsk_pkt_status( const void* context, lr11xx_ra if( status == LR11XX_STATUS_OK ) { + /* RSSI encoding: raw = -2 * dBm (LR11XX SWDR001 §5.8.4) */ pkt_status->rssi_sync_in_dbm = -( int8_t ) ( rbuffer[0] >> 1 ); pkt_status->rssi_avg_in_dbm = -( int8_t ) ( rbuffer[1] >> 1 ); pkt_status->rx_len_in_bytes = rbuffer[2]; + /* Status byte 3 bit field layout per SWDR001 §5.8.4 */ pkt_status->is_addr_err = ( ( rbuffer[3] & 0x20 ) != 0 ) ? true : false; pkt_status->is_crc_err = ( ( rbuffer[3] & 0x10 ) != 0 ) ? true : false; pkt_status->is_len_err = ( ( rbuffer[3] & 0x08 ) != 0 ) ? true : false; @@ -301,8 +301,9 @@ lr11xx_status_t lr11xx_radio_get_lora_pkt_status( const void* context, lr11xx_ra if( status == LR11XX_STATUS_OK ) { + /* RSSI encoding: raw = -2 * dBm; SNR encoding: raw = 4 * dB (SWDR001 §5.8.5) */ pkt_status->rssi_pkt_in_dbm = -( int8_t ) ( rbuffer[0] >> 1 ); - pkt_status->snr_pkt_in_db = ( ( ( int8_t ) rbuffer[1] ) + 2 ) >> 2; + pkt_status->snr_pkt_in_db = ( ( ( int8_t ) rbuffer[1] ) + 2 ) >> 2; /* round to nearest dB */ pkt_status->signal_rssi_pkt_in_dbm = -( int8_t ) ( rbuffer[2] >> 1 ); } @@ -322,7 +323,7 @@ lr11xx_status_t lr11xx_radio_get_rssi_inst( const void* context, int8_t* rssi_in if( status == LR11XX_STATUS_OK ) { - *rssi_in_dbm = -( int8_t ) ( rssi >> 1 ); + *rssi_in_dbm = -( int8_t ) ( rssi >> 1 ); /* raw = -2 * dBm (SWDR001 §5.8.3) */ } return status; @@ -371,6 +372,9 @@ lr11xx_status_t lr11xx_radio_set_lora_sync_word( const void* context, const uint lr11xx_status_t lr11xx_radio_set_lora_public_network( const void* context, const lr11xx_radio_lora_network_type_t network_type ) { + /* Deprecated — use lr11xx_radio_set_lora_sync_word for FW ≥ 0x303. + * cbuffer is oversized (LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_CMD_LENGTH = 10) + * but only 3 bytes are transmitted (opcode + 1 param), matching actual protocol. */ const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_CMD_LENGTH] = { ( uint8_t ) ( LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 8 ), ( uint8_t ) ( LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 0 ), @@ -1062,8 +1066,7 @@ uint32_t lr11xx_radio_get_lora_time_on_air_in_ms( const lr11xx_radio_pkt_params_ { uint32_t numerator = 1000U * lr11xx_radio_get_lora_time_on_air_numerator( pkt_p, mod_p ); uint32_t denominator = lr11xx_radio_get_lora_bw_in_hz( mod_p->bw ); - // Perform integral ceil() - return ( numerator + denominator - 1 ) / denominator; + return ( numerator + denominator - 1 ) / denominator; /* integer ceil */ } uint32_t lr11xx_radio_get_gfsk_time_on_air_numerator( const lr11xx_radio_pkt_params_gfsk_t* pkt_p ) @@ -1105,9 +1108,7 @@ uint32_t lr11xx_radio_get_gfsk_time_on_air_in_ms( const lr11xx_radio_pkt_params_ { uint32_t numerator = 1000U * lr11xx_radio_get_gfsk_time_on_air_numerator( pkt_p ); uint32_t denominator = mod_p->br_in_bps; - - // Perform integral ceil() - return ( numerator + denominator - 1 ) / denominator; + return ( numerator + denominator - 1 ) / denominator; /* integer ceil */ } uint32_t lr11xx_radio_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ) @@ -1128,6 +1129,7 @@ lr11xx_status_t lr11xx_radio_get_lora_rx_info( const void* context, bool* is_crc if( status == LR11XX_STATUS_OK ) { + /* Response byte layout per SWDR001: bit[4] = CRC present, bits[2:0] = CR */ *is_crc_present = ( ( ( rbuffer & ( 0x01 << 4 ) ) != 0 ) ) ? true : false; *cr = ( lr11xx_radio_lora_cr_t ) ( rbuffer & 0x07 ); } @@ -1137,6 +1139,8 @@ lr11xx_status_t lr11xx_radio_get_lora_rx_info( const void* context, bool* is_crc lr11xx_status_t lr11xx_radio_apply_high_acp_workaround( const void* context ) { + /* Clear bit 30 of internal PA register 0x00F30054 to reduce adjacent channel power. + * Required before SetRx/SetTx/SetCad on affected silicon (Semtech appnote). */ return lr11xx_regmem_write_regmem32_mask( context, 0x00F30054, 1 << 30, 0 << 30 ); } diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_radio_timings.c b/zephcore/adapters/radio/lr11xx/lr11xx_radio_timings.c index a27eeeb..92a6a6a 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_radio_timings.c +++ b/zephcore/adapters/radio/lr11xx/lr11xx_radio_timings.c @@ -50,14 +50,10 @@ * --- PRIVATE CONSTANTS ------------------------------------------------------- */ -/** - * @brief Time in microsecond taken by the chip to process the Rx done interrupt - */ +/** @brief Chip processing delay from last bit received to RxDone IRQ assertion (SWDR001 §6.1) */ #define RX_DONE_IRQ_PROCESSING_TIME_IN_US 74 -/** - * @brief Time in microsecond taken by the chip to process the Tx done interrupt - */ +/** @brief Chip processing delay from last bit transmitted to TxDone IRQ assertion (SWDR001 §6.1) */ #define TX_DONE_IRQ_PROCESSING_TIME_IN_US 111 /* @@ -201,27 +197,20 @@ static uint32_t lr11xx_radio_timings_get_pa_ramp_time_in_us( const lr11xx_radio_ } } +/* RX input pipeline delay by bandwidth (SWDR001 §6.1, characterised values) */ static uint32_t lr11xx_radio_timings_get_lora_rx_input_delay_in_us( lr11xx_radio_lora_bw_t bw ) { switch( bw ) { case LR11XX_RADIO_LORA_BW_500: - { - return 16; - } + return 16; /* 500 kHz: 16µs */ case LR11XX_RADIO_LORA_BW_250: - { - return 31; - } + return 31; /* 250 kHz: 31µs */ case LR11XX_RADIO_LORA_BW_125: - { - return 57; - } + return 57; /* 125 kHz: 57µs */ default: - { return 0; } - } } static uint32_t lr11xx_radio_timings_get_lora_symb_time_in_us( const lr11xx_radio_lora_sf_t sf, diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_radio_types.h b/zephcore/adapters/radio/lr11xx/lr11xx_radio_types.h index 6f5c58d..57b09fc 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_radio_types.h +++ b/zephcore/adapters/radio/lr11xx/lr11xx_radio_types.h @@ -204,7 +204,7 @@ typedef enum */ typedef enum { - LR11XX_RADIO_GFSK_CRC_OFF = 0x01, //!< CRC check deactivated + LR11XX_RADIO_GFSK_CRC_OFF = 0x01, //!< CRC disabled (non-zero by design — SWDR001 §5.6) LR11XX_RADIO_GFSK_CRC_1_BYTE = 0x00, LR11XX_RADIO_GFSK_CRC_2_BYTES = 0x02, LR11XX_RADIO_GFSK_CRC_1_BYTE_INV = 0x04, @@ -360,8 +360,8 @@ typedef enum typedef enum { LR11XX_RADIO_CAD_EXIT_MODE_STANDBYRC = 0x00, //!< Enter standby RC mode after CAD operation - LR11XX_RADIO_CAD_EXIT_MODE_RX = 0x01, //!< Enter in RX mode if an activity is detected - LR11XX_RADIO_CAD_EXIT_MODE_TX = 0x10, //!< Enter in TX mode if no activity is detected + LR11XX_RADIO_CAD_EXIT_MODE_RX = 0x01, //!< Enter RX if activity detected + LR11XX_RADIO_CAD_EXIT_MODE_TX = 0x10, //!< Enter TX if no activity detected (0x10, not 0x02 — SWDR001 §5.7) } lr11xx_radio_cad_exit_mode_t; /*! diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_regmem.c b/zephcore/adapters/radio/lr11xx/lr11xx_regmem.c index 3c5312a..688ad44 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_regmem.c +++ b/zephcore/adapters/radio/lr11xx/lr11xx_regmem.c @@ -59,7 +59,7 @@ #define LR11XX_REGMEM_READ_BUFFER8_CMD_LENGTH ( 2 + 2 ) #define LR11XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH ( 2 + 4 + 4 + 4 ) -#define LR11XX_REGMEM_BUFFER_SIZE_MAX ( 256 ) +#define LR11XX_REGMEM_BUFFER_SIZE_MAX ( 256 ) /* max payload per SPI transaction (SWDR001 §3.1) */ /* * ----------------------------------------------------------------------------- @@ -91,54 +91,22 @@ enum * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- */ -/*! - * @brief Helper function that fill both cbuffer with opcode and memory address - * - * It is typically used in read/write regmem32 functions. - * - * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! - */ +/* Caller must ensure cbuffer has capacity for opcode (2 bytes) + address (4 bytes) */ static void lr11xx_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ); -/*! - * @brief Helper function that fill both cbuffer with opcode memory address, and data length to read - * - * It is typically used in read functions. - * - * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! - */ +/* Caller must ensure cbuffer has capacity for opcode (2 bytes) + address (4 bytes) + length (1 byte) */ static void lr11xx_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, uint8_t length ); -/*! - * @brief Helper function that fill both cbuffer with data - * - * It is typically used in write write regmem32 functions. - * - * @warning It is up to the caller to ensure cdata is big enough to contain all data! - */ +/* Serialise uint32_t array to big-endian bytes; caller ensures cdata is data_length * 4 bytes */ static void lr11xx_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ); -/*! - * @brief Helper function that fill both cbuffer and cdata buffers with opcode, memory address and data - * - * It is typically used to factorize and write regmem32 operations. Behind the scene it calls the other helpers - * lr11xx_regmem_fill_cbuffer_opcode_address and lr11xx_regmem_fill_cdata. - * - * @warning It is up to the caller to ensure cbuffer and cdata are big enough to contain their respective information! - */ +/* Fills cbuffer (opcode + address) and cdata (serialised words) for a write command */ static void lr11xx_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, uint16_t opcode, uint32_t address, const uint32_t* data, uint8_t data_length ); -/*! - * @brief Helper function that convert an array of uint8_t into an array of uint32_t - * - * Typically used in the read function returning uint32_t array. - * - * @warning It is up to the caller to ensure the raw_buffer is of length at least "out_buffer_length * - * sizeof(uint32_t)"! - */ +/* Deserialise big-endian raw_buffer into out_buffer; raw_buffer must be out_buffer_length * 4 bytes */ static void lr11xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, uint8_t out_buffer_length ); diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_system.c b/zephcore/adapters/radio/lr11xx/lr11xx_system.c index cc7f372..91385ff 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_system.c +++ b/zephcore/adapters/radio/lr11xx/lr11xx_system.c @@ -75,12 +75,12 @@ #define LR11XX_SYSTEM_READ_UID_CMD_LENGTH ( 2 ) #define LR11XX_SYSTEM_READ_JOIN_EUI_CMD_LENGTH ( 2 ) #define LR11XX_SYSTEM_READ_PIN_CMD_LENGTH ( 2 ) -#define LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH ( LR11XX_SYSTEM_READ_PIN_CMD_LENGTH + 17 ) +#define LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH ( LR11XX_SYSTEM_READ_PIN_CMD_LENGTH + 17 ) /* +8 device_eui +8 join_eui +1 rfu */ #define LR11XX_SYSTEM_GET_RANDOM_CMD_LENGTH ( 2 ) -#define LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH ( 3 ) -#define LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH ( 3 ) +#define LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH ( 3 ) /* 2 opcode + 1 param */ +#define LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH ( 3 ) /* 2 opcode + 1 param */ -#define LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ( 6 ) +#define LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ( 6 ) /* stat1 + stat2 + 4-byte IRQ mask */ /* * ----------------------------------------------------------------------------- @@ -131,20 +131,7 @@ enum * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- */ -/*! - * @brief Fill stat1 structure with data from stat1_byte - * - * @param [in] stat1_byte stat1 byte - * @param [out] stat1 stat1 structure - */ static void lr11xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr11xx_system_stat1_t* stat1 ); - -/*! - * @brief Fill stat2 structure with data from stat2_byte - * - * @param [in] stat2_byte stat2 byte - * @param [out] stat2 stat2 structure - */ static void lr11xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr11xx_system_stat2_t* stat2 ); /* @@ -277,10 +264,9 @@ lr11xx_status_t lr11xx_system_calibrate_image( const void* context, const uint8_ lr11xx_status_t lr11xx_system_calibrate_image_in_mhz( const void* context, const uint16_t freq1_in_mhz, const uint16_t freq2_in_mhz ) { - // Perform a floor() to get a value for freq1 corresponding to a frequency lower than or equal to freq1_in_mhz + /* Floor: freq1 step ≤ freq1_in_mhz */ const uint8_t freq1 = freq1_in_mhz / LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; - - // Perform a ceil() to get a value for freq2 corresponding to a frequency higher than or equal to freq2_in_mhz + /* Ceil: freq2 step ≥ freq2_in_mhz */ const uint8_t freq2 = ( freq2_in_mhz + LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ - 1 ) / LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; @@ -391,7 +377,7 @@ lr11xx_status_t lr11xx_system_reboot( const void* context, const bool stay_in_bo const uint8_t cbuffer[LR11XX_SYSTEM_REBOOT_CMD_LENGTH] = { ( uint8_t ) ( LR11XX_SYSTEM_REBOOT_OC >> 8 ), ( uint8_t ) ( LR11XX_SYSTEM_REBOOT_OC >> 0 ), - ( stay_in_bootloader == true ) ? 0x03 : 0x00, + ( stay_in_bootloader == true ) ? 0x03 : 0x00, /* 0x03 = bootloader, 0x00 = flash (SWDR001 §3.2) */ }; return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_REBOOT_CMD_LENGTH, 0, 0 ); @@ -433,7 +419,7 @@ lr11xx_status_t lr11xx_system_set_sleep( const void* context, const lr11xx_syste const uint8_t cbuffer[LR11XX_SYSTEM_SET_SLEEP_CMD_LENGTH] = { ( uint8_t ) ( LR11XX_SYSTEM_SET_SLEEP_OC >> 8 ), ( uint8_t ) ( LR11XX_SYSTEM_SET_SLEEP_OC >> 0 ), - ( sleep_cfg.is_rtc_timeout << 1 ) + sleep_cfg.is_warm_start, + ( sleep_cfg.is_rtc_timeout << 1 ) + sleep_cfg.is_warm_start, /* bit[1]=RTC, bit[0]=warm start */ ( uint8_t ) ( sleep_time >> 24 ), ( uint8_t ) ( sleep_time >> 16 ), ( uint8_t ) ( sleep_time >> 8 ), @@ -637,6 +623,7 @@ lr11xx_status_t lr11xx_system_drive_dio_in_sleep_mode( const void* context, bool * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- */ +/* Stat1 byte layout per SWDR001: bit[0] = IRQ active, bits[2:1] = cmd_status */ static void lr11xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr11xx_system_stat1_t* stat1 ) { if( stat1 != NULL ) @@ -646,6 +633,7 @@ static void lr11xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr11xx } } +/* Stat2 byte layout per SWDR001: bit[0] = flash, bits[3:1] = chip_mode, bits[7:4] = reset_status */ static void lr11xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr11xx_system_stat2_t* stat2 ) { if( stat2 != NULL ) diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_system.h b/zephcore/adapters/radio/lr11xx/lr11xx_system.h index 0142689..d279c21 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_system.h +++ b/zephcore/adapters/radio/lr11xx/lr11xx_system.h @@ -57,11 +57,7 @@ extern "C" { * --- PUBLIC CONSTANTS -------------------------------------------------------- */ -/*! - * @brief Frequency step in MHz used to compute the image calibration parameter - * - * @see lr11xx_system_calibrate_image_in_mhz - */ +/** @brief Image calibration frequency resolution — chip accepts frequency in 4 MHz steps (SWDR001 §4.3) */ #define LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ 4 /* diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_system_types.h b/zephcore/adapters/radio/lr11xx/lr11xx_system_types.h index 0f2744b..16a8e9d 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_system_types.h +++ b/zephcore/adapters/radio/lr11xx/lr11xx_system_types.h @@ -57,19 +57,12 @@ extern "C" { * --- PUBLIC CONSTANTS -------------------------------------------------------- */ -/*! - * @brief Length in byte of the LR11XX version blob - */ +/** @brief Version response length: hw(1) + type(1) + fw(2) */ #define LR11XX_SYSTEM_VERSION_LENGTH ( 4 ) -/*! - * @brief Length of the LR11XX Unique Identifier in bytes - * - * The LR11XX Unique Identifiers is an 8 byte long buffer - */ -#define LR11XX_SYSTEM_UID_LENGTH ( 8 ) -#define LR11XX_SYSTEM_JOIN_EUI_LENGTH ( 8 ) -#define LR11XX_SYSTEM_PIN_LENGTH ( 4 ) +#define LR11XX_SYSTEM_UID_LENGTH ( 8 ) /**< Device unique identifier, 8 bytes */ +#define LR11XX_SYSTEM_JOIN_EUI_LENGTH ( 8 ) /**< LoRaWAN Join EUI, 8 bytes */ +#define LR11XX_SYSTEM_PIN_LENGTH ( 4 ) /**< Provisioning PIN, 4 bytes */ /* * ----------------------------------------------------------------------------- diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_types.h b/zephcore/adapters/radio/lr11xx/lr11xx_types.h index be52d7a..349d98c 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_types.h +++ b/zephcore/adapters/radio/lr11xx/lr11xx_types.h @@ -50,7 +50,7 @@ * --- PUBLIC CONSTANTS -------------------------------------------------------- */ -#define LR11XX_CMD_LENGTH_MAX ( 512 ) +#define LR11XX_CMD_LENGTH_MAX ( 512 ) /* maximum SPI command frame length in bytes (SWDR001 §3.1) */ /* * ----------------------------------------------------------------------------- @@ -63,7 +63,7 @@ typedef enum lr11xx_status_e { LR11XX_STATUS_OK = 0, - LR11XX_STATUS_ERROR = 3, + LR11XX_STATUS_ERROR = 3, /* matches lr11xx_hal_status_t ERROR value */ } lr11xx_status_t; /* diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_hal.h b/zephcore/adapters/radio/lr20xx/lr20xx_hal.h index 4bf1f1a..d922b46 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_hal.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_hal.h @@ -67,7 +67,7 @@ extern "C" { typedef enum lr20xx_hal_status_e { LR20XX_HAL_STATUS_OK = 0, - LR20XX_HAL_STATUS_ERROR = 3, + LR20XX_HAL_STATUS_ERROR = 3, /* Must match lr20xx_status_t ERROR value */ } lr20xx_hal_status_t; /* diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.c b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.c index 83b988f..c4c2732 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.c +++ b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.c @@ -11,10 +11,7 @@ #include LOG_MODULE_REGISTER(lr20xx_hal, CONFIG_LORA_LOG_LEVEL); -/* Timeout for busy wait in milliseconds. - * LR2021 should respond within a few ms after most commands. - * After reset, firmware boot can take up to ~300ms. - * Use 3000ms to be safe. */ +/* BUSY timeout; covers worst-case post-reset firmware boot (~300ms) with margin */ #define LR20XX_BUSY_TIMEOUT_MS 3000 /* Static state for DIO1 interrupt handling */ @@ -40,15 +37,11 @@ static uint16_t last_opcode; static int64_t last_cmd_time; /** - * @brief Wait until BUSY pin goes low or timeout. - * - * Uses GPIO interrupt + semaphore instead of polling. The CPU sleeps - * while waiting, saving power during long BUSY periods (reset, firmware - * commands). Fast-path returns immediately when already ready. + * @brief Wait until BUSY deasserts, using GPIO IRQ + semaphore (not polling). + * Fast-path returns immediately if already idle. */ static lr20xx_hal_status_t wait_on_busy(struct lr20xx_hal_context *ctx) { - /* Fast path: already ready */ if (!gpio_pin_get_dt(&ctx->busy)) { return LR20XX_HAL_STATUS_OK; } @@ -56,8 +49,8 @@ static lr20xx_hal_status_t wait_on_busy(struct lr20xx_hal_context *ctx) k_sem_reset(&busy_sem); gpio_pin_interrupt_configure_dt(&ctx->busy, GPIO_INT_EDGE_TO_INACTIVE); - /* Re-check after enabling interrupt to close the race window where - * BUSY dropped between our first check and the interrupt enable. */ + /* Re-check after arming IRQ to close the race: BUSY may have dropped + * between the first read and the interrupt enable. */ if (!gpio_pin_get_dt(&ctx->busy)) { gpio_pin_interrupt_configure_dt(&ctx->busy, GPIO_INT_DISABLE); return LR20XX_HAL_STATUS_OK; @@ -78,7 +71,7 @@ static lr20xx_hal_status_t wait_on_busy(struct lr20xx_hal_context *ctx) } /** - * @brief Check device ready, wake from sleep if needed. + * @brief Assert ready; if sleeping, issue NSS wake pulse first. */ static lr20xx_hal_status_t check_device_ready(struct lr20xx_hal_context *ctx) { @@ -86,19 +79,15 @@ static lr20xx_hal_status_t check_device_ready(struct lr20xx_hal_context *ctx) return wait_on_busy(ctx); } - /* Radio is sleeping — wake with NSS pulse. - * NSS is ACTIVE_LOW: logical 1 = physical LOW = asserted. */ - gpio_pin_set_dt(&ctx->nss, 1); /* Assert NSS (pull LOW) */ - k_busy_wait(10); - gpio_pin_set_dt(&ctx->nss, 0); /* Deassert NSS (release HIGH) */ + /* Wake from sleep: NSS pulse ≥10us per LR2021 datasheet §5.4.2 */ + gpio_pin_set_dt(&ctx->nss, 1); + k_busy_wait(10); /* ≥10us NSS hold; k_busy_wait unit is microseconds */ + gpio_pin_set_dt(&ctx->nss, 0); ctx->radio_is_sleeping = false; return wait_on_busy(ctx); } -/** - * @brief DIO1 GPIO interrupt callback (ISR context) - */ static void dio1_isr_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { @@ -111,40 +100,31 @@ static void dio1_isr_callback(const struct device *dev, struct gpio_callback *cb } } -/* Public HAL API - called by Semtech driver */ - int lr20xx_hal_init(struct lr20xx_hal_context *ctx) { int ret; ctx->radio_is_sleeping = false; - /* Configure NSS as output, inactive (deselected). - * GPIO_OUTPUT_INACTIVE with GPIO_ACTIVE_LOW: - * inactive = logical 0 = physical HIGH = chip deselected. */ ret = gpio_pin_configure_dt(&ctx->nss, GPIO_OUTPUT_INACTIVE); if (ret < 0) { LOG_ERR("Failed to configure NSS: %d", ret); return ret; } - /* Configure RESET as output, inactive (not in reset). - * GPIO_OUTPUT_INACTIVE with GPIO_ACTIVE_LOW: - * inactive = logical 0 = physical HIGH = reset released. */ ret = gpio_pin_configure_dt(&ctx->reset, GPIO_OUTPUT_INACTIVE); if (ret < 0) { LOG_ERR("Failed to configure RESET: %d", ret); return ret; } - /* Configure BUSY as input */ ret = gpio_pin_configure_dt(&ctx->busy, GPIO_INPUT); if (ret < 0) { LOG_ERR("Failed to configure BUSY: %d", ret); return ret; } - /* Set up BUSY interrupt callback (interrupt enabled on-demand by wait_on_busy) */ + /* BUSY IRQ enabled on-demand by wait_on_busy() */ gpio_init_callback(&busy_gpio_cb, busy_isr_callback, BIT(ctx->busy.pin)); ret = gpio_add_callback(ctx->busy.port, &busy_gpio_cb); if (ret < 0) { @@ -152,14 +132,12 @@ int lr20xx_hal_init(struct lr20xx_hal_context *ctx) return ret; } - /* Configure DIO1 as input */ ret = gpio_pin_configure_dt(&ctx->dio1, GPIO_INPUT); if (ret < 0) { LOG_ERR("Failed to configure DIO1: %d", ret); return ret; } - /* Set up DIO1 interrupt callback */ gpio_init_callback(&dio1_gpio_cb, dio1_isr_callback, BIT(ctx->dio1.pin)); ret = gpio_add_callback(ctx->dio1.port, &dio1_gpio_cb); if (ret < 0) { @@ -189,8 +167,6 @@ void lr20xx_hal_disable_dio1_irq(struct lr20xx_hal_context *ctx) gpio_pin_interrupt_configure_dt(&ctx->dio1, GPIO_INT_DISABLE); } -/* Semtech HAL interface implementation */ - lr20xx_hal_status_t lr20xx_hal_write(const void *context, const uint8_t *command, const uint16_t command_length, const uint8_t *data, const uint16_t data_length) @@ -218,10 +194,8 @@ lr20xx_hal_status_t lr20xx_hal_write(const void *context, const uint8_t *command .count = (data_length > 0) ? 2 : 1, }; - /* Assert NSS (active LOW: logical 1 = physical LOW = chip selected) */ gpio_pin_set_dt(&ctx->nss, 1); ret = spi_write(ctx->spi_dev, &ctx->spi_cfg, &tx); - /* Deassert NSS (logical 0 = physical HIGH = chip deselected) */ gpio_pin_set_dt(&ctx->nss, 0); if (ret < 0) { @@ -229,10 +203,10 @@ lr20xx_hal_status_t lr20xx_hal_write(const void *context, const uint8_t *command return LR20XX_HAL_STATUS_ERROR; } - /* Check for sleep command: opcode 0x0127 (LR2021 SetSleep) */ + /* Opcode 0x0127 = SetSleep (LR2021 datasheet §5.4.2) */ if (command_length >= 2 && command[0] == 0x01 && command[1] == 0x27) { ctx->radio_is_sleeping = true; - k_busy_wait(1000); /* 1ms for sleep transition */ + k_busy_wait(1000); /* ≥500us sleep entry per datasheet §5.4.2 */ return LR20XX_HAL_STATUS_OK; } @@ -257,7 +231,6 @@ lr20xx_hal_status_t lr20xx_hal_read(const void *context, const uint8_t *command, return LR20XX_HAL_STATUS_ERROR; } - /* Step 1: Write command */ const struct spi_buf tx_buf = { .buf = (uint8_t *)command, .len = command_length }; const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1 }; @@ -274,13 +247,11 @@ lr20xx_hal_status_t lr20xx_hal_read(const void *context, const uint8_t *command, return wait_on_busy(ctx); } - /* Step 2: Wait for device ready, then read response */ if (check_device_ready(ctx) != LR20XX_HAL_STATUS_OK) { return LR20XX_HAL_STATUS_ERROR; } - /* LR2021 returns 2-byte Stat (16-bit) before response data. - * LR11xx had 1 stat byte — LR2021 datasheet §5.4.1.2 says 16-bit. */ + /* LR2021 prepends 2-byte stat header before response data (datasheet §5.4.1.2) */ uint8_t dummy[2]; const struct spi_buf rx_bufs[] = { { .buf = dummy, .len = sizeof(dummy) }, @@ -338,11 +309,8 @@ lr20xx_hal_status_t lr20xx_hal_direct_read_fifo(const void *context, return LR20XX_HAL_STATUS_ERROR; } - /* One-step FIFO read: write command + receive data in a single NSS - * assertion. MOSI carries the command during the first phase, then - * zeros (NULL buf) during the data phase. MISO is discarded (NULL) - * during the command phase, then captured into data during the data - * phase. The nRF SPIM sends 0x00 bytes when TX buf is NULL. */ + /* Single NSS assertion: command on MOSI, data on MISO, overlapped. + * NULL tx buf causes nRF SPIM to send 0x00 during data phase. */ const struct spi_buf tx_bufs[] = { { .buf = (uint8_t *)command, .len = command_length }, { .buf = NULL, .len = data_length }, @@ -372,16 +340,12 @@ lr20xx_hal_status_t lr20xx_hal_reset(const void *context) LOG_INF("LR20xx reset: assert reset, hold 10ms"); - /* Reset pin is ACTIVE_LOW in DTS: - * gpio_pin_set_dt(..., 1) = logical assert = physical LOW = reset active - * gpio_pin_set_dt(..., 0) = logical deassert = physical HIGH = reset released */ - gpio_pin_set_dt(&ctx->reset, 1); /* Assert reset */ - k_msleep(10); + gpio_pin_set_dt(&ctx->reset, 1); + k_msleep(10); /* ≥100us reset pulse per LR2021 datasheet §5.1 */ - gpio_pin_set_dt(&ctx->reset, 0); /* Deassert reset */ + gpio_pin_set_dt(&ctx->reset, 0); - /* Wait 300ms for internal LR20xx firmware boot */ - k_msleep(300); + k_msleep(300); /* LR2021 firmware boot time per datasheet §5.1 */ LOG_INF("LR20xx reset complete, BUSY=%d", gpio_pin_get_dt(&ctx->busy)); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.h b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.h index e55df72..90fc6cb 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.h @@ -20,37 +20,28 @@ extern "C" { #include "lr20xx_hal.h" /** - * @brief LR20xx HAL context for Zephyr + * @brief LR20xx HAL context — passed as 'context' to all lr20xx_hal_* functions. * - * Passed as the 'context' pointer to all lr20xx_hal_* functions. - * Contains all hardware configuration needed to communicate with the radio. - * - * CRITICAL: All SPI operations must be protected by the driver's spi_mutex. - * The LR2021 radio is accessed from two threads (main event loop + DIO1 work - * queue). Without the mutex, concurrent SPI access corrupts the command/response - * protocol and the BUSY pin gets stuck HIGH permanently. + * WARNING: All SPI operations must be serialized. The LR2021 is accessed from + * two threads (main event loop + DIO1 work queue). Concurrent SPI access will + * corrupt the command/response protocol and permanently stick BUSY HIGH. */ struct lr20xx_hal_context { - /* SPI device */ const struct device *spi_dev; struct spi_config spi_cfg; - /* GPIO pins */ - struct gpio_dt_spec nss; /* Chip select (direct GPIO, not SPI peripheral CS) */ - struct gpio_dt_spec reset; /* Reset pin (active-low) */ - struct gpio_dt_spec busy; /* Busy pin (high = busy) */ - struct gpio_dt_spec dio1; /* DIO1 interrupt pin */ + struct gpio_dt_spec nss; /* Chip select (direct GPIO, not SPI controller CS) */ + struct gpio_dt_spec reset; /* Reset (active-low) */ + struct gpio_dt_spec busy; /* BUSY: high = chip processing command */ + struct gpio_dt_spec dio1; /* DIO1 interrupt */ - /* State tracking */ volatile bool radio_is_sleeping; }; /** - * @brief Initialize HAL context GPIOs + * @brief Initialize HAL context GPIOs. Must be called before any other HAL function. * - * Must be called before any other HAL functions. - * - * @param ctx HAL context with gpio specs already filled in + * @param ctx HAL context with gpio specs filled in * @return 0 on success, negative errno on failure */ int lr20xx_hal_init(struct lr20xx_hal_context *ctx); @@ -61,11 +52,7 @@ int lr20xx_hal_init(struct lr20xx_hal_context *ctx); typedef void (*lr20xx_dio1_callback_t)(void *user_data); /** - * @brief Set DIO1 interrupt callback - * - * @param ctx HAL context - * @param cb Callback (called directly from GPIO ISR — must be ISR-safe) - * @param user_data User data passed to callback + * @brief Set DIO1 interrupt callback (invoked directly from GPIO ISR — must be ISR-safe). */ void lr20xx_hal_set_dio1_callback(struct lr20xx_hal_context *ctx, lr20xx_dio1_callback_t cb, void *user_data); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.c b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.c index 77208b1..9577308 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.c +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.c @@ -49,21 +49,13 @@ * --- PRIVATE MACROS----------------------------------------------------------- */ -/** - * @brief Internal RTC frequency - */ +/* LR2021 internal RTC: 32.768kHz (datasheet §5.3) */ #define LR20XX_RTC_FREQ_IN_HZ ( 32768UL ) -/*! - * @brief Frequency step in Hz used to compute the front end calibration parameter - * - * @see lr20xx_radio_common_calibrate_front_end_helper - */ +/* Front-end calibration granularity: 4MHz steps (LR2021 datasheet §5.5.1) */ #define LR20XX_RADIO_COMMON_FRONT_END_CALIBRATION_STEP_IN_HZ ( 4000000u ) -/** - * Register address holding the LQI value - */ +/* LR2021 register: LQI (Link Quality Indicator) — Semtech SWDR001 register map */ #define LR20XX_RADIO_COMMON_REGISTER_LQI ( 0xF30C38 ) /* @@ -153,27 +145,13 @@ enum * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- */ -/*! - * @brief Serialize an RSSI calibration item into an array - * - * @param array Pointer to the array to write to. It is up to the caller to ensure the array is long enough to store the - * serialized item - * @param rssi_calibration_item Pointer to the RSSI calibration item to serialize. It is up to the caller to ensure it - * points to an actual item. - * @return uint8_t* Pointer to the next memory slot to write - */ +/* Serialize one RSSI calibration gain item; returns pointer past written data. + * Caller ensures array has sufficient space. */ uint8_t* lr20xx_radio_common_serialize_rssi_calibration_item( uint8_t* array, const lr20xx_radio_common_rssi_calibration_gain_item_t* rssi_calibration_item ); -/** - * @brief Serialize an RSSI calibration table into an array - * - * @param array Pointer to the array to write to. It is up to the caller to ensure the array is long enough to store the - * serialized table - * @param rssi_calibration_table Pointer to the calibration table to serialize. Can be NULL, in which case nothing is - * written to the array - * @return uint8_t* Pointer to the next memory slot to write - */ +/* Serialize a full RSSI calibration gain table; no-op if rssi_calibration_table is NULL. + * Returns pointer past written data. */ uint8_t* lr20xx_radio_common_serialize_rssi_calibration_table( uint8_t* array, const lr20xx_radio_common_rssi_calibration_gain_table_t* rssi_calibration_table ); @@ -218,7 +196,7 @@ lr20xx_status_t lr20xx_radio_common_calibrate_front_end_helper( const uint32_t freq_hz = front_end_calibration_structures[front_end_calibration_value_index].frequency_in_hertz; const lr20xx_radio_common_rx_path_t rx_path = front_end_calibration_structures[front_end_calibration_value_index].rx_path; - // Perform a ceil() to get a value for freq_4mhz corresponding to a frequency higher than or equal to freq_hz + /* ceil(freq_hz / 4MHz): calibrate at next 4MHz boundary ≥ freq_hz */ const uint16_t freq_4mhz = ( uint16_t ) ( ( freq_hz + LR20XX_RADIO_COMMON_FRONT_END_CALIBRATION_STEP_IN_HZ - 1u ) / LR20XX_RADIO_COMMON_FRONT_END_CALIBRATION_STEP_IN_HZ ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.h index 8ba0b70..311a6f7 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.h @@ -69,580 +69,169 @@ extern "C" { * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- */ -/*! - * @brief Executes front end calibration procedure on given raw frequencies and Rx path - * - * The front end calibration calibrates: - * - the ADC offset - * - the poly-phase filter - * - the image - * This function can be called only if the chip is neither in Rx nor Tx states. - * - * Upon completion, the chip will return to the same mode it was before calling this command. - * Potential calibration issues can be read out with lr20xx_system_get_errors command. - * - * Up to three calibration configuration values can be given. - * Only the provided and non-zero frequencies are calibrated. - * - * It is advised to configure calibration so that RF frequencies used during RF operations are at most 50MHz away from - * a calibrated RF frequency. - * - * If no calibration configuration is given, then one front end calibration is executed on the next 4MHz multiple of the - * currently configured RF frequency. - * - * @param [in] context Chip implementation context - * @param [in] front_end_calibration_values Array of front end calibration configuration. It is up to the caller to - * ensure that it has at least n_rx_path_frequency elements. - * @param [in] n_front_end_calibration_values Number of front end calibration values to consider. Valid values are [0:3] - * included. - * - * @returns Operation status - * - * @see lr20xx_system_get_errors, lr20xx_radio_common_calibrate_front_end_helper +/* + * Calibrate front-end (ADC offset, poly-phase filter, image) from raw calibration values. + * Must be called when chip is not in Rx/Tx. Up to 3 values; 0 calibrates at next 4MHz multiple of current RF freq. + * RF ops should stay within 50MHz of a calibrated frequency. Errors readable via lr20xx_system_get_errors. */ lr20xx_status_t lr20xx_radio_common_calibrate_front_end( const void* context, const lr20xx_radio_common_raw_front_end_calibration_value_t* front_end_calibration_values, uint8_t n_front_end_calibration_values ); -/*! - * @brief Helper function to execute front end calibration procedure - * - * This function really is a helper function that converts the front end calibration structures in argument to the - * corresponding raw values, and calls @ref lr20xx_radio_common_calibrate_front_end. - * For each given front end calibration frequency, the actual calibration frequency used is the next frequency multiple - * of 4MHz following the given frequency. - * - * @param [in] context Chip implementation context - * @param [in] front_end_calibration_structures Array of front end calibration configuration structures. It is up to the - * user that it contains at least n_rx_path_frequency items. - * @param [in] n_front_end_calibration_structures Number of front end calibration structures to consider. Valid values - * are [0:3] included. - * - * @returns Operation status - * - * @see lr20xx_radio_common_calibrate_front_end +/* + * Helper: converts front_end_calibration_value_t structs to raw values and calls calibrate_front_end. + * Each frequency is rounded up to the next 4MHz multiple. */ lr20xx_status_t lr20xx_radio_common_calibrate_front_end_helper( const void* context, const lr20xx_radio_common_front_end_calibration_value_t* front_end_calibration_structures, uint8_t n_front_end_calibration_structures ); -/** - * @brief Helper function that computes the number of RTC steps from a given time in millisecond - * - * @param [in] time_in_ms Time in millisecond - * - * @returns Number of RTC steps - */ +/* Convert milliseconds to 32.768kHz RTC step count */ uint32_t lr20xx_radio_common_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ); -/*! - * @brief Set the RF frequency to be used - * - * @param [in] context Chip implementation context - * @param [in] freq_in_hz RF frequency in Hertz - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_rf_freq( const void* context, uint32_t freq_in_hz ); -/*! - * @brief Select the Rx path and set the boost mode - * - * @param [in] context Chip implementation context - * @param [in] rx_path Rx path to be used - * @param [in] boost_mode Boost mode applied to selected Rx path - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_rx_path( const void* context, lr20xx_radio_common_rx_path_t rx_path, lr20xx_radio_common_rx_path_boost_mode_t boost_mode ); -/*! - * @brief Set the Power Amplifier configuration - * - * It must be called prior using @ref lr20xx_radio_common_set_tx_params. - * - * @param [in] context Chip implementation context - * @param [in] pa_cfg The structure for PA configuration - * - * @see lr20xx_radio_common_set_tx_params - * - * @returns Operation status - */ +/* Must be called before lr20xx_radio_common_set_tx_params */ lr20xx_status_t lr20xx_radio_common_set_pa_cfg( const void* context, const lr20xx_radio_common_pa_cfg_t* pa_cfg ); -/*! - * @brief Set the parameters for TX power and power amplifier ramp time - * - * @ref lr20xx_radio_common_set_pa_cfg must be called prior calling lr20xx_radio_common_set_tx_params. - * - * The range of possible TX output power values depends on PA selected with @ref - * lr20xx_radio_common_set_pa_cfg : - * - for @ref LR20XX_RADIO_COMMON_PA_SEL_LF : power value goes from -9.5dBm to +22dBm - * (ie. @p power_half_dbm from 0xED to 0x2C) - * - for @ref LR20XX_RADIO_COMMON_PA_SEL_HF : power value goes from -19.5dBm to +12dBm - * (ie. @p power_half_dbm from 0xD9 to 0x18) - * - * @param [in] context Chip implementation context - * @param [in] power_half_dbm TX output power raw value, as 0.5dBm steps (so twice the value in dBm) - * @param [in] ramp_time Ramping time configuration - * - * @see lr20xx_radio_common_set_pa_cfg - * - * @returns Operation status +/* + * Set TX output power (0.5dBm steps) and PA ramp time. Requires prior call to set_pa_cfg. + * LF PA: power_half_dbm in [0xED, 0x2C] (-9.5 to +22dBm) + * HF PA: power_half_dbm in [0xD9, 0x18] (-19.5 to +12dBm) */ lr20xx_status_t lr20xx_radio_common_set_tx_params( const void* context, const int8_t power_half_dbm, const lr20xx_radio_common_ramp_time_t ramp_time ); -/*! - * @brief Set RSSI calibration table(s) - * - * @param [in] context Chip implementation context - * @param [in] rssi_cal_table_lf Pointer to RSSI calibration table for low frequency path. Can be NULL, in which case - * this path is not configured - * @param [in] rssi_cal_table_hf Pointer to RSSI calibration table for high frequency path. Can be NULL, in which case - * this path is not configured - * - * @returns Operation status - */ +/* Set RSSI calibration gain tables; NULL pointer skips that path */ lr20xx_status_t lr20xx_radio_common_set_rssi_calibration( const void* context, const lr20xx_radio_common_rssi_calibration_gain_table_t* rssi_cal_table_lf, const lr20xx_radio_common_rssi_calibration_gain_table_t* rssi_cal_table_hf ); -/*! - * @brief Configure the chip mode shall be in after transmission or reception operation - * - * @remark The configured fallback mode is applied as soon as the chip leaves Tx / Rx mode: - * - after a successful transmission - * - after a successful reception if not set in continuous mode - * - after a successful reception in duty cycle mode - * - after a CAD operation (depending on configured exit mode) - * - when a timeout occurs - * - during automatic Tx/Rx (see @ref lr20xx_radio_common_configure_auto_tx_rx), both after the first Rx/Tx and the - * second Tx/Rx - * - * @param [in] context Chip implementation context - * @param [in] fallback_mode Chip mode to enter after transmission or reception operation - * - * @returns Operation status +/* + * Set chip mode after leaving Tx/Rx (successful, timeout, or CAD). + * Applied on: TX done, RX done (non-continuous), RX duty cycle done, CAD exit, timeout, auto-Tx/Rx transitions. */ lr20xx_status_t lr20xx_radio_common_set_rx_tx_fallback_mode( const void* context, const lr20xx_radio_common_fallback_modes_t fallback_mode ); -/*! - * @brief Set the packet type to be used - * - * @remark This command has to be sent prior to any modulation related configuration command - * - * @note This function automatically applies the workaround @ref lr20xx_workarounds_dcdc_reset unless the macro @p - * LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_RESET is defined at compile time. - * - * @param [in] context Chip implementation context - * @param [in] pkt_type Packet type to be configured - * - * @returns Operation status +/* + * Set packet type; must precede any modulation configuration. + * Automatically applies lr20xx_workarounds_dcdc_reset unless LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_RESET is defined. */ lr20xx_status_t lr20xx_radio_common_set_pkt_type( const void* context, lr20xx_radio_common_pkt_type_t pkt_type ); -/*! - * @brief Get the packet type currently in use - * - * @param [in] context Chip implementation context - * @param [out] pkt_type Packet type currently in use - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_get_pkt_type( const void* context, lr20xx_radio_common_pkt_type_t* pkt_type ); -/*! - * @brief Set the event on which the Rx timeout is stopped - * - * Depending on the configuration, Rx timeout is stopped either on the detection of the following events: - * - LoRa header detection (or Rx done in implicit mode) / GFSK syncword detection - * - Preamble detection - * - * @param [in] context Chip implementation context - * @param [in] is_stopped_on_preamble_detection If true, the timer stops on preamble detection - * - * @returns Operation status +/* + * Configure when the Rx timeout timer stops: + * - false: on LoRa header / GFSK syncword detection + * - true: on preamble detection */ lr20xx_status_t lr20xx_radio_common_set_rx_timeout_stop_event( const void* context, const bool is_stopped_on_preamble_detection ); -/*! - * @brief Reset internal Rx stats - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_reset_rx_stats( const void* context ); -/*! - * @brief Get the instantaneous RSSI while the transceiver is in reception mode - * - * This command can be used during reception of a packet - * - * The instantaneous RSSI can be obtained with 0.5 dBm accuracy thanks to the output argument half_dbm_count, which is - * either 0 or 1, using the following formula: - * - * RSSI = rssi_in_dbm - ( half_dbm_count * 0.5 ) - * - * The pointer half_dbm_count can be NULL, in which case the value is not returned. - * - * @param [in] context Chip implementation context - * @param [out] rssi_in_dbm Instantaneous RSSI. - * @param [out] half_dbm_count Count of 0.5 dBm to subtract to value in dBm. Can be NULL. - * - * @returns Operation status +/* + * Get instantaneous RSSI during active reception. + * Full precision: RSSI_dBm = rssi_in_dbm - (half_dbm_count * 0.5); half_dbm_count may be NULL. */ lr20xx_status_t lr20xx_radio_common_get_rssi_inst( const void* context, int16_t* rssi_in_dbm, uint8_t* half_dbm_count ); -/*! - * @brief Start RX operations with a timeout in millisecond - * - * @remark To set the radio in Rx continuous mode, refer to @ref lr20xx_radio_common_set_rx_with_timeout_in_rtc_step - * - * @param [in] context Chip implementation context - * @param [in] timeout_in_ms Timeout configuration in millisecond for RX operation - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_rx( const void* context, const uint32_t timeout_in_ms ); -/*! - * @brief Start RX operations with a timeout in RTC step - * - * The timeout duration is obtained by: - * \f$ timeout\_duration\_ms = timeout\_in\_rtc\_step \times \frac{1}{32.768} \f$ - * - * Maximal timeout value is 0xFFFFFE, which gives a maximal timeout of 511 seconds. - * - * The timeout argument can also have the following special values: - * - * - * - * - *
Special values Meaning
0x000000 RX single - transceiver stays in RX mode until a packet is received
0xFFFFFF RX continuous - transceiver stays in RX mode even after reception of a packet
- * - * @param [in] context Chip implementation context - * @param [in] timeout_in_rtc_step Timeout configuration in RTC step for RX operation - * - * @returns Operation status +/* + * Start RX with RTC step timeout. timeout_in_ms = steps / 32.768; max 0xFFFFFE (~511s). + * 0x000000 = single RX (wait for packet); 0xFFFFFF = continuous RX. */ lr20xx_status_t lr20xx_radio_common_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); -/*! - * @brief Start RX operations with a pre-configured default timeout - * - * @remark The timeout has to be configured by calling either @ref lr20xx_radio_common_set_default_rx_tx_timeout or @ref - * lr20xx_radio_common_set_default_rx_tx_timeout_in_rtc_step - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ +/* Start RX using timeout pre-configured by set_default_rx_tx_timeout[_in_rtc_step] */ lr20xx_status_t lr20xx_radio_common_set_rx_with_default_timeout( const void* context ); -/*! - * @brief Start transmission operation with a timeout in millisecond - * - * @param [in] context Chip implementation context - * @param [in] timeout_in_ms Timeout configuration in millisecond for RX operation - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_tx( const void* context, const uint32_t timeout_in_ms ); -/*! - * @brief Start transmission operation with a timeout in RTC step - * - * The timeout duration is obtained by: - * \f$ timeout\_duration\_ms = timeout_in_rtc_step \times \frac{1}{32.768} \f$ - * - * Maximal timeout value is 0xFFFFFF, which gives a maximal timeout of 511 seconds. - * - * If \p timeout_in_rtc_step is set to 0, then no timeout is used. - * - * @param [in] context Chip implementation context - * @param [in] timeout_in_rtc_step Timeout configuration in RTC step for TX operation - * - * @returns Operation status - */ +/* Start TX with RTC step timeout. timeout=0 disables timeout. Max 0xFFFFFF (~511s). */ lr20xx_status_t lr20xx_radio_common_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); -/*! - * @brief Start TX operations with a pre-configured default timeout - * - * @remark The timeout has to be configured by calling either @ref lr20xx_radio_common_set_default_rx_tx_timeout or @ref - * lr20xx_radio_common_set_default_rx_tx_timeout_in_rtc_step - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ +/* Start TX using timeout pre-configured by set_default_rx_tx_timeout[_in_rtc_step] */ lr20xx_status_t lr20xx_radio_common_set_tx_with_default_timeout( const void* context ); -/*! - * @brief Set the transceiver into a Tx test mode. - * - * @param [in] context Chip implementation context - * @param [in] mode Test mode - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_tx_test_mode( const void* context, lr20xx_radio_common_tx_test_mode_t mode ); -/*! - * @brief Select the Power Amplifier to use - * - * @remark Configuration has to be applied first by calling @ref lr20xx_radio_common_set_pa_cfg - * - * @param [in] context Chip implementation context - * @param [in] sel Power amplifier selection - * - * @returns Operation status - */ +/* Select PA; set_pa_cfg must be called first */ lr20xx_status_t lr20xx_radio_common_select_pa( const void* context, lr20xx_radio_common_pa_selection_t sel ); -/*! - * @brief Configure and start a Rx Duty Cycle operation with timings in millisecond - * - * @remark This function computes timings in RTC step from values given in millisecond and then calls @ref - * lr20xx_radio_common_set_rx_duty_cycle_with_timing_in_rtc_step - * - * @param [in] context Chip implementation context - * @param [in] rx_period_in_ms Rx period in millisecond - * @param [in] sleep_period_in_ms Sleep period in millisecond - * @param [in] mode Operation mode used during Rx phase - * - * @returns Operation status - */ +/* Converts ms to RTC steps and calls set_rx_duty_cycle_with_timing_in_rtc_step */ lr20xx_status_t lr20xx_radio_common_set_rx_duty_cycle( const void* context, const uint32_t rx_period_in_ms, const uint32_t sleep_period_in_ms, const lr20xx_radio_common_rx_duty_cycle_mode_t mode ); -/*! - * @brief Configure and start a Rx Duty Cycle operation with timings in RTC step - * - * It executes the following steps: - * 1. Reception - enters reception state for duration defined by @p rx_period_in_rtc_step: - * - @p mode = LR20XX_RADIO_COMMON_RX_DUTY_CYCLE_MODE_RX: regular Rx mode - * - @p mode = LR20XX_RADIO_COMMON_RX_DUTY_CYCLE_MODE_CAD (LoRa only) : CAD mode - * 2. Depending on the over-the-air activity detection (either preamble detection or valid CAD): - * - In case of positive over-the-air detection, the Rx period timeout is restarted with the value - * \f$2 \times rx_period_in_rtc_step + sleep_period_in_rtc_step\f$ - * - else, the transceiver goes into sleep mode with retention for a duration defined by @p - * sleep_period_in_rtc_step - * 3. On wake-up, the transceiver restarts the process to step 1 - * - * The loop described above is terminated in the following cases: - * - a packet is received during a Rx window - the chip goes back to fallback mode configured with @ref - * lr20xx_radio_common_set_rx_tx_fallback_mode - * - a call to @ref lr20xx_system_set_standby_mode is done during a Rx window - * - a call to @ref lr20xx_system_wakeup is done during a sleep phase - to prevent a possible race condition from - * happening when the call is performed during the boot phase, it is recommended to call @ref - * lr20xx_system_set_standby_mode when BUSY is going low - * - * @remark If @p mode is set to @ref LR20XX_RADIO_COMMON_RX_DUTY_CYCLE_MODE_CAD, CAD parameters have to be defined - * before calling this function - * - * @param [in] context Chip implementation context - * @param [in] rx_period_in_rtc_step Rx period in RTC step - * @param [in] sleep_period_in_rtc_step Sleep period in RTC step - * @param [in] mode Operation mode used during Rx phase - * - * @returns Operation status +/* + * Start RX duty cycle: Rx for rx_period, then sleep for sleep_period, repeat. + * On activity detected: extend Rx timeout to (2*rx_period + sleep_period). + * On packet received: return to fallback mode. + * CAD mode (LoRa only): configure CAD params before calling. + * To stop during sleep: call lr20xx_system_wakeup then lr20xx_system_set_standby_mode when BUSY goes low. */ lr20xx_status_t lr20xx_radio_common_set_rx_duty_cycle_with_timing_in_rtc_step( const void* context, const uint32_t rx_period_in_rtc_step, const uint32_t sleep_period_in_rtc_step, const lr20xx_radio_common_rx_duty_cycle_mode_t mode ); -/** - * @brief Configure the automatic Tx operation after Rx, or automatic Rx operation after Tx - * - * This feature allows the chip to automatically execute a Tx operation after an Rx one; or to automatically execute an - * Rx operation after a Tx one. - * - * The order of operation depends on the mode manually requested after issuing this command: - * - If the radio is set to Tx mode, then an automatic Rx will be executed; - * - If the radio is set to Rx mode, then an automatic Tx will be executed. - * - * This feature is similar to a call to @ref lr20xx_radio_common_set_tx_with_timeout_in_rtc_step (or @ref - * lr20xx_radio_common_set_rx_with_timeout_in_rtc_step) after the given delay_in_tick. Therefore to fine tune the - * instant of first bit automatically sent over-the-air (or reception window opening) other delays have to be taken into - * account when determining the delay_in_tick value. For instance, but not limited to: - * - PA ramp-up - * - TCXO start time (if applicable) - * - Configured fallback mode - * - Radio state switching time - * - * When the automatic Tx/Rx is enabled, the chip is in the state configured by @ref - * lr20xx_radio_common_set_rx_tx_fallback_mode between the end of Rx (or Tx) operation and the start of the next - * automatic Tx (or Rx) operation. - * - * Calling @ref lr20xx_radio_common_configure_auto_tx_rx with condition being @ref LR20XX_RADIO_COMMON_AUTO_TX_RX_OFF - * disables the automatic Tx or Rx behavior. Doing so after end of Rx (or Tx) operation and start of automatic Tx (or - * Rx) also cancels the automatic Tx or Rx operation. - * - * Once the automatic operation triggers, the feature is automatically disabled. So that to engage again an automatic - * operation after a manual one, the @ref lr20xx_radio_common_configure_auto_tx_rx must be called to enable it again. - * - * @param context Chip implementation context - * @param configuration The configuration of the automatic Tx/Rx - * - * @see lr20xx_radio_common_set_tx_with_timeout_in_rtc_step, lr20xx_radio_common_set_rx_with_timeout_in_rtc_step, - * lr20xx_radio_common_set_rx_tx_fallback_mode - * - * @return lr20xx_status_t +/* + * Configure automatic Tx-after-Rx or Rx-after-Tx. + * Set mode to Tx → auto-Rx fires; set mode to Rx → auto-Tx fires. + * Between operations the chip enters fallback mode. Triggers once then auto-disables. + * delay_in_tick must account for PA ramp, TCXO start, fallback mode switching. + * Pass condition=LR20XX_RADIO_COMMON_AUTO_TX_RX_OFF to disable. */ lr20xx_status_t lr20xx_radio_common_configure_auto_tx_rx( const void* context, const lr20xx_radio_common_auto_tx_rx_configuration_t* configuration ); -/*! - * @brief Get the length in byte of the last received packet - * - * @param [in] context Chip implementation context - * @param [out] pkt_len Length in byte of the last received packet - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_get_rx_packet_length( const void* context, uint16_t* pkt_len ); -/*! - * @brief Set default timeout values for RX and TX operations - * - * @param [in] context Chip implementation context - * @param [in] rx_timeout_in_ms Timeout configuration in millisecond for RX operation - * @param [in] tx_timeout_in_ms Timeout configuration in millisecond for TX operation - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_default_rx_tx_timeout( const void* context, uint32_t rx_timeout_in_ms, uint32_t tx_timeout_in_ms ); -/*! - * @brief Set default timeout values for RX and TX operations - * - * @remark Special values defined for @ref lr20xx_radio_common_set_rx_with_timeout_in_rtc_step and @ref - * lr20xx_radio_common_set_tx_with_timeout_in_rtc_step are also applicable here - * - * @param [in] context Chip implementation context - * @param [in] rx_timeout_in_rtc_step Timeout configuration in RTC step for RX operation - * @param [in] tx_timeout_in_rtc_step Timeout configuration in RTC step for TX operation - * - * @returns Operation status - */ +/* Same special values apply as for set_rx/tx_with_timeout_in_rtc_step */ lr20xx_status_t lr20xx_radio_common_set_default_rx_tx_timeout_in_rtc_step( const void* context, uint32_t rx_timeout_in_rtc_step, uint32_t tx_timeout_in_rtc_step ); -/*! - * @brief Set a timestamp source for a given configuration slot - * - * @remark This command configure a source linked to a radio event that will then be used by @ref - * lr20xx_radio_common_get_elapsed_time_in_tick to compute the elapsed time - * - * @param [in] context Chip implementation context - * @param [in] cfg_slot Configuration slot - * @param [in] source Timestamp source - * - * @returns Operation status - */ +/* Arm a 32MHz timestamp on the given radio event; read with get_elapsed_time_in_tick */ lr20xx_status_t lr20xx_radio_common_set_timestamp_source( const void* context, lr20xx_radio_common_timestamp_cfg_slot_t cfg_slot, lr20xx_radio_common_timestamp_source_t source ); -/*! - * @brief Get the elapsed time since radio event registered at given configuration slot - * - * @remark This is the time elapsed between the event configured with @ref lr20xx_radio_common_set_timestamp_source and - * the NSS falling edge of this request - * - * @remark That radio must not be put in sleep mode between the configured event and the call to this function - * - * @param [in] context Chip implementation context - * @param [in] cfg_slot Configuration slot - * @param [out] elapsed_time_in_tick Elapsed time in 32MHz tick - * - * @returns Operation status +/* + * Read elapsed 32MHz ticks since the event configured in set_timestamp_source for cfg_slot. + * Radio must not have entered sleep between the event and this call. */ lr20xx_status_t lr20xx_radio_common_get_elapsed_time_in_tick( const void* context, lr20xx_radio_common_timestamp_cfg_slot_t cfg_slot, uint32_t* elapsed_time_in_tick ); -/*! - * @brief Launch a CCA (Clear Channel Assessment) operation - * - * @param [in] context Chip implementation context - * @param [in] duration CCA duration in 32MHz step - * - * @returns Operation status - */ +/* Start CCA (Clear Channel Assessment); duration in 32MHz steps */ lr20xx_status_t lr20xx_radio_common_set_cca( const void* context, const uint32_t duration ); -/*! - * @brief Get the CCA values once the operation is over - * - * @param [in] context Chip implementation context - * @param [out] cca_res Structure holding CCA result - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_get_cca_result( const void* context, lr20xx_radio_common_cca_res_t* cca_res ); -/*! - * @brief Set the gain to be used by the AGC (Automatic Gain Control) - * - * @param [in] context Chip implementation context - * @param [in] gain Gain - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_agc_gain( const void* context, lr20xx_radio_common_gain_step_t gain ); -/*! - * @brief Set non-LoRa CAD parameters - * - * @remark This command is not applicable if the packet type is set to LoRa - * - * @param [in] context Chip implementation context - * @param [in] params CAD parameters - * - * @returns Operation status - */ +/* Set non-LoRa CAD parameters; not applicable when packet type is LoRa */ lr20xx_status_t lr20xx_radio_common_set_cad_params( const void* context, const lr20xx_radio_common_cad_params_t* params ); -/*! - * @brief Set the chip in non-LoRa CAD mode - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_common_set_cad( const void* context ); -/** - * @brief Get the Link Quality Indicator (LQI) of latest detected packet - * - * This function is only valid if the latest received packet is an FSK based modulation: - * - FSK - * - Bluetooth_LE - * - OQPSK 15.4 - * - Wi-SUN - * - Wireless M-Bus - * - Z-Wave - * - * The value returned corresponds to the latest detected packet. It is valid from the packet detection (corresponding to - * @ref LR20XX_SYSTEM_IRQ_PREAMBLE_DETECTED raised if enabled) until next Rx attempt (through call to @ref - * lr20xx_radio_common_set_rx or @ref lr20xx_radio_common_set_rx_with_timeout_in_rtc_step for instance). - * - * @param[in] context Chip implementation context - * @param[out] lqi The LQI value - * @return lr20xx_status_t Operation status +/* + * Get LQI of last detected packet. Valid from preamble detection until next Rx call. + * Applies to FSK-based modes: FSK, BLE, OQPSK-15.4, Wi-SUN, Wireless M-Bus, Z-Wave. */ lr20xx_status_t lr20xx_radio_common_get_lqi( const void* context, lr20xx_radio_common_lqi_value_t* lqi ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.h index 241d40b..3f53474 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.h @@ -68,135 +68,30 @@ extern "C" { * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- */ -/*! - * @brief Read data from RX First in First out (FiFo) radio memory - * - * The RX FiFo radio memory contains packet received or being received. - * - * @param [in] context Chip implementation context - * @param [in] buffer The buffer to be filled with data read from RX FiFo. It is up to the caller to ensure it is at - * least @p length bytes long. - * @param [in] length The number of bytes to read from RX FiFo - * - * @returns Operation status - * - * @see lr20xx_radio_fifo_write_tx - */ lr20xx_status_t lr20xx_radio_fifo_read_rx( const void* context, uint8_t* buffer, const uint16_t length ); - -/*! - * @brief Write data to TX First in First out (FiFo) radio memory - * - * The TX FiFo radio memory contains packet to send. - * - * @param [in] context Chip implementation context - * @param [in] buffer The buffer to be written to TX FiFo. It is up to the caller to ensure it is at least - * @p length bytes long. - * @param [in] length The number of bytes to write to TX FiFo - * - * @returns Operation status - * - * @see lr20xx_radio_fifo_read_rx - */ lr20xx_status_t lr20xx_radio_fifo_write_tx( const void* context, const uint8_t* buffer, const uint16_t length ); - -/*! - * @brief Clear Rx FIFO - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_fifo_clear_rx( const void* context ); - -/*! - * @brief Clear Tx FIFO - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_fifo_clear_tx( const void* context ); - -/*! - * @brief Get Rx FIFO level - * - * @param [in] context Chip implementation context - * @param [out] fifo_level Rx FIFO level in byte - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_fifo_get_rx_level( const void* context, uint16_t* fifo_level ); - -/*! - * @brief Get Tx FIFO level - * - * @param [in] context Chip implementation context - * @param [out] fifo_level Tx FIFO level in byte - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_fifo_get_tx_level( const void* context, uint16_t* fifo_level ); -/*! - * @brief Configure FIFO events and threshold levels triggering a FIFO interrupt in Rx and Tx - * - * @remark When configured, the FIFO interrupts are triggered if the FIFO level crosses the threshold in the correct - * direction. Therefore if a threshold related IRQ is cleared, it will be raised again only if the FIFO level crosses - * the threshold on the correct direction. - * - * @param [in] context Chip implementation context - * @param [in] rx_fifo_irq_enable FIFO events triggering an interrupt in Rx - * @param [in] tx_fifo_irq_enable FIFO events triggering an interrupt in Tx - * @param [in] rx_fifo_high_threshold Rx FIFO threshold above which an interrupt (if - * LR20XX_RADIO_FIFO_FLAG_THRESHOLD_HIGH is enabled) is triggered - * @param [in] tx_fifo_low_threshold Tx FIFO threshold below which an interrupt (if - * LR20XX_RADIO_FIFO_FLAG_THRESHOLD_LOW is enabled) is triggered - * @param [in] rx_fifo_low_threshold Rx FIFO threshold below which an interrupt (if - * LR20XX_RADIO_FIFO_FLAG_THRESHOLD_LOW is enabled) is triggered - * @param [in] tx_fifo_high_threshold Tx FIFO threshold above which an interrupt (if - * LR20XX_RADIO_FIFO_FLAG_THRESHOLD_HIGH is enabled) is triggered - * - * @returns Operation status +/* + * Configure FIFO threshold IRQs. Threshold IRQs fire on level crossing in the triggering direction; + * clearing the IRQ flag does not re-fire until the threshold is crossed again. + * rx_fifo_high_threshold: triggers THRESHOLD_HIGH when Rx level rises above this + * rx_fifo_low_threshold: triggers THRESHOLD_LOW when Rx level falls below this + * tx_fifo_high_threshold: triggers THRESHOLD_HIGH when Tx level rises above this + * tx_fifo_low_threshold: triggers THRESHOLD_LOW when Tx level falls below this */ lr20xx_status_t lr20xx_radio_fifo_cfg_irq( const void* context, lr20xx_radio_fifo_flag_t rx_fifo_irq_enable, lr20xx_radio_fifo_flag_t tx_fifo_irq_enable, uint16_t rx_fifo_high_threshold, uint16_t tx_fifo_low_threshold, uint16_t rx_fifo_low_threshold, uint16_t tx_fifo_high_threshold ); -/*! - * @brief Clear specific IRQ flags for both Rx and Tx FIFO - * - * @param [in] context Chip implementation context - * @param [in] rx_fifo_flags_to_clear Rx FIFO IRQ flags to clear - * @param [in] tx_fifo_flags_to_clear Tx FIFO IRQ flags to clear - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_fifo_clear_irq_flags( const void* context, lr20xx_radio_fifo_flag_t rx_fifo_flags_to_clear, lr20xx_radio_fifo_flag_t tx_fifo_flags_to_clear ); - -/*! - * @brief Get FIFO events triggering a FIFO interrupt in Rx and Tx - * - * @param [in] context Chip implementation context - * @param [out] rx_fifo_flags FIFO events triggering an interrupt in Rx - * @param [out] tx_fifo_flags FIFO events triggering an interrupt in Tx - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_fifo_get_irq( const void* context, lr20xx_radio_fifo_flag_t* rx_fifo_flags, lr20xx_radio_fifo_flag_t* tx_fifo_flags ); - -/*! - * @brief Clear and return FiFo IRQ flags - * - * @param [in] context Chip implementation context - * @param [out] rx_fifo_flags Rx FiFo flags - * @param [out] tx_fifo_flags Tx FiFo flags - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_fifo_get_and_clear_irq_flags( const void* context, lr20xx_radio_fifo_flag_t* rx_fifo_flags, lr20xx_radio_fifo_flag_t* tx_fifo_flags ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc.h index a4b01fb..caff9bf 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc.h @@ -68,132 +68,39 @@ extern "C" { * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- */ -/** - * @brief Set the modulation parameters for FLRC packets - * - * The workaround @ref lr20xx_workarounds_dcdc_configure must be called for Rx sub-GHz operations with regulator @ref - * LR20XX_SYSTEM_REG_MODE_DCDC after this function to avoid possible RF sensitivity degradation. - * - * @note This function automatically applies the workaround @ref lr20xx_workarounds_dcdc_configure unless the macro @p - * LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE is defined at compile time. - * - * @note This command is not available to LR2022 - * - * @param[in] context Chip implementation context - * @param[in] params Structure of FLRC modulation configuration - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_workarounds_dcdc_configure +/* + * Set FLRC modulation params. Not available on LR2022. + * Applies lr20xx_workarounds_dcdc_configure automatically unless LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE. */ lr20xx_status_t lr20xx_radio_flrc_set_modulation_params( const void* context, const lr20xx_radio_flrc_mod_params_t* params ); -/** - * @brief Set the packet parameters for FLRC packets - * - * @note This command is not available to LR2022 - * - * @param[in] context Chip implementation context - * @param[in] params Structure of FLRC packet configuration - * - * @return lr20xx_status_t Operation status - */ +/* Set FLRC packet params. Not available on LR2022. */ lr20xx_status_t lr20xx_radio_flrc_set_pkt_params( const void* context, const lr20xx_radio_flrc_pkt_params_t* params ); -/** - * @brief Get the internal statistics of received FLRC packets - * - * The internal statistics are reset on: - * - Power On Reset (POR) - * - sleep without memory retention - * - call to lr20xx_radio_common_reset_rx_stats - * - * @note This command is not available to LR2022 - * - * @param[in] context Chip implementation context - * @param[out] statistics FLRC received packet statistics - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_common_reset_rx_stats +/* + * Get FLRC Rx statistics. Not available on LR2022. + * Stats reset on POR, retention-less sleep, or lr20xx_radio_common_reset_rx_stats. */ lr20xx_status_t lr20xx_radio_flrc_get_rx_stats( const void* context, lr20xx_radio_flrc_rx_stats_t* statistics ); -/** - * @brief Get the status of the last FLRC received packet - * - * Availability of the packet status fields depend on the IRQ as follows: - * - Available from LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID: - * - lr20xx_radio_flrc_pkt_status_t.rssi_sync_in_dbm - * - lr20xx_radio_flrc_pkt_status_t.rssi_sync_half_dbm_count - * - lr20xx_radio_flrc_pkt_status_t.syncword_index - * - Available from LR20XX_SYSTEM_IRQ_RX_DONE: - * - lr20xx_radio_flrc_pkt_status_t.rssi_avg_in_dbm - * - lr20xx_radio_flrc_pkt_status_t.rssi_avg_half_dbm_count - * - * @note This command is not available to LR2022 - * - * @param[in] context Chip implementation context - * @param[out] pkt_status FLRC packet status structure - * - * @return lr20xx_status_t Operation status +/* + * Get status of last FLRC received packet. Not available on LR2022. + * rssi_sync/syncword_index available from SYNC_WORD_HEADER_VALID IRQ. + * rssi_avg available from RX_DONE IRQ. */ lr20xx_status_t lr20xx_radio_flrc_get_pkt_status( const void* context, lr20xx_radio_flrc_pkt_status_t* pkt_status ); -/** - * @brief Set a short syncword for FLRC packet - * - * A short syncword is a 2-bytes long syncword. - * - * Status is available only after the end of a packet reception. - * - * @note This command is not available to LR2022 - * - * @param[in] context Chip implementation context - * @param[in] syncword_index Syncword index to be configured - * @param[in] short_syncword Syncword value to be configured. It is up to the caller to ensure @p short_syncword is at - * least @ref LR20XX_RADIO_FLRC_SHORT_SYNCWORD_LENGTH bytes long - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_flrc_set_syncword - */ +/* Set 2-byte short syncword at syncword_index. Not available on LR2022. */ lr20xx_status_t lr20xx_radio_flrc_set_short_syncword( const void* context, uint8_t syncword_index, const uint8_t short_syncword[LR20XX_RADIO_FLRC_SHORT_SYNCWORD_LENGTH] ); -/** - * @brief Set the syncword for FLRC packet - * - * Status is available only after the end of a packet reception. - * - * @note This command is not available to LR2022 - * - * @param[in] context Chip implementation context - * @param[in] syncword_index Syncword index to be configured - * @param[in] syncword Syncword value to be configured. It is up to the caller to ensure @p short_syncword is at least - * @ref LR20XX_RADIO_FLRC_SYNCWORD_LENGTH bytes long - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_flrc_set_short_syncword - */ +/* Set 4-byte syncword at syncword_index. Not available on LR2022. */ lr20xx_status_t lr20xx_radio_flrc_set_syncword( const void* context, uint8_t syncword_index, const uint8_t syncword[LR20XX_RADIO_FLRC_SYNCWORD_LENGTH] ); -/** - * @brief Helper function to get the time-on-air of FLRC packet, in microseconds - * - * @note This command is not available to LR2022 - * - * @param pkt_params The packet parameter configuration - * @param mod_params The modulation parameter configuration - * - * @return Time-on-air of the packet in microsecond - * - * @see lr20xx_radio_flrc_set_modulation_params, lr20xx_radio_flrc_set_pkt_params - */ +/* Compute FLRC time-on-air in microseconds. Not available on LR2022. */ uint32_t lr20xx_get_flrc_time_on_air_in_us( const lr20xx_radio_flrc_pkt_params_t* pkt_params, const lr20xx_radio_flrc_mod_params_t* mod_params ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc_types.h index 6bdfcc9..e59813d 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc_types.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc_types.h @@ -51,16 +51,10 @@ extern "C" { * --- PUBLIC MACROS ----------------------------------------------------------- */ -/** - * @brief Length in bytes of the FLRC short syncword - * - */ +/*! @brief FLRC short syncword length in bytes */ #define LR20XX_RADIO_FLRC_SHORT_SYNCWORD_LENGTH ( 2 ) -/** - * @brief Length in bytes of the FLRC syncword - * - */ +/*! @brief FLRC syncword length in bytes */ #define LR20XX_RADIO_FLRC_SYNCWORD_LENGTH ( 4 ) /* @@ -185,9 +179,8 @@ typedef enum lr20xx_radio_flrc_crc_types_e LR20XX_RADIO_FLRC_CRC_4_BYTES = 0x03, } lr20xx_radio_flrc_crc_types_t; -/** - * @brief Modulation configuration for LoRa packet - * +/*! + * @brief Modulation configuration for FLRC packet */ typedef struct lr20xx_radio_flrc_mod_params_s { diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.c b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.c index d9bba41..6e1ac1c 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.c +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.c @@ -47,10 +47,7 @@ * --- PRIVATE MACROS----------------------------------------------------------- */ -/** - * @brief Length in byte of one side detector CAD configuration - */ -#define LR20XX_RADIO_LORA_CAD_SIDE_DETECTOR_CONFIGURATION_LENGTH ( 2u ) +#define LR20XX_RADIO_LORA_CAD_SIDE_DETECTOR_CONFIGURATION_LENGTH ( 2u ) /* pnr_delta + det_peak */ #define LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_CONFIGURE_CAD_CMD_LENGTH ( 2 ) #define LR20XX_RADIO_LORA_SET_MODULATION_PARAMS_CMD_LENGTH ( 2 + 2 ) @@ -82,9 +79,7 @@ * --- PRIVATE TYPES ----------------------------------------------------------- */ -/*! - * @brief Operating codes for radio related operations - */ +/* LoRa radio command opcodes */ enum { LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_CONFIGURE_CAD_OC = 0x021E, @@ -118,34 +113,11 @@ typedef enum * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- */ -/** - * @brief Helper function that abstract the call for lr20xx_radio_lora_set_lora_search_symbols_by_number and - * lr20xx_radio_lora_set_lora_search_symbols_by_mantissa - * - * @param[in] context Chip implementation context - * @param[in] n_symbols A byte representing the number of symbol. Meaning depends on format - * @param[in] format The format that defines the meaning of n_symbols - * @return lr20xx_status_t - */ +/* Send SET_LORA_SEARCH_SYMBOLS command with given n_symbols value and format byte */ static lr20xx_status_t abstract_search_symbols( const void* context, uint8_t n_symbols, search_symbol_format_t format ); - -/** - * @brief Read two bytes from buffer and convert it in 16 bits value MSB first - * - * @param buffer Pointer to location where to read 2 bytes. It is up to the caller to ensure there are at least two - * bytes to read - * - * @return The MSB first value corresponding to the consecutive bytes read - */ +/* Read 2 bytes MSB-first from buffer into uint16_t */ static uint16_t read_2_bytes_msbf( const uint8_t* buffer ); - -/** - * @brief Compute the byte representation of LoRa side detector configuration - * - * @param side_detector_cfg The LoRa side detector configuration - * - * @return uint8_t The byte representing the LoRa side detector configuration - */ +/* Pack lr20xx_radio_lora_side_detector_cfg_t into a single command byte: sf[7:4] | ppm[3:2] | iq[1:0] */ static uint8_t radio_lora_side_detector_cfg_to_byte( const lr20xx_radio_lora_side_detector_cfg_t* side_detector_cfg ); /* @@ -563,24 +535,20 @@ uint32_t lr20xx_radio_lora_get_time_on_air_in_ms( const lr20xx_radio_lora_pkt_pa { uint32_t numerator = 1000U * lr20xx_radio_lora_get_time_on_air_numerator( pkt_p, mod_p ); uint32_t denominator = lr20xx_radio_lora_get_bw_in_hz( mod_p->bw ); - // Perform integral ceil() return ( numerator + denominator - 1 ) / denominator; } lr20xx_radio_lora_ppm_t lr20xx_radio_lora_get_recommended_ppm_offset( lr20xx_radio_lora_sf_t sf, lr20xx_radio_lora_bw_t bw ) { - // PPM offset is LR20XX_RADIO_LORA_PPM_1_4, except for the cases that follow lr20xx_radio_lora_ppm_t ppm_offset = LR20XX_RADIO_LORA_PPM_1_4; if( ( sf != LR20XX_RADIO_LORA_SF11 ) && ( sf != LR20XX_RADIO_LORA_SF12 ) ) { - // 1. If sf is not SF11 nor SF12: no ppm offset ppm_offset = LR20XX_RADIO_LORA_NO_PPM; } else { - // 2. Else it depends on the bandwidth switch( bw ) { case LR20XX_RADIO_LORA_BW_1000: diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.h index df2fc09..1bfae61 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.h @@ -68,409 +68,113 @@ extern "C" { * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- */ -/** - * @brief Set the modulation parameters for LoRa packets - * - * @param[in] context Chip implementation context - * @param[in] mod_params Structure of LoRa modulation configuration - * - * The workaround @ref lr20xx_workarounds_dcdc_configure must be called for Rx sub-GHz operations with regulator - * @ref LR20XX_SYSTEM_REG_MODE_DCDC after this function to avoid possible RF sensitivity degradation. - * - * @note This function automatically applies the workaround @ref lr20xx_workarounds_dcdc_configure unless the macro @p - * LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE is defined at compile time. - * - * @note For RTToF operations with fractional bandwidth, the workaround @ref lr20xx_workarounds_rttof_results_deviation - * shall be applied. Refer to its documentation for details. - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_lora_get_recommended_ppm_offset, lr20xx_workarounds_dcdc_configure, - * lr20xx_workarounds_rttof_results_deviation +/* + * Set LoRa modulation params. Applies lr20xx_workarounds_dcdc_configure automatically unless + * LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE. For RTToF with fractional BW, also call + * lr20xx_workarounds_rttof_results_deviation. See lr20xx_radio_lora_get_recommended_ppm_offset for PPM offset. */ lr20xx_status_t lr20xx_radio_lora_set_modulation_params( const void* context, const lr20xx_radio_lora_mod_params_t* mod_params ); -/** - * @brief Set the packet parameters for LoRa packets - * - * The meaning of field pkt_params->pld_len_in_bytes depends on the packet mode selected: - * - If LR20XX_RADIO_LORA_PKT_EXPLICIT: - * - pld_len_in_bytes = 0 means that packets of all payload length will be accepted - * - pld_len_in_bytes > 0 means that packet with payload length in range [1:pld_len_in_bytes] will be accepted. - * Packet of payload length equals to 0 or strictly superior to pld_len_in_bytes will be rejected with IRQ - * LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR - * - * @param[in] context Chip implementation context - * @param[in] pkt_params Structure of LoRa packet configuration - * - * @return lr20xx_status_t Operation status +/* + * Set LoRa packet params. In LR20XX_RADIO_LORA_PKT_EXPLICIT mode: pld_len_in_bytes=0 accepts all lengths; + * pld_len_in_bytes>0 accepts [1:pld_len_in_bytes], rejecting 0 or >pld_len_in_bytes with LORA_HEADER_ERROR IRQ. */ lr20xx_status_t lr20xx_radio_lora_set_packet_params( const void* context, const lr20xx_radio_lora_pkt_params_t* pkt_params ); -/** - * @brief Configure a timeout given in number of LoRa symbols before stopping reception if no LoRa preamble symbols are - * detected - * - * A timeout interrupt is triggered if no LoRa preamble symbol is detected during the given period. - * - * Setting @p n_symbols to 0 disables the mechanism. - * - * If @p n_symbols is higher than 255, this function automatically propagate call to @ref - * lr20xx_radio_lora_configure_timeout_by_mantissa_exponent_symbols function, using @ref - * lr20xx_radio_convert_nb_symb_to_mant_exp to compute mantissa, exponent components. - * - * @param[in] context Chip implementation context - * @param[in] n_symbols The number of symbols to search for. - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_lora_configure_timeout_by_mantissa_exponent_symbols, lr20xx_radio_convert_nb_symb_to_mant_exp +/* + * Configure preamble-absent Rx timeout in LoRa symbols. n_symbols=0 disables. + * n_symbols>255 delegates to configure_timeout_by_mantissa_exponent_symbols via lr20xx_radio_convert_nb_symb_to_mant_exp. */ lr20xx_status_t lr20xx_radio_lora_configure_timeout_by_number_of_symbols( const void* context, uint16_t n_symbols ); -/** - * @brief Configure a timeout given in number of LoRa symbols before stopping reception if no LoRa preamble symbols are - * detected - * - * A timeout interrupt is triggered if no LoRa preamble symbol is detected during the given period. - * - * The number of symbol is computed as \f$ N_{symbols} = mantissa ^ {2 \times exponent + 1} \f$ - * - * Setting @p mantissa and @p exponent to get a number of symbol equal to 0 disables the mechanism. - * - * @param[in] context Chip implementation context - * @param[in] mantissa Mantissa - from 0 to 31 - to compute the number of symbols - * @param[in] exponent Exponent - from 0 to 7 - to compute the number of symbols - * - * @return lr20xx_status_t - * - * @see lr20xx_radio_lora_configure_timeout_by_number_of_symbols, lr20xx_radio_convert_nb_symb_to_mant_exp +/* + * Configure preamble-absent Rx timeout via mantissa/exponent encoding: N = mant × 2^(2×exp+1). + * mantissa in [0:31], exponent in [0:7]. N=0 disables. */ lr20xx_status_t lr20xx_radio_lora_configure_timeout_by_mantissa_exponent_symbols( const void* context, uint8_t mantissa, uint8_t exponent ); -/** - * @brief Helper function to get the mantissa and exponent for a given number of symbol - * - * @remark This function computes the [mantissa, exponent] duple which corresponds to \f$ nb\_of\_symb \f$ : the - * smallest value verifying both following conditions: - * - \f$ nb\_of\_symb >= nb\_symbol \f$; and - * - \f$ nb\_of\_symb = mant * 2 ^ { 2 * exp + 1 } \f$ - * - * @param [in] nb_symbol Number of symbols - * @param [out] mant Mantissa computed from @p nb_symbol - * @param [out] exp Exponent computed from @p nb_symbol - * - * @returns Number of symbols corresponding to the [mantissa, exponent] duple computed with the following formula: - * \f$ nb\_of\_symb = mant * 2 ^ { 2 * exp + 1 } \f$ - * - * @see lr20xx_radio_lora_configure_timeout_by_mantissa_exponent_symbols, - * lr20xx_radio_lora_configure_timeout_by_number_of_symbols +/* + * Compute mantissa/exponent [mant, exp] encoding for nb_symbol, finding the smallest N >= nb_symbol + * where N = mant × 2^(2×exp+1). Returns the actual N value used. */ uint16_t lr20xx_radio_convert_nb_symb_to_mant_exp( const uint16_t nb_symbol, uint8_t* mant, uint8_t* exp ); -/** - * @brief Configure the LoRa syncword. - * - * Default value is 0x12. - * Example of typical values: - * - LoRaWAN public network: 0x34 - * - LoRaWAN private network: 0x12 - * - * The syncword here should be understood as the concatenation of two 4 bits blocks as follows: - * @code{.c} - * uint8_t sync_block_1 = BLOCK_1; - * uint8_t sync_block_2 = BLOCK_2; - * uint8_t syncword = ((sync_block_1 & 0x0F) << 4) | (sync_block_2 & 0x0F); - * @endcode - * - * Here are some recommendations for syncword selection: - * - @p sync_block_1 must not be 0. So that syncword 0x0x must not be used; - * - avoid reusing a block value from another network - * - * Note that using different syncwords does not guarantee packet rejection. Receiver is just less likely to accept frame - * of different syncword. - * - * The following table indicates the compatible block values with other chips. Note that the block values are to be - * compared as signed integer when evaluating compatibility. - * A line indicates a set of values that are compatible together depending on other chips. - * Column SX126x/LR11xx/LR20xx syncword indicates block values used - * with @ref lr20xx_radio_lora_set_syncword function and SX1276 LoRa compatibility disabled (@ref - * lr20xx_workarounds_lora_disable_sx1276_compatibility_mode); the column LR20xx syncword SX127x compatibility - * indicates block values used with @ref lr20xx_radio_lora_set_syncword and with SX1276 LoRa compatibility enabled - * (@ref lr20xx_workarounds_lora_enable_sx1276_compatibility_mode). - * - * | SX126x/LR11xx/LR20xx syncword | LR20xx syncword SX127x compatibility | SX127x | - * | ----------------------------- | ------------------------------------ | --------------- | - * | 4 bits signed | 4 bits unsigned | 4 bits unsigned | - * | -8 | | | - * | -7 | | | - * | -6 | | | - * | -5 | | | - * | -4 | | | - * | -3 | | | - * | -2 | | | - * | -1 | | | - * | 0 | 0 | 0 | - * | 1 | 1 | 1 | - * | 2 | 2 | 2 | - * | 3 | 3 | 3 | - * | 4 | 4 | 4 | - * | 5 | 5 | 5 | - * | 6 | 6 | 6 | - * | 7 | 7 | 7 | - * | | 8 | 8 | - * | | 9 | 9 | - * | | 10 | 10 | - * | | 11 | 11 | - * | | 12 | 12 | - * | | 13 | 13 | - * | | 14 | 14 | - * | | 15 | 15 | - * - * @param[in] context Chip implementation context - * @param[in] syncword The syncword to configure - * - * @return lr20xx_status_t Operation status - * - * @see LR20XX_RADIO_LORA_SYNCWORD_LORAWAN_PUBLIC_NETWORK, - * LR20XX_RADIO_LORA_SYNCWORD_LORAWAN_PRIVATE_NETWORK +/* + * Set LoRa syncword. Default 0x12 (LoRaWAN private). 0x34 = LoRaWAN public. + * Syncword is two 4-bit nibbles: syncword = ((block1 & 0x0F) << 4) | (block2 & 0x0F). + * block1 must not be 0. With SX1276 compatibility disabled, blocks are 4-bit signed [-8:7]; + * with compatibility enabled they are 4-bit unsigned [0:15] matching SX127x. + * Different syncwords reduce but do not guarantee rejection of foreign packets. */ lr20xx_status_t lr20xx_radio_lora_set_syncword( const void* context, uint8_t syncword ); -/** - * @brief Configure the Channel Activity Detection (CAD) operation - * - * @param[in] context Chip implementation context - * @param[in] cad_params Structure of CAD parameter configuration - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_lora_set_cad - */ lr20xx_status_t lr20xx_radio_lora_configure_cad_params( const void* context, const lr20xx_radio_lora_cad_params_t* cad_params ); -/** - * @brief Start Channel Activity Detection (CAD) operation - * - * The CAD operation is a special mode of operation where the chip is looking for the presence of LoRa preamble symbols - * or for any Lora signal, depending on the setting in the @ref lr20xx_radio_lora_configure_cad_params command. - * - * At the end of the CAD operation a LR20XX_SYSTEM_IRQ_CAD_DONE is generated. If the CAD operation detects a signal, it - * also generates a LR20XX_SYSTEM_IRQ_CAD_DETECTED. - * - * Depending on the CAD configuration, the chip may either go back to the configured fallback mode, or enter the - * configured exit mode. - * - * If the exit mode is a radio operation the corresponding IRQ the CAD related IRQ(s) comes at the end CAD operation, - * and radio operations IRQ(s) of exit modes comes at the end of this radio operation. - * - * @param[in] context Chip implementation context - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_lora_configure_cad_params +/* + * Start CAD. Raises CAD_DONE IRQ; also raises CAD_DETECTED if signal found. Depending on cad_params exit_mode, + * chip either returns to fallback or starts the configured exit operation (RX or TX), whose IRQ fires after CAD IRQ. */ lr20xx_status_t lr20xx_radio_lora_set_cad( const void* context ); -/** - * @brief Get the internal statistics of received packets - * - * The internal statistics are reset on: - * - Power On Reset (POR) - * - sleep without memory retention - * - call to lr20xx_radio_common_reset_rx_stats - * - * @param[in] context Chip implementation context - * @param[out] statistics Pointer to a structure of statistic to populate with internal statistics - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_common_reset_rx_stats - */ +/* Get Rx statistics. Reset on POR, retentionless sleep, or lr20xx_radio_common_reset_rx_stats */ lr20xx_status_t lr20xx_radio_lora_get_rx_statistics( const void* context, lr20xx_radio_lora_rx_statistics_t* statistics ); -/** - * @brief Get the status of the last received LoRa packet - * - * Status is valid only after the end of a packet reception or CAD done and until the next LoRa packet configuration. - * - * CRC and coding rate source depends on the packet mode configured on the receiver: - * - If LR20XX_RADIO_LORA_PKT_EXPLICIT: it is obtained from the received payload - * - If LR20XX_RADIO_LORA_PKT_IMPLICIT: it is obtained from the receiver configuration - * - * @param[in] context Chip implementation context - * @param[out] pkt_status Pointer to a structure of packet status to populate - * - * @return lr20xx_status_t Operation status +/* + * Get last received LoRa packet status. Valid after RX_DONE or CAD_DONE, until next set_packet_params call. + * CRC/CR: from received header in EXPLICIT mode; from receiver config in IMPLICIT mode. */ lr20xx_status_t lr20xx_radio_lora_get_packet_status( const void* context, lr20xx_radio_lora_packet_status_t* pkt_status ); -/** - * @brief Set the address for filtering in reception - * - * @param[in] context Chip implementation context - * @param[in] address_offset Offset in byte of the address field in the payload (header not counted) - * @param[in] address_length Address length in byte - in [0:8], 0 disables LoRa address filtering - * @param[in] address Address - * - * @return lr20xx_status_t Operation status +/* + * Configure LoRa address filtering. address_offset: payload byte offset of address field (header not counted). + * address_length in [0:8]; 0 disables filtering. */ lr20xx_status_t lr20xx_radio_lora_set_address( const void* context, uint8_t address_offset, uint8_t address_length, const uint8_t* address ); -/** - * @brief Configure LoRa intra-packet frequency hopping - * - * If the intra-packet frequency hopping must be compatible with SX1276, then the workaround @ref - * lr20xx_workarounds_lora_freq_hop_enable_sx1276_compatibility_mode must be called after calling @ref - * lr20xx_radio_lora_set_freq_hop. - * - * @param[in] context Chip implementation context - * @param[in] cfg Frequency hopping configuration - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_workarounds_lora_freq_hop_enable_sx1276_compatibility_mode +/* + * Configure LoRa intra-packet frequency hopping. For SX1276 compatibility, call + * lr20xx_workarounds_lora_freq_hop_enable_sx1276_compatibility_mode after this. */ lr20xx_status_t lr20xx_radio_lora_set_freq_hop( const void* context, const lr20xx_radio_lora_hopping_cfg_t* cfg ); -/** - * @brief Configure the LoRa Channel Activity Detection (CAD) side detectors - * - * Up to three CAD side detectors can be configured. - * - * @param context Chip implementation context - * @param side_detector_cad_configurations Array of side detector CAD configurations - * @param n_side_detector_cad_configurations Number of CAD side detector configurations in @p - * side_detector_cad_configurations. It is up to the caller to ensure that @p side_detector_cad_configurations contains - * at least @p n_side_detector_cad_configurations elements - * - * @return lr20xx_status_t Operation status - */ +/* Configure up to 3 CAD side detectors (additional SF detectors for CAD). n_side_detector_cad_configurations in [0:3] */ lr20xx_status_t lr20xx_radio_lora_configure_side_detector_cad( const void* context, const lr20xx_radio_lora_side_detector_cad_configuration_t* side_detector_cad_configurations, uint8_t n_side_detector_cad_configurations ); -/** - * @brief Configure LoRa side detectors - * - * The side detectors allow to receive on multiple spreading factors, but on the same bandwidth as main detector. Up to - * three side detectors can be configured. - * - * To disable all side detectors, there are 2 options: - * - call this command with @p n_side_detector_cfgs set to 0. - * - call @ref lr20xx_radio_lora_set_modulation_params - * - * Once a packet is received, it is possible to know which SF has been demodulated thanks to @ref - * lr20xx_radio_lora_get_packet_status. - * - * Specificities related to the side detector configuration: - * - For normal Rx operations, the SF configured with @ref lr20xx_radio_lora_set_modulation_params must be lower than - * the SF of the side detectors - * - For CAD operations, the SF configured with @ref lr20xx_radio_lora_set_modulation_params must be higher than the - * SF of the side detectors - * - With BW set to @ref LR20XX_RADIO_LORA_BW_500 or higher, maximum 2 side detectors are allowed except if the SF - * configured with @ref lr20xx_radio_lora_set_modulation_params is @ref LR20XX_RADIO_LORA_SF10 or higher where only 1 - * side detector is allowed - * - All SF must be different - * - Difference between the highest and the lowest SF must be less than or equal to 4 - * - * @param[in] context Chip implementation context - * @param[in] side_detector_cfgs Array of side detector configuration to set. It is up to the caller to ensure - * there are at least @p n_side_detector_cfgs - * @param[in] n_side_detector_cfgs Number of side detector to configure. Un-configured side detectors are - * disabled. Value must be in range [0:3] included. - * - * @return lr20xx_status_t Operation status +/* + * Configure up to 3 LoRa side detectors (multi-SF Rx on same BW). n_side_detector_cfgs=0 or set_modulation_params + * disables all. Constraints: Rx-SF < side-SF; CAD-SF > side-SF; BW>=500 limits to 2 (or 1 if main SF>=SF10); + * all SFs distinct; max SF spread = 4. Demodulated SF readable via get_packet_status after RX_DONE. */ lr20xx_status_t lr20xx_radio_lora_configure_side_detectors( const void* context, const lr20xx_radio_lora_side_detector_cfg_t* side_detector_cfgs, uint8_t n_side_detector_cfgs ); -/** - * @brief Configure the LoRa syncwords for side detectors - * - * @param[in] context Chip implementation context - * @param[in] syncword Array of side detector syncword to set. It is up to the caller to ensure there are at least @p - * n_syncword - * @param[in] n_syncword Number of side detector syncword configure. Un-configured syncword are set to a default value. - * Value must be in range [0:3] included. - * - * @return lr20xx_status_t Operation status - */ +/* Set syncwords for up to 3 side detectors. n_syncword in [0:3]; unconfigured syncwords use default */ lr20xx_status_t lr20xx_radio_lora_set_side_detector_syncwords( const void* context, const uint8_t* syncword, uint8_t n_syncword ); -/** - * @brief Compute the numerator for LoRa time-on-air computation. - * - * @remark To get the actual time-on-air in seconds, this value has to be divided by the LoRa bandwidth in Hertz. - * - * @param [in] pkt_p Pointer to the structure holding the LoRa packet parameters - * @param [in] mod_p Pointer to the structure holding the LoRa modulation parameters - * - * @returns LoRa time-on-air numerator - */ +/* Time-on-air numerator (divide by BW in Hz to get seconds) */ uint32_t lr20xx_radio_lora_get_time_on_air_numerator( const lr20xx_radio_lora_pkt_params_t* pkt_p, const lr20xx_radio_lora_mod_params_t* mod_p ); -/** - * @brief Get the actual value in Hertz of a given LoRa bandwidth - * - * @param [in] bw LoRa bandwidth parameter - * - * @returns Actual LoRa bandwidth in Hertz - */ uint32_t lr20xx_radio_lora_get_bw_in_hz( lr20xx_radio_lora_bw_t bw ); - -/*! - * @brief Get the time on air in ms for LoRa transmission - * - * @param [in] pkt_p Pointer to a structure holding the LoRa packet parameters - * @param [in] mod_p Pointer to a structure holding the LoRa modulation parameters - * - * @returns Time-on-air value in ms for LoRa transmission - */ uint32_t lr20xx_radio_lora_get_time_on_air_in_ms( const lr20xx_radio_lora_pkt_params_t* pkt_p, const lr20xx_radio_lora_mod_params_t* mod_p ); -/** - * @brief Helper function to compute recommended ppm offset value from SF and BW - * - * This helper function provides recommended PPM offset configuration based on the following rules - * - @ref LR20XX_RADIO_LORA_NO_PPM for all spreading factors, except for @ref LR20XX_RADIO_LORA_SF11 and @ref - * LR20XX_RADIO_LORA_SF12 - * - @ref LR20XX_RADIO_LORA_PPM_1_4 for bandwidths @ref LR20XX_RADIO_LORA_BW_812, @ref LR20XX_RADIO_LORA_BW_406 and - * @ref LR20XX_RADIO_LORA_BW_203 to ensure SX128x compatibility - * - @ref LR20XX_RADIO_LORA_NO_PPM for bandwidths @ref LR20XX_RADIO_LORA_BW_1000 and @ref LR20XX_RADIO_LORA_BW_500 - * - @ref LR20XX_RADIO_LORA_NO_PPM for bandwidth @ref LR20XX_RADIO_LORA_BW_250 and spreading factor @ref - * LR20XX_RADIO_LORA_SF11 - * - @ref LR20XX_RADIO_LORA_PPM_1_4 for bandwidth @ref LR20XX_RADIO_LORA_BW_250 and spreading factor @ref - * LR20XX_RADIO_LORA_SF12 - * - @ref LR20XX_RADIO_LORA_PPM_1_4 otherwise - * - * | Bandwidths | @ref LR20XX_RADIO_LORA_SF12 | @ref LR20XX_RADIO_LORA_SF11 | other spreading factors | - * | -- | -- | -- | -- | - * | @ref LR20XX_RADIO_LORA_BW_1000 | @ref LR20XX_RADIO_LORA_NO_PPM ||| - * | @ref LR20XX_RADIO_LORA_BW_500 | @ref LR20XX_RADIO_LORA_NO_PPM ||| - * | @ref LR20XX_RADIO_LORA_BW_250 | @ref LR20XX_RADIO_LORA_PPM_1_4 | @ref LR20XX_RADIO_LORA_NO_PPM || - * | @ref LR20XX_RADIO_LORA_BW_812 | @ref LR20XX_RADIO_LORA_PPM_1_4 || @ref LR20XX_RADIO_LORA_NO_PPM | - * | @ref LR20XX_RADIO_LORA_BW_406 | @ref LR20XX_RADIO_LORA_PPM_1_4 || @ref LR20XX_RADIO_LORA_NO_PPM | - * | @ref LR20XX_RADIO_LORA_BW_203 | @ref LR20XX_RADIO_LORA_PPM_1_4 || @ref LR20XX_RADIO_LORA_NO_PPM | - * | other bandwidths | @ref LR20XX_RADIO_LORA_PPM_1_4 || @ref LR20XX_RADIO_LORA_NO_PPM | - * - * @param sf Spreading factor - * @param bw Bandwidth - * - * @return The recommended PPM offset configuration for the given spreading factor and bandwidth - * - * @see lr20xx_radio_lora_set_modulation_params +/* + * Recommended ppm_offset for given SF/BW: + * NO_PPM: SF<11, or BW>=500, or BW=250+SF11 + * PPM_1_4: SF11/12 at BW<=406 (SX128x compat), BW=250+SF12, or SF12 at other narrow BWs + * See lr20xx_radio_lora_set_modulation_params. */ lr20xx_radio_lora_ppm_t lr20xx_radio_lora_get_recommended_ppm_offset( lr20xx_radio_lora_sf_t sf, lr20xx_radio_lora_bw_t bw ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora_types.h index 7d55c7d..d3ed6b5 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora_types.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora_types.h @@ -52,14 +52,10 @@ extern "C" { * --- PUBLIC MACROS ----------------------------------------------------------- */ -/** - * @brief LoRa syncword value for LoRaWAN public networks - */ +/*! @brief LoRa syncword for LoRaWAN public networks */ #define LR20XX_RADIO_LORA_SYNCWORD_LORAWAN_PUBLIC_NETWORK ( 0x34 ) -/** - * @brief LoRa syncword value for LoRaWAN private networks - */ +/*! @brief LoRa syncword for LoRaWAN private networks */ #define LR20XX_RADIO_LORA_SYNCWORD_LORAWAN_PRIVATE_NETWORK ( 0x12 ) /* @@ -178,6 +174,7 @@ typedef enum //!< mode. Otherwise it enters in fallback mode LR20XX_RADIO_LORA_CAD_EXIT_MODE_TX = 0x10, //!< If the CAD operation does not detect an activity, the chip enters //!< in TX mode. Otherwise it enters in fallback mode + //!< (0x10, not 0x02: register encoding gap in LR2021 datasheet) } lr20xx_radio_lora_cad_exit_mode_t; /** diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook.h index c7da789..c86924b 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook.h @@ -68,171 +68,44 @@ extern "C" { * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- */ -/** - * @brief Set the modulation parameters for OOK packets - * - * The workaround @ref lr20xx_workarounds_dcdc_configure must be called for Rx sub-GHz operations with regulator @ref - * LR20XX_SYSTEM_REG_MODE_DCDC after this function to avoid possible RF sensitivity degradation. - * - * @note This function automatically applies the workaround @ref lr20xx_workarounds_dcdc_configure unless the macro @p - * LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE is defined at compile time. - * - * @param[in] context Chip implementation context - * @param[in] params Structure of OOK modulation configuration - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_workarounds_dcdc_configure +/* + * Set OOK modulation params. + * Applies lr20xx_workarounds_dcdc_configure automatically unless LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE. */ lr20xx_status_t lr20xx_radio_ook_set_modulation_params( const void* context, const lr20xx_radio_ook_mod_params_t* params ); -/** - * @brief Set the packet parameters for OOK packets - * - * The OOK packet configuration with header explicit and CRC disabled is known to generate incorrect packet reception. - * - * @param[in] context Chip implementation context - * @param[in] params Structure of the OOK packet parameter to configure - * - * @returns Operation status - */ +/* Set OOK packet params. Note: explicit header + CRC disabled causes incorrect reception. */ lr20xx_status_t lr20xx_radio_ook_set_packet_params( const void* context, const lr20xx_radio_ook_pkt_params_t* params ); -/** - * @brief Set the CRC configuration for OOK packets - * - * @param[in] context Chip implementation context - * @param[in] crc_polynomial Polynomial to use for CRC LFSR - * @param[in] crc_seed LFSR initial value - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_ook_set_crc_params( const void* context, uint32_t crc_polynomial, uint32_t crc_seed ); -/** - * @brief Set the syncword for OOK packets - * - * The argument \p syncword is a 4-byte array. However, it should be understood as 32-bit variable, where Most - * Significant Bit is the MSB of syncword[0] and Least Significant Bit is LSB of syncword[3]. - * - * For instance: - * - * @code{.c} - * syncword = 0x0000AA67 - * nb_bits = 12 - * @endcode - * - * Then, syncword in bits is: - * - * @verbatim - * 0...0 01010101 01100111 - * ^ ^ ^ - * MSB nb_bit'th LSB - * @endverbatim - * - * Then, the bit stream sent over the air will be - * - if \p bit_order == LR20XX_RADIO_OOK_SYNCWORD_LSBF: - * @verbatim - * 111001101010 - * @endverbatim - * - if \p bit_order == LR20XX_RADIO_OOK_SYNCWORD_MSBF: - * @verbatim - * 010101100111 - * @endverbatim - * - * @param[in] context Chip implementation context - * @param[in] syncword Array holding the syncword value. It is up to the caller that this array holds at least - * LR20XX_RADIO_OOK_SYNCWORD_LENGTH bytes, even if nb_bits is not 32 - * @param[in] nb_bits The number of significant bits in syncword to use for syncword. The significant bits are taken as - * Least Significant Bits of \p syncword argument. Value in range of [0:32] - * @param[in] bit_order The order of transmission of the selected bits of syncword over-the-air - * - * @returns Operation status +/* + * Set OOK syncword. syncword is a 32-bit value stored MSB-first in 4 bytes; nb_bits LSBs are used. + * bit_order controls OTA transmission order (LSBF or MSBF). + * Example: syncword=0x0000AA67, nb_bits=12 → bits 0xA67 (12 LSBs). */ lr20xx_status_t lr20xx_radio_ook_set_syncword( const void* context, const uint8_t syncword[LR20XX_RADIO_OOK_SYNCWORD_LENGTH], uint8_t nb_bits, lr20xx_radio_ook_syncword_bit_order_t bit_order ); -/** - * @brief Set the node and broadcast addresses for OOK packets - * - * @param[in] context Chip implementation context - * @param[in] node_address Node address - * @param[in] broadcast_address Broadcast address - * - * @returns Operation status - */ lr20xx_status_t lr20xx_radio_ook_set_addresses( const void* context, uint8_t node_address, uint8_t broadcast_address ); -/** - * @brief Get the internal statistics of received OOK packets - * - * The internal statistics are reset on: - * - Power On Reset (POR) - * - sleep without memory retention - * - call to lr20xx_radio_common_reset_rx_stats - * - * @param[in] context Chip implementation context - * @param[out] statistics Pointer to a structure of statistic to populate with internal statistics - * - * @return lr20xx_status_t Operation status - * - * @see lr20xx_radio_common_reset_rx_stats - */ +/* Get OOK Rx statistics; reset on POR, retention-less sleep, or lr20xx_radio_common_reset_rx_stats */ lr20xx_status_t lr20xx_radio_ook_get_rx_statistics( const void* context, lr20xx_radio_ook_rx_statistics_t* statistics ); -/** - * @brief Get the status of the last received OOK packet - * - * Availability of the packet status fields depend on the IRQ as follows: - * - Available from LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID: - * - lr20xx_radio_ook_packet_status_t.rssi_on_in_dbm - * - lr20xx_radio_ook_packet_status_t.rssi_on_half_dbm_count - * - Available from LR20XX_SYSTEM_IRQ_RX_DONE: - * - lr20xx_radio_ook_packet_status_t.rssi_avg_in_dbm - * - lr20xx_radio_ook_packet_status_t.rssi_avg_half_dbm_count - * - lr20xx_radio_ook_packet_status_t.is_addr_match_broadcast - * - lr20xx_radio_ook_packet_status_t.is_addr_match_node - * - * @param[in] context Chip implementation context - * @param[out] pkt_status Pointer to a structure of packet status to populate - * - * @return lr20xx_status_t Operation status +/* + * Get status of last received OOK packet. + * rssi_on/syncword available from SYNC_WORD_HEADER_VALID IRQ. + * rssi_avg/addr_match available from RX_DONE IRQ. */ lr20xx_status_t lr20xx_radio_ook_get_packet_status( const void* context, lr20xx_radio_ook_packet_status_t* pkt_status ); -/** - * @brief Configure the Rx detector OOK packet - * - * @param[in] context Chip implementation context - * @param[in] rx_detector Rx detector configuration - * - * @return lr20xx_status_t Operation status - */ lr20xx_status_t lr20xx_radio_ook_set_rx_detector( const void* context, const lr20xx_radio_ook_rx_detector_t* rx_detector ); - -/** - * @brief Set whitening parameters for OOK packet - * - * @param[in] context Chip implementation context - * @param[in] params Whitening parameters - * - * @return lr20xx_status_t Operation status - */ lr20xx_status_t lr20xx_radio_ook_set_whitening_params( const void* context, const lr20xx_radio_ook_whitening_params_t* params ); -/** - * @brief Get the time on air in ms for OOK transmission - * - * @param [in] pkt_p Pointer to a structure holding the OOK packet parameters - * @param [in] mod_p Pointer to a structure holding the OOK modulation parameters - * @param [in] syncword_len_in_bit Syncword length in bit - * - * @returns Time-on-air value in ms for OOK transmission - */ uint32_t lr20xx_radio_ook_get_time_on_air_in_ms( const lr20xx_radio_ook_pkt_params_t* pkt_p, const lr20xx_radio_ook_mod_params_t* mod_p, uint8_t syncword_len_in_bit ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook_types.h index 7ed94a9..f64cbf2 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook_types.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_ook_types.h @@ -53,10 +53,7 @@ extern "C" { * --- PUBLIC MACROS ----------------------------------------------------------- */ -/** - * @brief Length in bytes of the OOK syncword - * - */ +/*! @brief OOK syncword length in bytes */ #define LR20XX_RADIO_OOK_SYNCWORD_LENGTH ( 4 ) /* @@ -241,7 +238,7 @@ typedef struct } lr20xx_radio_ook_rx_detector_t; /** - * @brief Rx detector configuration for OOK packet + * @brief Whitening configuration for OOK packet */ typedef struct { diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_regmem.c b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.c index 88a4557..a2348c8 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_regmem.c +++ b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.c @@ -54,6 +54,7 @@ #define LR20XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH ( 2 + 3 + 4 + 4 ) #define LR20XX_REGMEM_READ_REGMEM32_CMD_LENGTH ( 2 + 3 + 1 ) +/* 32 words × 4 bytes = 128 bytes max payload; 256 provides headroom */ #define LR20XX_REGMEM_BUFFER_SIZE_MAX ( 256 ) /* @@ -81,54 +82,22 @@ enum * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- */ -/*! - * @brief Helper function that fill both cbuffer with opcode and memory address - * - * It is typically used in read/write regmem32 functions. - * - * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! - */ +/* @warning caller must ensure cbuffer is sized for opcode + 3-byte address */ static void lr20xx_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ); -/*! - * @brief Helper function that fill both cbuffer with opcode memory address, and data length to read - * - * It is typically used in read functions. - * - * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! - */ +/* @warning caller must ensure cbuffer is sized for opcode + 3-byte address + length byte */ static void lr20xx_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, uint8_t length ); -/*! - * @brief Helper function that fill both cbuffer with data - * - * It is typically used in write write regmem32 functions. - * - * @warning It is up to the caller to ensure cdata is big enough to contain all data! - */ +/* @warning caller must ensure cdata is sized for data_length × 4 bytes */ static void lr20xx_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ); -/*! - * @brief Helper function that fill both cbuffer and cdata buffers with opcode, memory address and data - * - * It is typically used to factorize and write regmem32 operations. Behind the scene it calls the other helpers - * lr20xx_regmem_fill_cbuffer_opcode_address and lr20xx_regmem_fill_cdata. - * - * @warning It is up to the caller to ensure cbuffer and cdata are big enough to contain their respective information! - */ +/* @warning caller must ensure cbuffer and cdata are appropriately sized */ static void lr20xx_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, uint16_t opcode, uint32_t address, const uint32_t* data, uint8_t data_length ); -/*! - * @brief Helper function that convert an array of uint8_t into an array of uint32_t - * - * Typically used in the read function returning uint32_t array. - * - * @warning It is up to the caller to ensure the raw_buffer is of length at least "out_buffer_length * - * sizeof(uint32_t)"! - */ +/* @warning caller must ensure raw_buffer is at least out_buffer_length × 4 bytes */ static void lr20xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, uint8_t out_buffer_length ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_regmem.h b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.h index 188defd..f10b509 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_regmem.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.h @@ -52,9 +52,7 @@ extern "C" { * --- PUBLIC MACROS ----------------------------------------------------------- */ -/*! - * @brief Maximum number of words that can be written to / read from a LR20XX chip with regmem32 commands - */ +/*! @brief Max words per regmem32 read/write (LR2021 datasheet §5.4.3) */ #define LR20XX_REGMEM_MAX_WRITE_READ_WORDS 32 /* diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_status.h b/zephcore/adapters/radio/lr20xx/lr20xx_status.h index e9a9b69..8fa8f57 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_status.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_status.h @@ -75,7 +75,7 @@ typedef enum lr20xx_status_e { LR20XX_STATUS_OK = 0, - LR20XX_STATUS_ERROR = 3, + LR20XX_STATUS_ERROR = 3, /* Must match lr20xx_hal_status_t ERROR value */ } lr20xx_status_t; /* diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_system.c b/zephcore/adapters/radio/lr20xx/lr20xx_system.c index 4ea92a1..ea32076 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_system.c +++ b/zephcore/adapters/radio/lr20xx/lr20xx_system.c @@ -78,35 +78,12 @@ #define LR20XX_SYSTEM_SET_TEMP_COMP_CFG_CMD_LENGTH ( 2 + 1 ) #define LR20XX_SYSTEM_SET_NTC_PARAMS_CMD_LENGTH ( 2 + 5 ) -/*! - * @brief Length in byte of the status returned by the transceiver - */ -#define LR20XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ( 6 ) - -/*! - * @brief Length in byte of the version returned by the transceiver - */ -#define LR20XX_SYSTEM_VERSION_LENGTH ( 2 ) - -/*! - * @brief Length in byte of the error list returned by the transceiver - */ -#define LR20XX_SYSTEM_ERRORS_LENGTH ( 2 ) - -/*! - * @brief Length in byte of the random number returned by the transceiver - */ -#define LR20XX_SYSTEM_RANDOM_NUMBER_LENGTH ( 4 ) - -/*! - * @brief Length in byte of the measure (temperature or voltage) returned by the transceiver - */ -#define LR20XX_SYSTEM_MEASURE_LENGTH ( 2 ) - -/*! - * @brief Length in byte of the interrupt flags returned by the transceiver - */ -#define LR20XX_SYSTEM_INTERRUPTS_LENGTH ( 4 ) +#define LR20XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ( 6 ) /* stat1 + stat2 + 4-byte IRQ mask */ +#define LR20XX_SYSTEM_VERSION_LENGTH ( 2 ) /* major + minor */ +#define LR20XX_SYSTEM_ERRORS_LENGTH ( 2 ) /* 16-bit error bitmask */ +#define LR20XX_SYSTEM_RANDOM_NUMBER_LENGTH ( 4 ) /* 32-bit random number */ +#define LR20XX_SYSTEM_MEASURE_LENGTH ( 2 ) /* vbat / temp measurement */ +#define LR20XX_SYSTEM_INTERRUPTS_LENGTH ( 4 ) /* 32-bit IRQ mask */ static const lr20xx_system_dio_t dio_list[] = { LR20XX_SYSTEM_DIO_5, LR20XX_SYSTEM_DIO_6, LR20XX_SYSTEM_DIO_7, LR20XX_SYSTEM_DIO_8, @@ -118,9 +95,7 @@ static const lr20xx_system_dio_t dio_list[] = { * --- PRIVATE TYPES ----------------------------------------------------------- */ -/*! - * @brief Operating codes for system related operations - */ +/* System command opcodes */ enum { LR20XX_SYSTEM_GET_STATUS_OC = 0x0100, @@ -160,24 +135,9 @@ enum * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- */ -/*! - * @brief Fill stat1 structure with data from stat1_byte - * - * @remark If \p stat1 is NULL, the function does not perform any operation - * - * @param [in] stat1_byte stat1 byte - * @param [out] stat1 stat1 structure - */ +/* Parse stat1 byte into stat1 struct; no-op if stat1 is NULL */ static void lr20xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr20xx_system_stat1_t* stat1 ); - -/*! - * @brief Fill stat2 structure with data from stat2_byte - * - * @remark If \p stat2 is NULL, the function does not perform any operation - * - * @param [in] stat2_byte stat2 byte - * @param [out] stat2 stat2 structure - */ +/* Parse stat2 byte into stat2 struct; no-op if stat2 is NULL */ static void lr20xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr20xx_system_stat2_t* stat2 ); /* @@ -468,7 +428,7 @@ lr20xx_status_t lr20xx_system_get_temp( const void* context, lr20xx_system_value if( status == LR20XX_STATUS_OK ) { - *temp = ( uint16_t ) ( ( ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1] ) >> 3 ); + *temp = ( uint16_t ) ( ( ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1] ) >> 3 ); /* 3 LSBs are status bits, not measurement data */ } return status; @@ -504,8 +464,8 @@ lr20xx_status_t lr20xx_system_set_sleep_mode( const void* context, const lr20xx_ const uint8_t cbuffer[LR20XX_SYSTEM_SET_SLEEP_MODE_CMD_LENGTH] = { ( uint8_t ) ( LR20XX_SYSTEM_SET_SLEEP_MODE_OC >> 8 ), ( uint8_t ) ( LR20XX_SYSTEM_SET_SLEEP_MODE_OC >> 0 ), - ( uint8_t ) ( ( ( sleep_cfg->is_ram_retention_enabled == true ) ? 0x02 : 0x00 ) + - ( ( sleep_cfg->is_clk_32k_enabled == true ) ? 0x01 : 0x00 ) ), + ( uint8_t ) ( ( ( sleep_cfg->is_ram_retention_enabled == true ) ? 0x02 : 0x00 ) + /* bit[1]: RAM retention */ + ( ( sleep_cfg->is_clk_32k_enabled == true ) ? 0x01 : 0x00 ) ), /* bit[0]: 32kHz clock */ ( uint8_t ) ( sleep_time >> 24 ), ( uint8_t ) ( sleep_time >> 16 ), ( uint8_t ) ( sleep_time >> 8 ), diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_system.h b/zephcore/adapters/radio/lr20xx/lr20xx_system.h index 062ce47..b0fd21f 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_system.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_system.h @@ -68,471 +68,147 @@ extern "C" { * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- */ -/*! - * @brief Reset the radio - * - * @param [in] context Chip implementation context. - * - * @returns Operation status - */ lr20xx_status_t lr20xx_system_reset( const void* context ); - -/*! - * @brief Wake the radio up from sleep mode. - * - * @param [in] context Chip implementation context. - * - * @returns Operation status - */ lr20xx_status_t lr20xx_system_wakeup( const void* context ); -/*! - * @brief Return stat1, stat2, and irq_status - * - * @param [in] context Chip implementation context - * @param [out] stat1 Pointer to a variable for holding stat1. Can be NULL. - * @param [out] stat2 Pointer to a variable for holding stat2. Can be NULL. - * @param [out] irq_status Pointer to a variable for holding irq_status. Can be NULL. - * - * @returns Operation status - * - * @remark To simplify system integration, this function does not actually execute the GetStatus command, which would - * require bidirectional SPI communication. It obtains the stat1, stat2, and irq_status values by performing an ordinary - * SPI read (which is required to send null/NOP bytes on the MOSI line). This is possible since the LR20XX returns these - * values automatically whenever a read that does not directly follow a response-carrying command is performed. - * Unlike with the GetStatus command, however, the reset status information is NOT cleared by this command. The function - * @ref lr20xx_system_clear_reset_status_info may be used for this purpose when necessary. +/* + * Return stat1, stat2, and irq_status. Any pointer may be NULL. + * Implemented as a bare SPI read (NOP bytes on MOSI); does NOT execute the GetStatus command. + * The LR20XX prefixes every SPI read response with stat1/stat2/irq_status automatically. + * Reset status in stat2 is NOT cleared by this call — use lr20xx_system_clear_reset_status_info. */ lr20xx_status_t lr20xx_system_get_status( const void* context, lr20xx_system_stat1_t* stat1, lr20xx_system_stat2_t* stat2, lr20xx_system_irq_mask_t* irq_status ); -/*! - * @brief Clear the reset status information stored in stat2 - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ lr20xx_status_t lr20xx_system_clear_reset_status_info( const void* context ); -/*! - * @brief Return the version of the system - * - * The following table provides expected version per LR20xx derivatives: - * - * | Derivative | Version major | Version minor | - * | ---------- | ------------- | ------------- | - * | LR2021 | 0x01 | 0x18 | - * | LR2022 | 0x02 | 0x00 | - * - * @param [in] context Chip implementation context - * @param [out] version Pointer to the structure holding the system version - * - * @returns Operation status +/* + * Read firmware version. Expected: LR2021 = major 0x01 / minor 0x18; LR2022 = major 0x02 / minor 0x00. */ lr20xx_status_t lr20xx_system_get_version( const void* context, lr20xx_system_version_t* version ); -/*! - * @brief Return the system errors - * - * Errors may be fixed following: - * - calibration error can be fixed by attempting another RC calibration; - * - XOsc related errors may be due to hardware problems, can be fixed by reset; - * - PLL lock related errors can be due to not-locked PLL, or by attempting to use an out-of-band frequency, can be - * fixed by executing a PLL calibration, or by using other frequencies. - * - * @param [in] context Chip implementation context - * @param [out] errors Pointer to a value holding error flags - * - * @returns Operation status - * - * @see lr20xx_system_calibrate, lr20xx_radio_common_calibrate_front_end, lr20xx_system_clear_errors +/* + * Return system error flags. Remediation: calibration errors → retry RC calibration; + * XOSC errors → hardware issue, reset; PLL lock errors → run PLL calibration or change frequency. */ lr20xx_status_t lr20xx_system_get_errors( const void* context, lr20xx_system_errors_t* errors ); - -/*! - * @brief Clear all error flags pending. - * - * @param [in] context Chip implementation context - * - * @returns Operation status - * - * @see lr20xx_system_get_errors - */ lr20xx_status_t lr20xx_system_clear_errors( const void* context ); -/** - * @brief Returns the number of available DIOs - * - * @remark Also is the valid range for lr20xx_system_dio_get_nth. - * - * @return uint8_t the number of valid DIOs. - * - * @see lr20xx_system_dio_get_nth - */ +/* Return number of available DIOs (valid range for lr20xx_system_dio_get_nth) */ uint8_t lr20xx_system_dio_get_count( void ); -/** - * @brief Returns the nth value from the enum lr20xx_system_dio_t - * - * @param [in] nth from 0 to lr20xx_system_dio_get_count() - 1 - * @param [out] dio Pointer to a value holding the corresponding DIO enum - * @return true if nth is a valid number, false otherwise - * - * @see lr20xx_system_dio_get_count, lr20xx_system_dio_t - */ +/* Return nth DIO enum value; returns false if nth >= dio_count */ bool lr20xx_system_dio_get_nth( uint8_t nth, lr20xx_system_dio_t* dio ); -/*! - * @brief Configure the function and the drive mode of a given DIO - * - * @remark @p drive is applied when entering sleep mode if @p func is not @ref LR20XX_SYSTEM_DIO_FUNC_NONE. - * When leaving sleep mode without retention, @p drive is reset to @ref LR20XX_SYSTEM_DIO_DRIVE_NONE (except for @p dio - * LR20XX_SYSTEM_DIO_5 and LR20XX_SYSTEM_DIO_6 where @p drive is reset to LR20XX_SYSTEM_DIO_DRIVE_PULL_UP). - * - * @remark The state of @p dio is reevaluated when sending this command - * - * @remark When @p func is set to either LR20XX_SYSTEM_DIO_FUNC_TX_TRIGGER or LR20XX_SYSTEM_DIO_FUNC_RX_TRIGGER, default - * timeout set through @ref lr20xx_radio_common_set_default_rx_tx_timeout or @ref - * lr20xx_radio_common_set_default_rx_tx_timeout_in_rtc_step is used when radio operation is triggered - * - * @remark On @ref LR20XX_SYSTEM_DIO_5, only @ref LR20XX_SYSTEM_DIO_DRIVE_PULL_UP for @p drive - * - * @remark Function @ref LR20XX_SYSTEM_DIO_FUNC_LF_CLK_OUT can only be used if @p dio is one of: - * - LR20XX_SYSTEM_DIO_7 - * - LR20XX_SYSTEM_DIO_8 - * - LR20XX_SYSTEM_DIO_9 - * - LR20XX_SYSTEM_DIO_10 - * - LR20XX_SYSTEM_DIO_11 - * - * @ref LR20XX_SYSTEM_DIO_5 and @ref LR20XX_SYSTEM_DIO_6 must be configured explicitly to function @ref - * LR20XX_SYSTEM_DIO_FUNC_NONE if they are connected to an external component that could toggle their state between a - * cold start or a start without retention and the configuration of a function. - * - * @param [in] context Chip implementation context - * @param [in] dio DIO pin - * @param [in] func DIO pin function - * @param [in] drive DIO pin drive - * - * @returns Operation status +/* + * Configure DIO function and drive mode. DIO state is re-evaluated on command. + * drive applied on sleep entry (if func != NONE); reset to NONE (or PULL_UP for DIO5/6) on wake without retention. + * TX/RX_TRIGGER uses the default timeout configured via set_default_rx_tx_timeout. + * LF_CLK_OUT only valid on DIO7–DIO11. + * DIO5/DIO6 must be set to FUNC_NONE if connected to external components that could toggle them during cold-start. */ lr20xx_status_t lr20xx_system_set_dio_function( const void* context, lr20xx_system_dio_t dio, lr20xx_system_dio_func_t func, lr20xx_system_dio_drive_t drive ); -/*! - * @brief Set the RF switch configurations for a given DIO - * - * @remark The state of @p dio is reevaluated when sending this command - * - * @param [in] context Chip implementation context - * @param [in] dio DIO pin - * @param [in] rf_switch_cfg Pointer to a structure that holds the RF switch configuration for @p dio - * - * @returns Operation status - */ +/* Set RF switch configuration for a DIO; DIO state re-evaluated on command */ lr20xx_status_t lr20xx_system_set_dio_rf_switch_cfg( const void* context, lr20xx_system_dio_t dio, const lr20xx_system_dio_rf_switch_cfg_t rf_switch_cfg ); -/*! - * @brief Set the interrupt configurations for a given DIO - * - * It is not possible to set the same IRQ on multiple DIOs. Only the last mapping for each IRQ is take into account. - * - * @remark The state of \p dio is reevaluated when sending this command - * - * @param [in] context Chip implementation context - * @param [in] dio DIO pin - * @param [in] irq_cfg Interrupt mask for \p dio - * - * @returns Operation status +/* + * Map IRQs to a DIO. Each IRQ can only be mapped to one DIO at a time (last write wins). + * DIO state re-evaluated on command. */ lr20xx_status_t lr20xx_system_set_dio_irq_cfg( const void* context, lr20xx_system_dio_t dio, const lr20xx_system_irq_mask_t irq_cfg ); -/*! - * @brief Clear requested bits in the internal pending interrupt register - * - * @param [in] context Chip implementation context - * @param [in] irqs_to_clear Variable that holds the interrupts to be cleared - * - * @returns Operation status - * - * @see lr20xx_system_get_and_clear_irq_status - */ lr20xx_status_t lr20xx_system_clear_irq_status( const void* context, const lr20xx_system_irq_mask_t irqs_to_clear ); -/** - * @brief This helper function clears any radio irq status flags that are set and returns the flags that were cleared. - * - * @param [in] context Chip implementation context. - * @param [out] irq Pointer to a variable for holding the system interrupt status. - * - * @returns Operation status - * - * @see lr20xx_system_clear_irq_status - */ +/* Atomically clear and return pending IRQ flags */ lr20xx_status_t lr20xx_system_get_and_clear_irq_status( const void* context, lr20xx_system_irq_mask_t* irq ); -/*! - * @brief Configure the source of the Low Frequency Clock (LF_CLK) - * - * When switching LF CLK to external source (@ref LR20XX_SYSTEM_LFCLK_EXT), the external clock source must be already - * running, and shall keep running afterwards. - * - * @param [in] context Chip implementation context - * @param [in] lfclock_cfg Low frequency clock configuration - * - * @returns Operation status - * - * @see lr20xx_system_calibrate, lr20xx_radio_common_calibrate_front_end, lr20xx_system_set_dio_function +/* + * Select LF clock source. When switching to LR20XX_SYSTEM_LFCLK_EXT, the external clock must already be running + * and must remain running. Call lr20xx_system_calibrate after changing LF clock source. */ lr20xx_status_t lr20xx_system_cfg_lfclk( const void* context, const lr20xx_system_lfclk_cfg_t lfclock_cfg ); -/*! - * @brief Configure the High Frequency clock scaling on the output - * - * This command sets the HF clock scaling on the DIO configured with functionality - * LR20XX_SYSTEM_DIO_FUNC_HF_CLK_OUT through lr20xx_system_set_dio_function - * - * @param [in] context Chip implementation context - * @param [in] hf_clk_scaling High frequency output scaling - * - * @returns Operation status - * - * @see lr20xx_system_set_dio_function +/* + * Set HF clock output scaling on the DIO configured with LR20XX_SYSTEM_DIO_FUNC_HF_CLK_OUT. */ lr20xx_status_t lr20xx_system_cfg_clk_output( const void* context, lr20xx_system_hf_clk_scaling_t hf_clk_scaling ); -/*! - * @brief Enable the usage of a TCXO as HF clock and configure supply voltage & start delay - * - * \p start_delay_in_32mhz_step is the time the firmware waits before going into RF mode, expressed in number of 32MHz - * clock ticks. - * The timeout duration is given by: \f$ start\_delay\_in\_ns = start\_delay\_in\_32mhz\_step \times 31.25 \f$ - * - * The TCXO mode can be disabled by setting \p start_delay_in_32mhz_step to 0. - * - * In the situation where the TCXO has not started within \p start_delay_in_32mhz_step then the error bit - * LR20XX_SYSTEM_ERRORS_HF_XOSC_START_MASK will be set. It can be checked with a call to \p lr20xx_system_get_errors. - * - * It must be noted that the TCXO start time duration can last twice the duration of \p start_delay_in_32mhz_step - * lr20xx_system_calibrate if the internal 32MHz RC clock source is not calibrated. Refer to \p lr20xx_system_calibrate - * for details. - * - * The maximum value for \p start_delay_in_32mhz_step is 0xFFFFFFFF. - * - * @param [in] context Chip implementation context - * @param [in] supply_voltage Supply voltage value - * @param [in] start_delay_in_32mhz_step Gating time before which the radio starts its RF operation - * - * @returns Operation status - * - * @see lr20xx_system_calibrate, lr20xx_radio_common_calibrate_front_end, lr20xx_system_get_errors +/* + * Configure TCXO supply voltage and start delay. start_delay_in_32mhz_step is a gating timeout in 32MHz ticks + * (1 tick = 31.25ns); max value 0xFFFFFFFF. Set to 0 to disable TCXO mode. + * If TCXO does not start within the delay, LR20XX_SYSTEM_ERRORS_HF_XOSC_START_MASK is set (check get_errors). + * If 32MHz RC is uncalibrated, actual start time may be up to 2x the configured delay. */ lr20xx_status_t lr20xx_system_set_tcxo_mode( const void* context, const lr20xx_system_tcxo_supply_voltage_t supply_voltage, const uint32_t start_delay_in_32mhz_step ); -/*! - * @brief Configure the regulator mode to be used in specific modes - * - * \p reg_mode defines if the DC-DC converter is switched on in the following modes: STANDBY XOSC, FS, RX, TX. - * - * @param [in] context Chip implementation context - * @param [in] reg_mode Regulator mode configuration - * - * @returns Operation status - */ +/* Set regulator mode; controls whether DCDC is enabled in STANDBY_XOSC, FS, RX, TX modes */ lr20xx_status_t lr20xx_system_set_reg_mode( const void* context, const lr20xx_system_reg_mode_t reg_mode ); -/*! - * @brief Calibrate the requested blocks - * - * This function can be called in any mode of the chip. - * - * The chip will return to standby RC mode on exit. Potential calibration issues can be read out with - * lr20xx_system_get_errors command. - * - * The calibration should be executed at boot. The calibration can then be executed again: - * - @ref lr20xx_system_calibration_e::LR20XX_SYSTEM_CALIB_AAF_MASK : should be calibrated again for a temperature - * change superior to 20 degree Celsius - * - @ref lr20xx_system_calibration_e::LR20XX_SYSTEM_CALIB_MU_MASK : initial calibration is enough - * - * @param [in] context Chip implementation context - * @param [in] blocks_to_calibrate Blocks to be calibrated - bitfield built with lr20xx_system_calibration_e - * - * @returns Operation status - * - * @see lr20xx_system_get_errors +/* + * Calibrate selected blocks (bitmask of lr20xx_system_calibration_e). Can be called from any mode. + * Chip returns to STANDBY_RC on exit. Errors readable via lr20xx_system_get_errors. + * Run at boot; re-run AAF_MASK if temperature changes by >20°C; MU_MASK needs boot calibration only. */ lr20xx_status_t lr20xx_system_calibrate( const void* context, const lr20xx_system_calibration_mask_t blocks_to_calibrate ); -/*! - * @brief Get the value of the power supply voltage - * - * If @p format is set to LR20XX_SYSTEM_VALUE_FORMAT_RAW, Vbat value (in [V]) is a function of Vana (typ. 1.35V) and can - * be obtained using the following formula: \f$ Vbat_{V} = (\frac{vbat}{8192} \times 5 - 1) \times Vana \f$ where vbat - * is a 13-bit long value - * - * If @p format is set to LR20XX_SYSTEM_VALUE_FORMAT_UNIT, the power supply voltage is given in [mV] - * - * @param [in] context Chip implementation context - * @param [in] format Format of the returned value of @p vbat - * @param [in] res Resolution of the measure of @p vbat - * @param [out] vbat A pointer to the @p vbat value - * - * @returns Operation status +/* + * Read supply voltage. RAW format: Vbat_V = (vbat/8192 × 5 - 1) × Vana (Vana typ. 1.35V; vbat is 13-bit). + * UNIT format: result in mV. */ lr20xx_status_t lr20xx_system_get_vbat( const void* context, lr20xx_system_value_format_t format, lr20xx_system_meas_res_t res, uint16_t* vbat ); -/*! - * @brief Get the value of the internal junction temperature - * - * If @p format is set to LR20XX_SYSTEM_VALUE_FORMAT_RAW, the temperature (in [°C]) is a function of Vana (typ. 1.35V), - * Vbe25 (Vbe voltage @ 25°C, typ. 0.7295V) and VbeSlope (typ. -1.7mV/°C) using the following formula: - * \f$ Temperature_{°C} = (\frac{temp(12:0)}{8192} \times Vana - Vbe25) \times \frac{1000}{VbeSlope} + 25 \f$ where - * temp{12:0} is the value corresponding to the 12 LSBs of the output argument of @ref lr20xx_system_get_temp - * - * If @p format is set to LR20XX_SYSTEM_VALUE_FORMAT_UNIT, the temperature is given in [°C] in 13.5sb format, the first - * byte returned contains the integer part, the second the fractional part. - * - * @remark If a TCXO is used, make sure to configure it with @ref lr20xx_system_set_tcxo_mode before calling this - * function - * - * @param [in] context Chip implementation context - * @param [in] format Format of the returned value of @p temp - * @param [in] res Resolution of the measure - * @param [in] src Temperature source - * @param [out] temp A pointer to the @p temp value - * - * @returns Operation status +/* + * Read internal junction temperature. RAW format: Temp_°C = (temp[12:0]/8192 × Vana - Vbe25) × 1000/VbeSlope + 25 + * (Vana typ. 1.35V, Vbe25 typ. 0.7295V, VbeSlope typ. -1.7mV/°C). UNIT format: °C in 13.5sb (integer + fractional). + * Configure TCXO with set_tcxo_mode before calling if TCXO is used. */ lr20xx_status_t lr20xx_system_get_temp( const void* context, lr20xx_system_value_format_t format, lr20xx_system_meas_res_t res, lr20xx_system_temp_src_t src, uint16_t* temp ); -/*! - * @brief Read and return a 32-bit random number - * - * This random number generator is not suitable for cryptographic operations. - * It can be called during any mode without perturbation on ongoing Rx or Tx operation. - * - * @remark Radio operating mode must be set into standby. - * - * @param [in] context Chip implementation context - * @param [in] source Select source of entropy for random number generator - * @param [out] random_number 32-bit random number - * - * @returns Operation status +/* + * Read a 32-bit random number. Not suitable for cryptographic use. Radio must be in standby mode. */ lr20xx_status_t lr20xx_system_get_random_number( const void* context, lr20xx_system_random_entropy_source_bitmask_t source, uint32_t* random_number ); -/*! - * @brief Switch the transceiver into sleep mode with the request configuration - * - * @param [in] context Chip implementation context - * @param [in] sleep_cfg Sleep configuration - * @param [in] sleep_time Sleep time in LF clock steps - * - * @returns Operation status - */ +/* Enter sleep mode; sleep_time in LF clock steps (0 = sleep until wakeup pin) */ lr20xx_status_t lr20xx_system_set_sleep_mode( const void* context, const lr20xx_system_sleep_cfg_t* sleep_cfg, const uint32_t sleep_time ); -/*! - * @brief Switch the transceiver into the requested stand-by mode - * - * @param [in] context Chip implementation context - * @param [in] standby_mode Requested stand-by mode - * - * @returns Operation status - */ lr20xx_status_t lr20xx_system_set_standby_mode( const void* context, const lr20xx_system_standby_mode_t standby_mode ); - -/*! - * @brief Switch the transceiver into the Frequency Synthesis (FS) mode - * - * @param [in] context Chip implementation context - * - * @returns Operation status - */ lr20xx_status_t lr20xx_system_set_fs_mode( const void* context ); -/*! - * @brief Add a register to be saved in retention memory - * - * @remark This command is used when a register is not added by default to the retention memory. It gives the - * possibility to store up to 32 additional registers when entering sleep mode. - * - * @param [in] context Chip implementation context - * @param [in] slot Index in the storage list. Allowed values [0:31] - * @param [in] address Address of the register to be added to the list. Only the 3 LSBs are significant. Address must be - * word-aligned - * - * @returns Operation status - * - * @see lr20xx_system_set_sleep_mode +/* + * Add a register to the sleep retention list. slot in [0:31]; address must be word-aligned (only 3 LSBs significant). + * Up to 32 additional registers beyond the hardware defaults can be retained across retentionless sleep. */ lr20xx_status_t lr20xx_system_add_register_to_retention_mem( const void* context, uint8_t slot, uint32_t address ); -/*! - * @brief Configure the low battery detector - * - * @param [in] context Chip implementation context - * @param [in] is_enabled Low battery detector activation - * @param [in] trim Trimming value defining the threshold used to trigger a low battery interrupt - * - * @returns Operation status - */ lr20xx_status_t lr20xx_system_set_lbd_cfg( const void* context, bool is_enabled, lr20xx_system_lbd_trim_t trim ); -/*! - * @brief Configure the internal trimming capacitor values and XTAL start time - * - * @remark The device is fitted with internal programmable capacitors connected independently to the pins XTA and XTB of - * the device. Each capacitor can be controlled independently in steps of 0.47 pF added to the minimal value of 11.3pF - * for XTA and 10.1pF for XTB. - * - * The maximal capacitor value corresponds to 47 LSB steps added to the corresponding minimal value, so it is 33.39pF - * for XTA and 32.19pF for XTB. - * - * @param [in] context Chip implementation context - * @param [in] xta Value for the trimming capacitor connected to XTA pin - * @param [in] xtb Value for the trimming capacitor connected to XTB pin - * @param [in] wait_time_us Additional wait time after XTAL readiness in microsecond - * - * @returns Operation status +/* + * Configure internal XTAL trim capacitors and post-ready wait time. XTA: 11.3pF + xta×0.47pF (max 47 steps = 33.39pF). + * XTB: 10.1pF + xtb×0.47pF (max 47 steps = 32.19pF). wait_time_us is an additional delay after XTAL readiness. */ lr20xx_status_t lr20xx_system_configure_xosc( const void* context, uint8_t xta, uint8_t xtb, uint8_t wait_time_us ); -/*! - * @brief Set the temperature compensation configuration - * - * This command configures the heating compensation during Tx operations when XTAL 32MHz is used. - * This command will fail if a TCXO is configured. - * - * @param [in] context Chip implementation context - * @param [in] mode Temperature compensation mode - * @param [in] is_ntc_en Indicate if an external temperature sensor is available - * - * @returns Operation status +/* + * Configure TX heating compensation for XTAL 32MHz (not TCXO). Fails if TCXO is configured. + * Set is_ntc_en if an external NTC temperature sensor is present. */ lr20xx_status_t lr20xx_system_set_temp_comp_cfg( const void* context, lr20xx_system_temp_comp_mode_t mode, bool is_ntc_en ); -/*! - * @brief Set Negative Temperature Coefficient parameters - * - * @param [in] context Chip implementation context - * @param [in] ntc_r_ratio Resistance bias ratio (10.9b) - ratio between resistance bias and NTC resistance at 25°C - * @param [in] ntc_beta Beta coefficient (unit is 2 Kelvin) - * @param [in] delay First order time delay coefficient - * - * @returns Operation status - */ +/* Set NTC parameters: ntc_r_ratio is 10.9b resistance bias ratio at 25°C; ntc_beta in units of 2K */ lr20xx_status_t lr20xx_system_set_ntc_params( const void* context, uint16_t ntc_r_ratio, uint16_t ntc_beta, uint8_t delay ); diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_system_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_system_types.h index b0c7d3c..4ff1622 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_system_types.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_system_types.h @@ -127,7 +127,7 @@ typedef struct lr20xx_system_version_s } lr20xx_system_version_t; /** - * @brief Version structure definition + * @brief Sleep mode configuration */ typedef struct lr20xx_system_sleep_cfg_s { @@ -394,12 +394,7 @@ typedef enum lr20xx_system_temp_src_e LR20XX_SYSTEM_TEMP_SRC_NTC = 0x02, } lr20xx_system_temp_src_t; -/** - * @brief Select the entropy source to enable for random number generator - * - * It is advised to enable both PLL and ADC entropy sources. - * By default PLL and ADC are used as entropy sources. - */ +/** @brief Entropy source selection for random number generator (default: PLL | ADC) */ typedef enum { LR20XX_SYSTEM_RANDOM_ENTROPY_SOURCE_PLL = 0x01, //!< PLL is used as entropy source. The chip automatically goes to @@ -408,11 +403,7 @@ typedef enum //!< FS mode when needed, and goes back to original mode afterward. } lr20xx_system_random_entropy_source_t; -/** - * @brief Bit mask of entropy source to enable. - * - * The values are from @ref lr20xx_system_random_entropy_source_t. - */ +/** @brief Bitmask of lr20xx_system_random_entropy_source_t values */ typedef uint8_t lr20xx_system_random_entropy_source_bitmask_t; /** diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.c b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.c index 2f3e38c..44f973b 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.c +++ b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.c @@ -51,46 +51,47 @@ * --- PRIVATE MACROS----------------------------------------------------------- */ -#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_SYNCWORDS ( 7 ) -#define LR20XX_WORKAROUND_BLUETOOTH_LE_2MBPS_PREAMBLE_LENGTH_BUFFER_LENGTH ( 7 ) +/* Semtech SWDR001 workaround register addresses and field masks */ +#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_SYNCWORDS ( 7 ) /* Semtech SWDR001 prescribed value */ +#define LR20XX_WORKAROUND_BLUETOOTH_LE_2MBPS_PREAMBLE_LENGTH_BUFFER_LENGTH ( 7 ) /* Semtech SWDR001 prescribed value */ -#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_REGISTER_ADDRESS ( 0x00F30C28 ) +#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_REGISTER_ADDRESS ( 0x00F30C28 ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_REGISTER_MASK ( 0x1F << 5 ) -#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_VALUE ( 30 << 5 ) +#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_VALUE ( 30 << 5 ) /* Semtech SWDR001 prescribed value; field occupies bits[9:5] */ -#define LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_ADDRESS ( 0x00F30A14 ) +#define LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_ADDRESS ( 0x00F30A14 ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_MASK ( 3 << 18 ) -#define LR20XX_WORKAROUND_LORA_FREQ_HOP_SX1276_COMPATIBILITY_REGISTER_ADDRESS ( 0x00F30A24 ) +#define LR20XX_WORKAROUND_LORA_FREQ_HOP_SX1276_COMPATIBILITY_REGISTER_ADDRESS ( 0x00F30A24 ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_LORA_FREQ_HOP_SX1276_COMPATIBILITY_REGISTER_MASK ( 1 << 18 ) -#define LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_ADDRESS ( 0x00F30E14 ) +#define LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_ADDRESS ( 0x00F30E14 ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_MASK ( 0x7F << 20 ) -#define LR20XX_WORKAROUND_RTTOF_RF_FREQ_ADDRESS ( 0x00F40144 ) -#define LR20XX_WORKAROUND_RTTOF_RF_FREQ_MASK ( 0x7F ) +#define LR20XX_WORKAROUND_RTTOF_RF_FREQ_ADDRESS ( 0x00F40144 ) /* Semtech SWDR001 */ +#define LR20XX_WORKAROUND_RTTOF_RF_FREQ_MASK ( 0x7F ) /* 7-bit PLL step fractional part */ -#define LR20XX_WORKAROUND_RTTOF_RSSI_MAX_GAIN_REGISTER_ADDRESS ( 0x00F301A4 ) -#define LR20XX_WORKAROUND_RTTOF_RSSI_POWER_OFFSET_REGISTER_ADDRESS ( 0x00F30128 ) +#define LR20XX_WORKAROUND_RTTOF_RSSI_MAX_GAIN_REGISTER_ADDRESS ( 0x00F301A4 ) /* Semtech SWDR001 */ +#define LR20XX_WORKAROUND_RTTOF_RSSI_POWER_OFFSET_REGISTER_ADDRESS ( 0x00F30128 ) /* Semtech SWDR001 */ -#define LR20XX_WORKAROUND_DCDC_ADC_CTRL_REGISTER_ADDRESS ( 0x00F40200 ) -#define LR20XX_WORKAROUND_DCDC_RX_PATH_REGISTER_ADDRESS ( 0x00F40430 ) -#define LR20XX_WORKAROUND_DCDC_SWITCHER_REGISTER_ADDRESS ( 0x00F20024 ) +#define LR20XX_WORKAROUND_DCDC_ADC_CTRL_REGISTER_ADDRESS ( 0x00F40200 ) /* Semtech SWDR001 */ +#define LR20XX_WORKAROUND_DCDC_RX_PATH_REGISTER_ADDRESS ( 0x00F40430 ) /* Semtech SWDR001 */ +#define LR20XX_WORKAROUND_DCDC_SWITCHER_REGISTER_ADDRESS ( 0x00F20024 ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_DCDC_SWITCHER_RISE_REGISTER_MASK ( 0xF << 20 ) #define LR20XX_WORKAROUND_DCDC_SWITCHER_FALL_REGISTER_MASK ( 0xF << 16 ) -#define LR20XX_WORKAROUND_DCDC_FREQ_LF_REGISTER_ADDRESS ( 0x80004C ) +#define LR20XX_WORKAROUND_DCDC_FREQ_LF_REGISTER_ADDRESS ( 0x80004C ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_DCDC_RF_FREQ_ADDRESS ( LR20XX_WORKAROUND_RTTOF_RF_FREQ_ADDRESS ) -#define LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_ADDRESS ( 0xF3013C ) +#define LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_ADDRESS ( 0xF3013C ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_MASK ( 0x38 ) -#define LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_VALUE ( 0x30 ) +#define LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_VALUE ( 0x30 ) /* Semtech SWDR001 prescribed value; within mask 0x38 */ -#define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_ADDRESS ( 0xF30134 ) +#define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_ADDRESS ( 0xF30134 ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_MASK ( 0x1B ) -#define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_MANAGER_VALUE ( 0x08 ) -#define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_SUBORDINATE_VALUE ( 0x0A ) +#define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_MANAGER_VALUE ( 0x08 ) /* Semtech SWDR001 prescribed value; within mask 0x1B */ +#define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_SUBORDINATE_VALUE ( 0x0A ) /* Semtech SWDR001 prescribed value; within mask 0x1B */ -#define LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_ADDRESS ( 0x00F30B50 ) +#define LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_ADDRESS ( 0x00F30B50 ) /* Semtech SWDR001 */ #define LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_MASK ( 0x7 << 24 ) #define LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_SET_VALUE ( 0x0 << 24 ) #define LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_RESET_VALUE ( 0x1 << 24 ) @@ -115,88 +116,32 @@ * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- */ -/** - * @brief Helper function to write the appropriate field to store LoRa SX1276 compatibility parameter - * - * @param context Chip implementation context - * @param value True to enable the compatibility mode, false to disable it - * @return Operation status - */ +/* Write SX1276 LoRa compatibility field; value=true enables */ static lr20xx_status_t lr20xx_workaround_lora_sx1276_compatibility_write_value( const void* context, bool value ); -/** - * @brief Helper function to write the appropriate field to store LoRa frequency hopping SX1276 compatibility parameter - * - * @param context Chip implementation context - * @param value True to enable the compatibility mode, false to disable it - * @return Operation status - */ +/* Write SX1276 frequency-hopping compatibility field; value=true enables */ static lr20xx_status_t lr20xx_workaround_lora_frequency_hopping_sx1276_compatibility_write_value( const void* context, bool value ); -/** - * @brief Read the configured SF value configured - * - * This command is to be used only when disabling the SX1276 LoRa compatibility mode. - * - * @param context Chip implementation context - * @param [out] sf The configure SF - * - * @return Operation status - */ +/* Read SF bits from SX1276 compatibility register; used only when disabling the mode */ static lr20xx_status_t lr20xx_workaround_lora_sx1276_compatibility_read_sf_value( const void* context, uint8_t* sf ); -/** - * @brief Read RTToF max gain and power offset from the LR20xx register - * - * @param context Chip implementation context - * @param [out] max_gain Max gain read from register - * @param [out] power_offset Power offset read from register - * @return Operation status - * - * @see lr20xx_workarounds_rttof_rssi_computation_apply_correction, lr20xx_workarounds_rttof_rssi_computation - */ +/* Read RTToF max gain (10-bit) and 6-bit signed power offset from hardware registers */ static lr20xx_status_t lr20xx_workarounds_rttof_rssi_computation_get_gain_power( const void* context, uint16_t* max_gain, int16_t* power_offset ); -/** - * @brief Compute RTToF RSSI correction from max gain, power offset, and raw RSSI value - * - * @param max_gain The max gain obtained from reading the LR20xx register - * @param power_offset The power offset obtained from reading the LR20xx register - * @param raw_rssi The raw RSSI value typically obtained from SPI response to @ref lr20xx_rttof_get_results, before - * converting value to dB - * - * @return uint8_t The corrected RSSI value - * - * @see lr20xx_workarounds_rttof_rssi_computation_get_gain_power, lr20xx_workarounds_rttof_rssi_computation - */ +/* Apply RTToF RSSI correction formula (Semtech SWDR001) to a raw RSSI byte */ static uint8_t lr20xx_workarounds_rttof_rssi_computation_apply_correction( uint16_t max_gain, int16_t power_offset, uint8_t raw_rssi ); -/** - * @brief Set the DCDC regulator frequency - * - * @param context Chip implementation context - * @param frequency [in] The frequency to set, expressed in Hz - * - * @return Operation status - */ +/* Write DCDC LF switching frequency register (frequency in Hz) */ static lr20xx_status_t lr20xx_workaround_dcdc_set_frequency( const void* context, uint32_t frequency ); -/** - * @brief Get the RF frequency configured - * - * This function must be used only in the context of DCDC workaround. - * - * @param context Chip implementation context - * @param [out] frequency The RF frequency, in Hz - * - * @return Operation status - */ +/* Read current RF frequency from PLL register; used only in DCDC workaround context */ static lr20xx_status_t lr20xx_workaround_dcdc_get_rf_frequency( const void* context, uint32_t* frequency ); +/* Convert raw PLL step count to Hz: step_hz = 15625/2^14 ≈ 0.9537Hz */ static uint32_t pll_step_to_hz( uint32_t pll_steps ); /* @@ -245,11 +190,10 @@ lr20xx_status_t lr20xx_workarounds_lora_enable_sx1276_compatibility_mode( const lr20xx_status_t lr20xx_workarounds_lora_disable_sx1276_compatibility_mode( const void* context ) { - // 1. Get the currently configured SF value uint8_t sf = 0; const lr20xx_status_t get_sf_status = lr20xx_workaround_lora_sx1276_compatibility_read_sf_value( context, &sf ); - // 2. Modify the compatibility mode value depending on currently configured SF + /* SF6 requires compatibility mode even when "disabling" (SX1276 SF6 implicit-only constraint) */ if( get_sf_status == LR20XX_STATUS_OK ) { return lr20xx_workaround_lora_sx1276_compatibility_write_value( context, ( ( sf <= 6 ) ? true : false ) ); @@ -286,6 +230,7 @@ lr20xx_status_t lr20xx_workarounds_lora_freq_hop_sx1276_compatibility_mode_store lr20xx_status_t lr20xx_workarounds_ook_set_detection_threshold_level( const void* context, int16_t threshold_level_db ) { + /* Register field is biased: +10 dB hardware offset + 64 to map signed dBm to unsigned field (Semtech SWDR001) */ const int threshold_db = threshold_level_db + 10 + 64; return lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_ADDRESS, @@ -503,19 +448,19 @@ lr20xx_status_t lr20xx_workarounds_rttof_rssi_computation( const void* context, lr20xx_workarounds_rttof_rssi_computation_apply_correction( max_gain, power_offset, rssi2_raw_value ); } - // OK is returned here, as an error would have returned on previous RETURN_STATUS_ON_NOT_OK return LR20XX_STATUS_OK; } lr20xx_status_t lr20xx_workarounds_dcdc_reset( const void* context ) { + /* Rise/fall timing fields (bits 23:20 and 19:16): default values 15,15 per Semtech SWDR001 */ RETURN_STATUS_ON_NOT_OK( lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_DCDC_SWITCHER_REGISTER_ADDRESS, LR20XX_WORKAROUND_DCDC_SWITCHER_RISE_REGISTER_MASK, 15 << 20 ) ); RETURN_STATUS_ON_NOT_OK( lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_DCDC_SWITCHER_REGISTER_ADDRESS, LR20XX_WORKAROUND_DCDC_SWITCHER_FALL_REGISTER_MASK, 15 << 16 ) ); - return lr20xx_workaround_dcdc_set_frequency( context, 2800000 ); + return lr20xx_workaround_dcdc_set_frequency( context, 2800000 ); /* 2.8MHz default switching frequency */ } lr20xx_status_t lr20xx_workarounds_dcdc_configure( const void* context ) @@ -530,6 +475,7 @@ lr20xx_status_t lr20xx_workarounds_dcdc_configure( const void* context ) lr20xx_regmem_read_regmem32( context, LR20XX_WORKAROUND_DCDC_RX_PATH_REGISTER_ADDRESS, &rx_path_raw, 1 ) ); const bool is_rx_hf = ( ( rx_path_raw & 0x3 ) == 1 ); + /* Rise=11,Fall=13 for narrow-band LF RX path; Rise=15,Fall=15 otherwise — Semtech SWDR001 */ if( ( is_rx_hf == false ) && ( ( ana_dec == 1 ) || ( ana_dec == 2 ) ) ) { RETURN_STATUS_ON_NOT_OK( @@ -549,6 +495,7 @@ lr20xx_status_t lr20xx_workarounds_dcdc_configure( const void* context ) LR20XX_WORKAROUND_DCDC_SWITCHER_FALL_REGISTER_MASK, 15 << 16 ) ); } + /* ana_dec==1: 4.3MHz switching; otherwise: 2.8MHz — Semtech SWDR001 */ if( ana_dec == 1 ) { return lr20xx_workaround_dcdc_set_frequency( context, 4300000 ); @@ -633,7 +580,7 @@ lr20xx_status_t lr20xx_workaround_lora_sx1276_compatibility_read_sf_value( const context, LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_ADDRESS, &raw_register_value, 1 ); if( read_status == LR20XX_STATUS_OK ) { - *sf = raw_register_value & 0x0f; + *sf = raw_register_value & 0x0f; /* SF is stored in bits [3:0] */ } return read_status; } @@ -644,11 +591,12 @@ lr20xx_status_t lr20xx_workarounds_rttof_rssi_computation_get_gain_power( const uint32_t max_gain_raw = 0; RETURN_STATUS_ON_NOT_OK( lr20xx_regmem_read_regmem32( context, LR20XX_WORKAROUND_RTTOF_RSSI_MAX_GAIN_REGISTER_ADDRESS, &max_gain_raw, 1 ) ); - ( *max_gain ) = ( uint16_t ) ( max_gain_raw & 0x03FF ); + ( *max_gain ) = ( uint16_t ) ( max_gain_raw & 0x03FF ); /* 10-bit gain field */ uint32_t power_offset_raw = 0; RETURN_STATUS_ON_NOT_OK( lr20xx_regmem_read_regmem32( context, LR20XX_WORKAROUND_RTTOF_RSSI_POWER_OFFSET_REGISTER_ADDRESS, &power_offset_raw, 1 ) ); + /* 6-bit two's-complement field at bit[11:6]: sign-extend by subtracting 64 if > 31 */ const int16_t power_offset_raw_value = ( power_offset_raw >> 6 ) & 0x3F; ( *power_offset ) = ( int16_t ) ( ( ( power_offset_raw_value ) > 32 ) ? ( power_offset_raw_value - ( int16_t ) 64 ) : power_offset_raw_value ); @@ -658,11 +606,13 @@ lr20xx_status_t lr20xx_workarounds_rttof_rssi_computation_get_gain_power( const uint8_t lr20xx_workarounds_rttof_rssi_computation_apply_correction( uint16_t max_gain, int16_t power_offset, uint8_t raw_rssi ) { + /* 208 = RSSI register bias per Semtech SWDR001 RSSI correction formula */ return ( uint8_t ) ( 208 + ( max_gain >> 1 ) + power_offset - ( raw_rssi << 1 ) ); } lr20xx_status_t lr20xx_workaround_dcdc_set_frequency( const void* context, uint32_t frequency ) { + /* 1.048576 = 2^20 / 1e6: converts Hz to LF register units (Semtech SWDR001) */ const uint32_t freq_lf = ( uint32_t ) ( ( float ) frequency * 1.048576f ); RETURN_STATUS_ON_NOT_OK( lr20xx_regmem_write_regmem32( context, LR20XX_WORKAROUND_DCDC_FREQ_LF_REGISTER_ADDRESS, &freq_lf, 1 ) ); @@ -683,7 +633,7 @@ lr20xx_status_t lr20xx_workaround_dcdc_get_rf_frequency( const void* context, ui uint32_t pll_step_to_hz( uint32_t pll_steps ) { const uint_least64_t numerator = ( ( uint_least64_t ) pll_steps * ( uint_least64_t ) 15625ULL ); - const uint_least64_t denominator = ( ( uint_least64_t ) ( 1 << 14 ) ); // 1<<14 is 2**14 + const uint_least64_t denominator = ( ( uint_least64_t ) ( 1 << 14 ) ); /* PLL step = 15625/2^14 Hz */ return ( uint32_t ) ( ( numerator + denominator - 1 ) / denominator ); } diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.h b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.h index 37bb84f..9cbfb8c 100644 --- a/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.h +++ b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.h @@ -88,397 +88,108 @@ extern "C" { * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- */ -/** - * @brief Apply workaround for syncwords usage with BLE LE coded PHY - * - * This workaround is to be applied after configuring Bluetooth LE modulation and packet, if phy @ref - * LR20XX_RADIO_BLUETOOTH_LE_PHY_LE_CODED_125KB or @ref LR20XX_RADIO_BLUETOOTH_LE_PHY_LE_CODED_500KB are configured. - * - * @param [in] context Chip implementation context - * - * @returns Operation status - * - * @see lr20xx_radio_bluetooth_le_set_modulation_params, lr20xx_radio_bluetooth_le_set_pkt_params, - * lr20xx_workarounds_bluetooth_le_phy_coded_frequency_drift - */ +/* Call after lr20xx_radio_bluetooth_le_set_pkt_params when PHY is LE_CODED_125KB or LE_CODED_500KB */ lr20xx_status_t lr20xx_workarounds_bluetooth_le_phy_coded_syncwords( const void* context ); -/** - * @brief Apply workaround to support frequency drift with BLE LE coded PHY - * - * This workaround is to be applied after configuring Bluetooth LE modulation and packet, if phy @ref - * LR20XX_RADIO_BLUETOOTH_LE_PHY_LE_CODED_125KB or @ref LR20XX_RADIO_BLUETOOTH_LE_PHY_LE_CODED_500KB are configured. - * - * @param [in] context Chip implementation context - * - * @returns Operation status - * - * @see lr20xx_radio_bluetooth_le_set_modulation_params, lr20xx_radio_bluetooth_le_set_pkt_params, - * lr20xx_workarounds_bluetooth_le_phy_coded_syncwords - */ +/* Call after lr20xx_radio_bluetooth_le_set_pkt_params when PHY is LE_CODED_125KB or LE_CODED_500KB */ lr20xx_status_t lr20xx_workarounds_bluetooth_le_phy_coded_frequency_drift( const void* context ); -/** - * @brief Store the Bluetooth LE PHY coded frequency drift workaround in retention memory - * - * Calling this function allows to store the Bluetooth LE PHY coded frequency drift workaround state during sleep mode. - * This helper function internally calls @ref lr20xx_system_add_register_to_retention_mem with the appropriate register - * address. - * - * @param context Chip implementation context - * @param slot Index in the storage list. Allowed values [0:31] - * - * @return Operation status - * - * @see lr20xx_system_add_register_to_retention_mem, lr20xx_workarounds_bluetooth_le_phy_coded_frequency_drift - */ +/* Persist BLE coded-PHY frequency drift register across sleep; slot in [0:31] */ lr20xx_status_t lr20xx_workarounds_bluetooth_le_phy_coded_frequency_drift_store_retention_mem( const void* context, uint8_t slot ); -/** - * @brief Fix preamble length for Bluetooth LE 2Mbps - * - * The preamble length is by default incorrect for 2Mbps datarate. This workaround fixes the preamble length. - * It must be called right after @ref lr20xx_radio_bluetooth_le_set_modulation_params if the mode is @ref - * LR20XX_RADIO_BLUETOOTH_LE_PHY_LE_2M. - * - * Note that by default this workaround is automatically applied by @ref - * lr20xx_radio_bluetooth_le_set_modulation_params, unless the macro @ref - * LR20XX_WORKAROUND_DISABLE_AUTOMATIC_BLE_2MBPS_PREAMBLE_LENGTH is defined. - * - * @param context Chip implementation context - * @return Operation status +/* + * Fix incorrect default preamble length for BLE 2Mbps mode. + * Call after lr20xx_radio_bluetooth_le_set_modulation_params when PHY is LE_2M. + * Applied automatically unless LR20XX_WORKAROUND_DISABLE_AUTOMATIC_BLE_2MBPS_PREAMBLE_LENGTH is defined. */ lr20xx_status_t lr20xx_workarounds_bluetooth_le_2mbps_preamble_length( const void* context ); -/** - * @brief Enable LoRa compatibility mode with SX1276 - * - * If the SX1276 LoRa compatibility is required, this workaround must be called after calling @ref - * lr20xx_radio_lora_set_modulation_params. - * - * SX1276 LoRa compatibility mode allows: - * - transmission to, and reception from, SX1276 LoRa packets at SF6 only in implicit mode (@ref - * LR20XX_RADIO_LORA_PKT_IMPLICIT); and - * - syncword nibbles greater than 7 for all SF. - * - * @param context Chip implementation context - * - * @return Operation status - * - * @see lr20xx_workarounds_lora_disable_sx1276_compatibility_mode, - * lr20xx_workarounds_lora_sx1276_compatibility_mode_store_retention_mem +/* + * Enable SX1276 LoRa compatibility: SF6 implicit mode and syncword nibbles > 7 for all SF. + * Call after lr20xx_radio_lora_set_modulation_params. */ lr20xx_status_t lr20xx_workarounds_lora_enable_sx1276_compatibility_mode( const void* context ); -/** - * @brief Disable the LoRa compatibility mode with SX1276 - * - * To disable the SX1276 LoRa compatibility mode, this workaround can be call either before or after @ref - * lr20xx_radio_lora_set_modulation_params. - * - * @param context Chip implementation context - * - * @return Operation status - * - * @see lr20xx_workarounds_lora_enable_sx1276_compatibility_mode, - * lr20xx_workarounds_lora_sx1276_compatibility_mode_store_retention_mem - */ +/* Disable SX1276 LoRa compatibility; may be called before or after lr20xx_radio_lora_set_modulation_params */ lr20xx_status_t lr20xx_workarounds_lora_disable_sx1276_compatibility_mode( const void* context ); -/** - * @brief Store the LoRa SX1276 compatibility mode in retention memory - * - * Calling this function allows to store the SX1276 LoRa compatible state during sleep mode. - * This helper function internally calls @ref lr20xx_system_add_register_to_retention_mem with the appropriate register - * address. - * - * @param context Chip implementation context - * @param slot Index in the storage list. Allowed values [0:31] - * - * @return Operation status - * - * @see lr20xx_system_add_register_to_retention_mem, lr20xx_workarounds_lora_enable_sx1276_compatibility_mode, - * lr20xx_workarounds_lora_disable_sx1276_compatibility_mode - */ +/* Persist SX1276 LoRa compatibility register across sleep; slot in [0:31] */ lr20xx_status_t lr20xx_workarounds_lora_sx1276_compatibility_mode_store_retention_mem( const void* context, uint8_t slot ); -/** - * @brief Enable the SX1276 compatibility mode for LoRa intra-packet frequency hopping - * - * If the LoRa intra-packet frequency hopping compatible with SX1276 is required, this function must be called after - * @ref lr20xx_radio_lora_set_freq_hop. - * - * @param context Chip implementation context - * - * @return Operation status - * - * @see lr20xx_radio_lora_set_freq_hop, lr20xx_workarounds_lora_freq_hop_disable_sx1276_compatibility_mode, - * lr20xx_workarounds_lora_freq_hop_sx1276_compatibility_mode_store_retention_mem - */ +/* Enable SX1276 freq-hopping compatibility; call after lr20xx_radio_lora_set_freq_hop */ lr20xx_status_t lr20xx_workarounds_lora_freq_hop_enable_sx1276_compatibility_mode( const void* context ); -/** - * @brief Disable the SX1276 compatibility mode for LoRa intra-packet frequency hopping - * - * @param context Chip implementation context - * - * @return Operation status - * - * @see lr20xx_radio_lora_set_freq_hop, lr20xx_workarounds_lora_freq_hop_enable_sx1276_compatibility_mode, - * lr20xx_workarounds_lora_freq_hop_sx1276_compatibility_mode_store_retention_mem - */ +/* Disable SX1276 freq-hopping compatibility */ lr20xx_status_t lr20xx_workarounds_lora_freq_hop_disable_sx1276_compatibility_mode( const void* context ); -/** - * @brief Store the SX1276 compatibility mode for LoRa intra-packet frequency hopping in retention memory - * - * Calling this function allows to store the SX1276 LoRa intra-packet frequency hopping compatible state during sleep - * mode. This helper function internally calls @ref lr20xx_system_add_register_to_retention_mem with the appropriate - * register address. - * - * @param context Chip implementation context - * @param slot Index in the storage list. Allowed values [0:31] - * - * @return Operation status - * - * @see lr20xx_system_add_register_to_retention_mem, lr20xx_workarounds_lora_freq_hop_enable_sx1276_compatibility_mode, - * lr20xx_workarounds_lora_freq_hop_disable_sx1276_compatibility_mode - */ +/* Persist SX1276 freq-hopping compatibility register across sleep; slot in [0:31] */ lr20xx_status_t lr20xx_workarounds_lora_freq_hop_sx1276_compatibility_mode_store_retention_mem( const void* context, uint8_t slot ); -/** - * @brief Override the OOK detection threshold level - * - * The OOK detection threshold level is automatically computed by the LR20xx depending on the modulation parameters. - * However the computed value may be too conservative which increase the packet error rate. - * The detection threshold level can be therefore modified with this function. The threshold to provide is typically the - * noise level returned by @ref lr20xx_radio_common_get_rssi_inst using the same modulation parameters, if it is higher - * than the LR20xx default computed value. - * - * Refer to @ref lr20xx_workarounds_ook_get_default_detection_threshold_level to obtain the default computed values - * depending on modulation bandwidth. - * - * This function should be called after @ref lr20xx_radio_ook_set_modulation_params. - * - * @param context Chip implementation context - * @param threshold_level_db The threshold level to set, in dB - * - * @return Operation status - * - * @see lr20xx_radio_ook_set_modulation_params, lr20xx_radio_common_get_rssi_inst, - * lr20xx_workarounds_ook_get_default_detection_threshold_level +/* + * Override OOK detection threshold. The default chip-computed value may be too conservative, raising PER. + * Set to the noise floor (from lr20xx_radio_common_get_rssi_inst) if it exceeds the default. + * Call after lr20xx_radio_ook_set_modulation_params. threshold_level_db in dBm. */ lr20xx_status_t lr20xx_workarounds_ook_set_detection_threshold_level( const void* context, int16_t threshold_level_db ); -/** - * @brief Helper function that returns default OOK detection threshold level - * - * This helper function helps to determine if the workaround @ref lr20xx_workarounds_ook_set_detection_threshold_level - * is to be applied. - * - * @param bw The bandwidth for which the detection threshold is to be computed - * - * @return The default OOK detection threshold level, or 0 if the bandwidth @p bw is unknown - * - * @see lr20xx_workarounds_ook_set_detection_threshold_level - * +/* + * Return default OOK detection threshold (dBm) for the given bandwidth. + * Returns 0 for unknown bandwidth values. */ int16_t lr20xx_workarounds_ook_get_default_detection_threshold_level( lr20xx_radio_fsk_common_bw_t bw ); -/** - * @brief Apply workaround to truncate internal PLL frequency step for RTToF operation - * - * Unexpected RTToF results may be obtained if the RF frequency is not set to a value multiple of 122Hz. - * This workaround ensures internal RF frequency is configured to a multiple of 122Hz. - * - * This workaround must be applied after configuring the RF frequency of RTToF ranging operations with @ref - * lr20xx_radio_common_set_rf_freq. - * After applying the workaround, the RF frequency is therefore modified by a quantity inferior or equal to 122Hz - * compared to the value set by last call to @ref lr20xx_radio_common_set_rf_freq. - * - * @param context Chip implementation context - * - * @return Operation status - * - * @see lr20xx_radio_common_set_rf_freq +/* + * Truncate internal PLL frequency to the nearest 122Hz multiple for RTToF accuracy. + * Call after lr20xx_radio_common_set_rf_freq for RTToF ranging; adjusts RF freq by ≤122Hz. */ lr20xx_status_t lr20xx_workarounds_rttof_truncate_pll_freq_step( const void* context ); -/** - * @brief Fix RTToF RSSI raw values - * - * This workaround fixes raw values of RTToF RSSIs by gathering information from the chip. - * This function can be used either for: - * - normal result (with only one RSSI value), setting @p rssi2_raw_fixed to null pointer; and - * - extended result (with two RSSI values). - * - * This workaround handles the raw RSSI values, which are available only in the internal of the driver, and not exposed - * by the API. It is however possible to retrieve approximation of raw RSSI value from exposed one in dB (and the other - * way around) thanks to the following pseudo-code: - * @code{.c} - * // Convert from RSSI in dB to raw value - * uint8_t raw_rssi_value = -(rssi_db * 2); - * - * // Convert from raw RSSI value to dB value - * uint8_t rssi_db = -(raw_rssi_value / 2); - * @endcode - * - * @param context Chip implementation context - * @param [in] rssi1_raw_value Raw value of RSSI1 - * @param [in] rssi2_raw_value Raw value of RSSI2 - * @param [out] rssi1_raw_fixed Pointer to store fixed raw value for RSSI1 - * @param [out] rssi2_raw_fixed Pointer to store fixed raw value for RSSI2, can be null. - * @return lr20xx_status_t +/* + * Correct RTToF raw RSSI values using gain/offset read from hardware registers. + * raw = -(rssi_dB * 2); rssi_dB = -(raw / 2). + * rssi2_raw_fixed may be null for single-RSSI (normal) results. */ lr20xx_status_t lr20xx_workarounds_rttof_rssi_computation( const void* context, uint8_t rssi1_raw_value, uint8_t rssi2_raw_value, uint8_t* rssi1_raw_fixed, uint8_t* rssi2_raw_fixed ); -/** - * @brief Reset DCDC regulator internal value to appropriate configuration - * - * This workaround must be called after each @ref lr20xx_radio_common_set_pkt_type if all the following is true: - * - Rx operations are intended - * - @ref LR20XX_SYSTEM_REG_MODE_DCDC is used - * - sub GHz operations are intended - * - * @param context Chip implementation context - * @return Operation status - * - * @see lr20xx_radio_common_set_pkt_type, lr20xx_workarounds_dcdc_configure, - * lr20xx_workarounds_lora_sx1276_compatibility_mode_store_retention_mem +/* + * Reset DCDC switcher to default timing. + * Required after lr20xx_radio_common_set_pkt_type when: sub-GHz RX + DCDC regulator mode. */ lr20xx_status_t lr20xx_workarounds_dcdc_reset( const void* context ); -/** - * @brief Configure DCDC regulator internal value for specific operations - * - * This workaround must be called if all the following is true: - * - Rx operations are intended - * - @ref LR20XX_SYSTEM_REG_MODE_DCDC is used - * - sub GHz operations are intended - * - * This workaround must be called after each of the following commands: - * - @ref lr20xx_radio_fsk_set_modulation_params - * - @ref lr20xx_radio_flrc_set_modulation_params - * - @ref lr20xx_radio_ook_set_modulation_params - * - @ref lr20xx_radio_lora_set_modulation_params - * - @ref lr20xx_radio_z_wave_set_params - * - @ref lr20xx_radio_common_set_rx_path - * - * @param context Chip implementation context - * @return Operation status - * - * @see lr20xx_workarounds_dcdc_reset, lr20xx_radio_fsk_set_modulation_params, lr20xx_radio_flrc_set_modulation_params, - * lr20xx_radio_ook_set_modulation_params, lr20xx_radio_lora_set_modulation_params, lr20xx_radio_z_wave_set_params, - * lr20xx_radio_common_set_rx_path, lr20xx_workarounds_lora_sx1276_compatibility_mode_store_retention_mem +/* + * Configure DCDC switcher timing based on current RX path. + * Required after any of: fsk/flrc/ook/lora set_modulation_params, z_wave_set_params, set_rx_path; + * when: sub-GHz RX + DCDC regulator mode. */ lr20xx_status_t lr20xx_workarounds_dcdc_configure( const void* context ); -/** - * @brief Store the LoRa DCDC configuration in retention memory - * - * Calling this function allows to store the DCDC new reset value or configuration during sleep mode. - * This helper function internally calls @ref lr20xx_system_add_register_to_retention_mem with the appropriate register - * address. - * - * @param context Chip implementation context - * @param slot Index in the storage list. Allowed values [0:31] - * - * @return Operation status - * - * @see lr20xx_system_add_register_to_retention_mem, lr20xx_workarounds_dcdc_reset, lr20xx_workarounds_dcdc_configure - */ +/* Persist DCDC switcher register across sleep; slot in [0:31] */ lr20xx_status_t lr20xx_workarounds_dcdc_store_retention_mem( const void* context, uint8_t slot ); -/** - * @brief Apply workaround to reduce standard deviation of RTToF results with fractional bandwidths - * - * This workaround reduces the standard deviation of observed RTToF result on the following bandwiths: - * - @ref LR20XX_RADIO_LORA_BW_812 - * - @ref LR20XX_RADIO_LORA_BW_406 - * - @ref LR20XX_RADIO_LORA_BW_203 - * - @ref LR20XX_RADIO_LORA_BW_101 - * The workaround must be called only on these bandwidths, after calling @ref lr20xx_radio_lora_set_modulation_params. - * - * Note that a call to @ref lr20xx_radio_lora_set_modulation_params reset the changes executed by this workaround. - * - * @param context Chip implementation context - * @param is_manager True if the device operate as manager, false if it operates as subordinate - * - * @return Operation status - * - * @see lr20xx_radio_lora_set_modulation_params +/* + * Reduce RTToF result deviation on fractional bandwidths (BW_812/406/203/101). + * Call after lr20xx_radio_lora_set_modulation_params; reset by subsequent set_modulation_params. + * is_manager: true for RTToF manager role, false for subordinate. */ lr20xx_status_t lr20xx_workarounds_rttof_results_deviation( const void* context, bool is_manager ); -/** - * @brief Store the registers for RTToF results deviation workaround in retention memory - * - * Calling this function allows to store the RTToF results deviation workaround registers during sleep mode. This helper - * function internally calls @ref lr20xx_system_add_register_to_retention_mem with the appropriate register address. - * - * The @ref lr20xx_workarounds_rttof_results_deviation workaround addresses two registers, hence the two configurable - * slots. - * - * @param context Chip implementation context - * @param slot_1 Index in the storage list. Allowed values [0:31] - * @param slot_2 Index in the storage list. Allowed values [0:31] - * - * @return Operation status - * - * @see lr20xx_system_add_register_to_retention_mem, lr20xx_workarounds_rttof_results_deviation - */ +/* Persist RTToF deviation workaround registers (two slots) across sleep; slots in [0:31] */ lr20xx_status_t lr20xx_workarounds_rttof_results_deviation_store_retention_mem( const void* context, uint8_t slot_1, uint8_t slot_2 ); -/** - * @brief Enable the workaround for RTToF Extention mode - * - * This workaround must be called when attempting RTToF operations with @ref - * lr20xx_rttof_mode_e:LR20XX_RTTOF_MODE_EXTENDED - * - * @param context Chip implementation context - * - * @return Operation status - * - * @see lr20xx_rttof_set_params, lr20xx_workarounds_rttof_extended_stuck_second_request_disable, - * lr20xx_workarounds_rttof_extended_stuck_second_request_store_retention_mem - */ +/* Enable RTToF extended-mode workaround; required when using LR20XX_RTTOF_MODE_EXTENDED */ lr20xx_status_t lr20xx_workarounds_rttof_extended_stuck_second_request_enable( const void* context ); -/** - * @brief Disable the RTToF workaround for Extention mode - * - * If @ref lr20xx_workarounds_rttof_extended_stuck_second_request_enable has previously been called and @ref - * lr20xx_rttof_mode_e:LR20XX_RTTOF_MODE_NORMAL are attempted, the workaround must be disabled calling this function. - * - * @param context Chip implementation context - * - * @return Operation status - * - * @see lr20xx_rttof_set_params, lr20xx_workarounds_rttof_extended_stuck_second_request_enable, - * lr20xx_workarounds_rttof_extended_stuck_second_request_store_retention_mem - */ +/* Disable RTToF extended-mode workaround when switching back to LR20XX_RTTOF_MODE_NORMAL */ lr20xx_status_t lr20xx_workarounds_rttof_extended_stuck_second_request_disable( const void* context ); -/** - * @brief Store the registers for RTToF extended stuck workaround in retention memory - * - * Calling this function allows to store the workaround register during sleep mode. This helper function internally - * calls @ref lr20xx_system_add_register_to_retention_mem with the appropriate register address. - * - * @param context Chip implementation context - * @param slot Index in the storage list. Allowed values [0:31] - * - * @return Operation status - * - * @see lr20xx_system_add_register_to_retention_mem, lr20xx_workarounds_rttof_extended_stuck_second_request_enable, - * lr20xx_workarounds_rttof_extended_stuck_second_request_disable - */ +/* Persist RTToF extended-mode workaround register across sleep; slot in [0:31] */ lr20xx_status_t lr20xx_workarounds_rttof_extended_stuck_second_request_store_retention_mem( const void* context, uint8_t slot ); diff --git a/zephcore/adapters/radio/radio_common.h b/zephcore/adapters/radio/radio_common.h index c4fd743..5c0a32d 100644 --- a/zephcore/adapters/radio/radio_common.h +++ b/zephcore/adapters/radio/radio_common.h @@ -12,40 +12,34 @@ #include /* --- Noise floor calibration (EMA) --- - * Median of SAMPLES_PER_TICK RSSI reads (~200 us), fed into an - * exponential moving average. Median rejects up to N/2-1 outliers - * without the downward bias of min or spike sensitivity of average. - * alpha = 1/8: new_floor = floor + round((sample - floor) / 8) - * Convergence: ~8 ticks (~40s at 5s housekeeping) to track a step change. - * Samples above floor + SAMPLING_THRESHOLD are rejected (interference). */ -#define NOISE_FLOOR_EMA_SHIFT 3 /* alpha = 1 / (1 << 3) = 1/8 */ -#define NOISE_FLOOR_SAMPLES_PER_TICK 8 /* median of 8 RSSI reads per tick */ -#define NOISE_FLOOR_UNGUARDED_INTERVAL 16 /* ticks between unfiltered samples (must be power of 2) */ -#define NOISE_FLOOR_SAMPLING_THRESHOLD 14 /* only sample if rssi < floor + threshold */ -#define DEFAULT_NOISE_FLOOR 0 /* accept all samples until first update */ + * Median-of-N RSSI reads → EMA. alpha = 1/8, converges in ~8 ticks (~40s). + * Samples above floor + SAMPLING_THRESHOLD rejected as interference. */ +#define NOISE_FLOOR_EMA_SHIFT 3 /* alpha = 1/(1<<3) = 1/8 */ +#define NOISE_FLOOR_SAMPLES_PER_TICK 8 /* median of 8 reads per tick */ +#define NOISE_FLOOR_UNGUARDED_INTERVAL 16 /* ticks between unfiltered samples (power of 2) */ +#define NOISE_FLOOR_SAMPLING_THRESHOLD 14 /* dB above floor to reject as interference */ +#define DEFAULT_NOISE_FLOOR 0 /* sentinel: seed from first sample */ -/* --- RX ring buffer --- - * 8 slots buffer ~40ms+ of back-to-back arrivals at SF7/BW500. - * Main loop drains in microseconds per packet. Cost: 8 × 260 = ~2 KB. */ -#define RX_RING_SIZE 8 +/* --- RX ring buffer --- */ +#define RX_RING_SIZE 8 /* ~2 KB; buffers burst arrivals at SF7/BW500 */ /* --- TX wait thread --- */ #define TX_WAIT_THREAD_STACK_SIZE 1024 -#define TX_WAIT_THREAD_PRIORITY 10 /* preemptible, lower than main */ -#define TX_TIMEOUT_MS 5000 /* TX completion timeout */ +#define TX_WAIT_THREAD_PRIORITY 10 /* preemptible, below main thread */ +#define TX_TIMEOUT_MS 5000 /* hard timeout for TX completion signal */ /* --- SNR thresholds per spreading factor (SF7..SF12) --- */ inline constexpr float lora_snr_threshold[] = { -7.5f, -10.0f, -12.5f, -15.0f, -17.5f, -20.0f }; -/* --- Callback types (ISR-safe) --- */ +/* --- Callback types --- */ typedef void (*RadioRxCallback)(void *user_data); typedef void (*RadioTxDoneCallback)(void *user_data); -/* --- Zephyr enum mapping utilities --- */ +/* --- Zephyr enum mapping --- */ + -/* Map Zephyr bandwidth enum to Hz */ static inline uint32_t bandwidth_to_hz(enum lora_signal_bandwidth bw) { switch (bw) { @@ -63,9 +57,7 @@ static inline uint32_t bandwidth_to_hz(enum lora_signal_bandwidth bw) } } -/* Map kHz bandwidth value to Zephyr enum. - * Input is (uint16_t)(float_bw) — truncated, e.g. 7.8→7, 10.4→10, 62.5→62. - * Zephyr >=4.4 has narrow BWs (7-62 kHz); <=4.3 only has 125/250/500. */ +/* Input is truncated kHz (e.g. 7.8→7, 10.4→10, 62.5→62) */ static inline enum lora_signal_bandwidth bw_khz_to_enum(uint16_t bw_khz) { switch (bw_khz) { @@ -83,7 +75,7 @@ static inline enum lora_signal_bandwidth bw_khz_to_enum(uint16_t bw_khz) } } -/* Map ZephCore CR (5-8) to Zephyr coding_rate enum (1-4) */ +/* CR 5-8 → Zephyr coding_rate enum */ static inline enum lora_coding_rate cr_to_enum(uint8_t cr) { switch (cr) { diff --git a/zephcore/app/CompanionMesh.h b/zephcore/app/CompanionMesh.h index e655393..2fdd887 100644 --- a/zephcore/app/CompanionMesh.h +++ b/zephcore/app/CompanionMesh.h @@ -10,7 +10,7 @@ #include #include -/* BLE push notification codes - matches Arduino */ +/* BLE push notification codes */ #define PUSH_CODE_ADVERT 0x80 #define PUSH_CODE_PATH_UPDATED 0x81 #define PUSH_CODE_SEND_CONFIRMED 0x82 @@ -36,36 +36,34 @@ #define AUTO_ADD_ROOM_SERVER (1 << 3) #define AUTO_ADD_SENSOR (1 << 4) -/* Maximum BLE frame size — must match NUS MTU negotiated size. - * All protocol response buffers use this. */ +/* Max BLE frame size — must match NUS MTU negotiated size */ #define MAX_FRAME_SIZE 172 -/* Contact frame size: 1 header + 32 pubkey + 1 type + 1 flags + 1 path_len - * + 64 path + 32 name + 4 timestamp + 4 lat + 4 lon + 4 lastmod = 148 */ +/* 1 header + 32 pubkey + 1 type + 1 flags + 1 path_len + 64 path + 32 name + 4*4 fields = 148 */ #define CONTACT_FRAME_SIZE 148 -/* Offline message queue size */ +/* Offline message queue depth */ #ifdef CONFIG_ZEPHCORE_OFFLINE_QUEUE_SIZE #define OFFLINE_QUEUE_SIZE CONFIG_ZEPHCORE_OFFLINE_QUEUE_SIZE #else #define OFFLINE_QUEUE_SIZE 16 #endif -/* ACK table size */ +/* Pending ACK tracking slots */ #ifdef CONFIG_ZEPHCORE_ACK_TABLE_SIZE #define ACK_TABLE_SIZE CONFIG_ZEPHCORE_ACK_TABLE_SIZE #else #define ACK_TABLE_SIZE 8 #endif -/* Advert path table size */ +/* Recently-heard advert path slots */ #ifdef CONFIG_ZEPHCORE_ADVERT_PATH_TABLE_SIZE #define ADVERT_PATH_TABLE_SIZE CONFIG_ZEPHCORE_ADVERT_PATH_TABLE_SIZE #else #define ADVERT_PATH_TABLE_SIZE 16 #endif -/* Structure for tracking recently heard adverts */ +/* Cached advert path entry */ struct AdvertPath { uint8_t pubkey_prefix[7]; uint8_t path_len; @@ -74,19 +72,19 @@ struct AdvertPath { uint8_t path[MAX_PATH_SIZE]; }; -/* Callback type for BLE push notifications */ +/* BLE push notification callback */ typedef void (*PushCallback)(uint8_t code, const uint8_t *data, size_t len); -/* Callback type for write frame */ +/* BLE write frame callback */ typedef size_t (*WriteFrameCallback)(const uint8_t *data, size_t len); -/* Callback for getting battery millivolts */ +/* Battery millivolt read callback */ typedef uint16_t (*GetBatteryCallback)(void); -/* Callback for radio reconfigure */ +/* Radio reconfigure callback */ typedef void (*RadioReconfigureCallback)(void); -/* Callback for BLE PIN change */ +/* BLE PIN change callback */ typedef void (*PinChangeCallback)(uint32_t new_pin); /* Callback for scheduling background save (called instead of blocking) */ diff --git a/zephcore/helpers/IdentityStore.h b/zephcore/helpers/IdentityStore.h index 16930c2..34b593a 100644 --- a/zephcore/helpers/IdentityStore.h +++ b/zephcore/helpers/IdentityStore.h @@ -1,9 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * IdentityStore.h - Compatibility header for ZephCore constants - * - * This header provides common identity and crypto constants used - * by repeater helpers. It simply re-exports from mesh/MeshCore.h. + * IdentityStore.h - Re-exports identity/crypto constants from MeshCore */ #pragma once diff --git a/zephcore/helpers/NodePrefs.h b/zephcore/helpers/NodePrefs.h index d977c73..3d82c82 100644 --- a/zephcore/helpers/NodePrefs.h +++ b/zephcore/helpers/NodePrefs.h @@ -2,12 +2,8 @@ * SPDX-License-Identifier: Apache-2.0 * NodePrefs - persisted node configuration (unified for all roles) * - * Matches Arduino MeshCore's single NodePrefs struct. - * Both companion and repeater use this same type. Role-specific - * fields are simply unused by the other role. - * - * Storage format is field-by-field (not raw memcpy), so struct - * layout doesn't affect on-disk compatibility. + * Serialized field-by-field, not raw memcpy; struct layout does + * not affect on-disk compatibility. */ #pragma once @@ -37,13 +33,13 @@ struct NodePrefs { float freq; int8_t tx_power_dbm; uint8_t disable_fwd; // repeater: disable forwarding - uint8_t advert_interval; // minutes / 2 + uint8_t advert_interval; // stored as minutes / 2 uint8_t flood_advert_interval; // hours float rx_delay_base; float tx_delay_factor; char guest_password[16]; float direct_tx_delay_factor; - float backoff_multiplier; // reactive backoff cap (0.0 = sentinel → use default 0.5) + float backoff_multiplier; // per-dupe reactive backoff (0.0 = disabled) uint32_t guard; uint8_t sf; uint8_t cr; @@ -52,7 +48,7 @@ struct NodePrefs { float bw; uint8_t flood_max; uint8_t interference_threshold; - uint8_t agc_reset_interval; // secs / 4 + uint8_t agc_reset_interval; // stored as secs / 4 // Power saving uint8_t powersaving_enabled; // GPS settings @@ -62,12 +58,12 @@ struct NodePrefs { uint32_t discovery_mod_timestamp; float adc_multiplier; char owner_info[120]; - uint8_t rx_boost; // 1 = boosted RX gain (+3dB, +2mA), 0 = power save - uint8_t rx_duty_cycle; // 1 = RX duty cycle (power save), 0 = continuous RX - uint8_t apc_enabled; // 1 = adaptive power control on, 0 = fixed TX power - uint8_t apc_margin; // APC target link margin in dB (6-30, default 16) + uint8_t rx_boost; // 1 = boosted RX gain (+3dB), 0 = power save + uint8_t rx_duty_cycle; // 1 = RX duty cycle, 0 = continuous RX + uint8_t apc_enabled; // 1 = APC on, 0 = fixed TX power + uint8_t apc_margin; // APC target link margin dB (6-30) - /* ---- Companion-only fields (Zephyr additions, not in Arduino) ---- */ + /* ---- Companion-only fields ---- */ uint8_t manual_add_contacts; uint8_t telemetry_mode_base; uint8_t telemetry_mode_loc; @@ -75,19 +71,17 @@ struct NodePrefs { uint32_t ble_pin; uint8_t buzzer_quiet; uint8_t autoadd_config; - uint8_t client_repeat; // 1 = offgrid mode (forward packets), 0 = companion only - uint8_t path_hash_mode; // which path mode to use when sending (0-2) - uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) - uint8_t loop_detect; // LOOP_DETECT_OFF/MINIMAL/MODERATE/STRICT - uint8_t leds_disabled; // 1 = LEDs off (heartbeat disabled), 0 = LEDs on + uint8_t client_repeat; // 1 = offgrid mode (forward packets) + uint8_t path_hash_mode; // path mode 0-2 + uint8_t autoadd_max_hops; // 0 = no limit, N = up to N-1 hops + uint8_t loop_detect; // LOOP_DETECT_{OFF,MINIMAL,MODERATE,STRICT} + uint8_t leds_disabled; // 1 = LEDs off }; -/* Default prefs — MUST match LoRaConfig.h defaults for radio interop. - * Used by both roles; each role's data store calls this on first boot. */ +/* Default prefs -- must match LoRaConfig.h defaults for radio interop. */ static inline void initNodePrefs(NodePrefs* prefs) { memset(prefs, 0, sizeof(NodePrefs)); - prefs->airtime_factor = 10.0f; /* 10% duty cycle (EU 868 default) */ - /* node_name left empty — each role sets its own default name */ + prefs->airtime_factor = 10.0f; /* 10% duty cycle */ prefs->node_lat = 0.0; prefs->node_lon = 0.0; #ifdef CONFIG_ZEPHCORE_ADMIN_PASSWORD diff --git a/zephcore/helpers/ui/buzzer.c b/zephcore/helpers/ui/buzzer.c index 7990789..98c314c 100644 --- a/zephcore/helpers/ui/buzzer.c +++ b/zephcore/helpers/ui/buzzer.c @@ -29,18 +29,14 @@ #include LOG_MODULE_REGISTER(buzzer, CONFIG_ZEPHCORE_BOARD_LOG_LEVEL); -/* ========== Dedicated Buzzer Work Queue ========== */ -/* Runs note scheduling at high priority so flash/BLE/FS operations - * on the system workqueue can't delay tone timing. */ +/* Dedicated work queue — high priority prevents flash/BLE/FS delays */ #define BUZZER_WQ_STACK_SIZE 512 -#define BUZZER_WQ_PRIORITY 2 /* Higher than default workqueue (usually 10+) */ +#define BUZZER_WQ_PRIORITY 2 /* above default wq (~10+) */ K_THREAD_STACK_DEFINE(buzzer_wq_stack, BUZZER_WQ_STACK_SIZE); static struct k_work_q buzzer_wq; -/* Maximum duration (ms) a single tone can play before auto-silence. - * Safety net: if the work queue stalls or the handler doesn't fire, - * the hardware timer kills the PWM after this timeout. */ +/* Safety watchdog: auto-silence if note handler stalls */ #define BUZZER_TONE_MAX_MS 2000 /* ========== Note Frequency Table ========== */ diff --git a/zephcore/helpers/ui/buzzer.h b/zephcore/helpers/ui/buzzer.h index 63d6f90..0fdcfe5 100644 --- a/zephcore/helpers/ui/buzzer.h +++ b/zephcore/helpers/ui/buzzer.h @@ -4,14 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Non-blocking RTTTL melody playback using Zephyr PWM API. - * Notes are scheduled via k_work_delayable - no polling needed. - * - * Usage: - * buzzer_init(); // Auto-detect from DT aliases - * buzzer_play(MELODY_STARTUP); // Start melody (non-blocking) - * buzzer_stop(); // Stop immediately - * buzzer_set_quiet(true); // Mute - * buzzer_is_playing(); // Check if melody active + * Notes scheduled via k_work_delayable on a dedicated work queue. */ #ifndef ZEPHCORE_BUZZER_H @@ -23,8 +16,7 @@ extern "C" { #endif -/* ========== Predefined RTTTL Melodies ========== */ -/* Format: "Name:d=duration,o=octave,b=bpm:notes..." */ +/* Predefined RTTTL melodies */ #define MELODY_STARTUP "Startup:d=4,o=5,b=160:16c6,16e6,8g6" #define MELODY_SHUTDOWN "Shutdown:d=4,o=5,b=100:8g5,16e5,16c5" @@ -32,58 +24,38 @@ extern "C" { #define MELODY_MSG_CHANNEL "kerplop:d=16,o=6,b=120:32g#,32c#" #define MELODY_ACK "ack:d=32,o=8,b=120:c" -/* ========== Public API ========== */ - /** - * Initialize buzzer from devicetree. - * Looks for 'buzzer' alias in DT → pwm-leds node. - * Also checks for optional 'buzzer-enable' alias for power gating. - * - * @return 0 on success, negative errno on failure, -ENODEV if no buzzer in DT + * Initialize buzzer from devicetree ('buzzer' alias → pwm-leds). + * Optional 'buzzer-enable' alias for amplifier power gating. + * @return 0 on success, -ENODEV if no buzzer in DT, negative errno on failure */ int buzzer_init(void); /** - * Play an RTTTL melody string. Non-blocking - returns immediately. - * If a melody is already playing, it is stopped first. - * If buzzer is in quiet mode, this is a no-op. - * - * @param rtttl RTTTL format melody string (must remain valid until done) + * Play an RTTTL melody string (non-blocking). Stops any current melody. + * No-op if quiet. String must remain valid until melody completes. */ void buzzer_play(const char *rtttl); -/** - * Stop any playing melody immediately and silence the buzzer. - */ +/** Stop current melody and silence the buzzer. */ void buzzer_stop(void); /** - * Set quiet mode. When quiet, buzzer_play() is a no-op. - * Also controls the optional enable pin (power gate). - * - * @param quiet true to mute, false to enable + * Set quiet mode. Stops current melody and gates amplifier power. + * When quiet, buzzer_play() becomes a no-op. */ void buzzer_set_quiet(bool quiet); -/** - * @return true if buzzer is in quiet mode - */ +/** @return true if muted */ bool buzzer_is_quiet(void); /** - * Set quiet mode without stopping a currently playing melody. - * Unlike buzzer_set_quiet(), this allows the in-progress melody - * to finish playing. Future buzzer_play() calls will be suppressed. - * Useful for "mute" feedback where you want the confirmation sound - * to play out before silence takes effect. - * - * @param quiet true to mute (after current melody), false to enable + * Set quiet mode without interrupting the current melody. + * In-progress melody plays to completion; future plays suppressed. */ void buzzer_set_quiet_deferred(bool quiet); -/** - * @return true if a melody is currently playing - */ +/** @return true if a melody is currently playing */ bool buzzer_is_playing(void); #ifdef __cplusplus diff --git a/zephcore/include/mesh/Board.h b/zephcore/include/mesh/Board.h index 4968126..59baa2c 100644 --- a/zephcore/include/mesh/Board.h +++ b/zephcore/include/mesh/Board.h @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * ZephCore MainBoard interface - matches MeshCore.h + * ZephCore MainBoard interface */ #pragma once diff --git a/zephcore/include/mesh/Clock.h b/zephcore/include/mesh/Clock.h index 8404924..7cde515 100644 --- a/zephcore/include/mesh/Clock.h +++ b/zephcore/include/mesh/Clock.h @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * ZephCore clock interfaces - matches Dispatcher.h / MeshCore.h + * ZephCore clock interfaces */ #pragma once diff --git a/zephcore/include/mesh/ContentionTracker.h b/zephcore/include/mesh/ContentionTracker.h index 9194591..0f45ede 100644 --- a/zephcore/include/mesh/ContentionTracker.h +++ b/zephcore/include/mesh/ContentionTracker.h @@ -1,12 +1,9 @@ /* * SPDX-License-Identifier: Apache-2.0 - * Adaptive Contention Window — replaces static txdelay/rxdelay + * Adaptive Contention Window — EMA-based flood retransmit delay * - * Measures local retransmit contention by counting how many times - * we hear the same flood packet retransmitted by neighbors within - * a 10-second window after we decide to retransmit it ourselves. - * Feeds dupe counts into a rolling EMA to produce an adaptive - * delay factor for future retransmits. + * Counts neighbor retransmit dupes within a 10s window per packet. + * Dupe counts feed a rolling EMA that drives an adaptive delay factor. */ #pragma once @@ -21,35 +18,26 @@ class ContentionTracker { public: ContentionTracker(); - /* Cheap 32-bit hash for packet correlation (FNV-1a). - * NOT the same as the SHA256 used for dedup — this is only - * for matching packets in the 16-entry ring buffer. */ + /* FNV-1a 32-bit hash for ring buffer correlation (not dedup SHA256). */ static uint32_t computePacketHash32(const Packet *pkt); - /* Called when we decide to retransmit a flood packet. */ void trackRetransmit(uint32_t hash32, uint32_t now_ms); - /* Called for every received flood packet. Returns true if - * the packet matched a tracked retransmit (dupe recorded). - * Caller should attempt reactive backoff when true. */ + /* Returns true if packet matched a tracked retransmit (dupe recorded). */ bool recordDupeIfTracked(uint32_t hash32, uint32_t now_ms); - /* Per-dupe reactive delay: returns backoff_multiplier × airtime, - * clamped by hard cap minus cumulative extension so far. + /* Returns backoff_multiplier * airtime, clamped by remaining headroom. * Returns 0 when hard cap reached or backoff disabled. */ uint16_t getReactiveHeadroom(uint32_t hash32, uint32_t airtime_ms) const; - /* Record that we added reactive extension to this entry. */ void addReactiveExtension(uint32_t hash32, uint16_t added_ms); - /* Finalize expired entries into EMA. Call from maintenanceLoop. */ + /* Finalize expired entries into EMA. */ void tick(uint32_t now_ms); - /* Current contention estimate (EMA of dupes per retransmitted packet). */ float getContentionEstimate() const; - /* Adaptive delay factor for flood retransmits. - * sqrt curve: 0.05 + 0.116 * sqrt(est), cap 2.0. + /* sqrt curve: MIN_FLOOD_FACTOR + FLOOD_SCALE * sqrt(est), cap 2.0. * Returns 0.5 during warmup. */ float getFloodDelayFactor() const; @@ -59,16 +47,16 @@ public: float getBackoffMultiplier() const { return _backoff_multiplier; } private: - static constexpr int RING_SIZE = 16; - static constexpr uint32_t WINDOW_MS = 10000; - static constexpr int EMA_SHIFT = 3; /* alpha = 1/8 */ - static constexpr int WARMUP_PACKETS = 4; - static constexpr float MIN_FLOOD_FACTOR = 0.05f; - static constexpr float FLOOD_SCALE = 0.170f; /* (0.5 - 0.05) / sqrt(15) */ - static constexpr float MAX_FLOOD_FACTOR = 2.0f; - static constexpr float DEFAULT_BACKOFF_MULT = 0.5f; - static constexpr uint32_t REACTIVE_HARD_CAP_MS = 2000; - static constexpr uint32_t STALE_MS = 300000; /* 5 minutes */ + static constexpr int RING_SIZE = 16; /* max concurrent tracked retransmits */ + static constexpr uint32_t WINDOW_MS = 10000; /* dupe observation window; covers SF12 2-hop */ + static constexpr int EMA_SHIFT = 3; /* alpha = 1/8 */ + static constexpr int WARMUP_PACKETS = 4; /* min samples before EMA is trusted */ + static constexpr float MIN_FLOOD_FACTOR = 0.05f; /* floor: near-zero delay in quiet networks */ + static constexpr float FLOOD_SCALE = 0.170f; /* (0.5 - 0.05) / sqrt(15) */ + static constexpr float MAX_FLOOD_FACTOR = 2.0f; /* ceiling: 2x base airtime */ + static constexpr float DEFAULT_BACKOFF_MULT = 0.5f; /* half-airtime per dupe heard */ + static constexpr uint32_t REACTIVE_HARD_CAP_MS = 2000; /* max cumulative reactive extension */ + static constexpr uint32_t STALE_MS = 300000; /* 5 min: reset EMA if no traffic */ struct Entry { uint32_t hash32; diff --git a/zephcore/include/mesh/Dispatcher.h b/zephcore/include/mesh/Dispatcher.h index 73358a4..5122cca 100644 --- a/zephcore/include/mesh/Dispatcher.h +++ b/zephcore/include/mesh/Dispatcher.h @@ -31,7 +31,7 @@ struct DutyCycleTracker { void recordTx(uint32_t duration_ms, uint32_t now) { if (duty_pct == 0) return; - if (now - window_start > 3600000UL) { + if (now - window_start > 3600000UL) { /* 1 hour window */ window_start = now; window_airtime_ms = 0; } @@ -40,14 +40,14 @@ struct DutyCycleTracker { bool isExceeded(uint32_t now) const { if (duty_pct == 0) return false; - if (now - window_start > 3600000UL) return false; /* window expired */ - uint32_t budget_ms = (3600000UL / 100) * (uint32_t)duty_pct; + if (now - window_start > 3600000UL) return false; /* 1h window expired */ + uint32_t budget_ms = (3600000UL / 100) * (uint32_t)duty_pct; /* ms per 1% of 1h */ return window_airtime_ms >= budget_ms; } uint32_t budgetMs() const { if (duty_pct == 0) return 0; - return (3600000UL / 100) * (uint32_t)duty_pct; + return (3600000UL / 100) * (uint32_t)duty_pct; /* ms per 1% of 1h */ } }; @@ -68,8 +68,7 @@ public: virtual Packet *getNextInbound(uint32_t now) = 0; }; -/* Callback fired when a packet is queued for transmission with a delay. - * Allows the event loop to schedule a precise wake at delay expiry. */ +/* Notifies event loop of pending TX so it can schedule a wake. */ typedef void (*tx_queued_callback_t)(uint32_t delay_ms, void *user_data); typedef uint32_t DispatcherAction; diff --git a/zephcore/include/mesh/Identity.h b/zephcore/include/mesh/Identity.h index 79e6e5a..ee6f181 100644 --- a/zephcore/include/mesh/Identity.h +++ b/zephcore/include/mesh/Identity.h @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * ZephCore Identity - Ed25519 public identity + * ZephCore Identity - Ed25519 key pairs */ #pragma once diff --git a/zephcore/include/mesh/LoRaConfig.h b/zephcore/include/mesh/LoRaConfig.h index cc89eb9..3da0bfe 100644 --- a/zephcore/include/mesh/LoRaConfig.h +++ b/zephcore/include/mesh/LoRaConfig.h @@ -10,12 +10,12 @@ namespace mesh { struct LoRaConfig { - static constexpr uint32_t FREQ_HZ = 869618000; - static constexpr uint16_t BANDWIDTH = 62; /* BW_62_KHZ */ - static constexpr uint8_t SPREADING_FACTOR = 8; - static constexpr uint8_t CODING_RATE = 8; /* CR_4_8 */ - static constexpr uint16_t PREAMBLE_LEN = 16; - static constexpr int8_t TX_POWER_DBM = 22; + static constexpr uint32_t FREQ_HZ = 869618000; /* MeshCore default channel (EU 869 MHz ISM) */ + static constexpr uint16_t BANDWIDTH = 62; /* BW_62_KHZ — MeshCore default */ + static constexpr uint8_t SPREADING_FACTOR = 8; /* SF8: balance of range vs airtime */ + static constexpr uint8_t CODING_RATE = 8; /* CR_4_8: max FEC */ + static constexpr uint16_t PREAMBLE_LEN = 16; /* MeshCore default; longer aids RX sync */ + static constexpr int8_t TX_POWER_DBM = 22; /* SX1262 max */ }; } /* namespace mesh */ diff --git a/zephcore/include/mesh/MeshCore.h b/zephcore/include/mesh/MeshCore.h index 214f8ad..42faab3 100644 --- a/zephcore/include/mesh/MeshCore.h +++ b/zephcore/include/mesh/MeshCore.h @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * ZephCore constants and base types (Zephyr port) + * ZephCore constants and base types */ #pragma once @@ -8,28 +8,21 @@ #include #include -#define MAX_HASH_SIZE 8 -#define PUB_KEY_SIZE 32 -#define PRV_KEY_SIZE 64 -#define SEED_SIZE 32 -#define SIGNATURE_SIZE 64 +#define MAX_HASH_SIZE 8 /* SHA256 truncated to 8 bytes for dedup */ +#define PUB_KEY_SIZE 32 /* Ed25519 public key */ +#define PRV_KEY_SIZE 64 /* Ed25519 expanded private key */ +#define SEED_SIZE 32 /* Ed25519 seed */ +#define SIGNATURE_SIZE 64 /* Ed25519 signature */ #define MAX_ADVERT_DATA_SIZE 32 -#define CIPHER_KEY_SIZE 16 -#define CIPHER_BLOCK_SIZE 16 -#define CIPHER_MAC_SIZE 2 -#define PATH_HASH_SIZE 1 +#define CIPHER_KEY_SIZE 16 /* AES-128 */ +#define CIPHER_BLOCK_SIZE 16 /* AES block size */ +#define CIPHER_MAC_SIZE 2 /* truncated MAC for bandwidth savings */ +#define PATH_HASH_SIZE 1 /* 1-byte per-hop hash in path field */ -#define MAX_PACKET_PAYLOAD 184 -#define MAX_PATH_SIZE 64 -#define MAX_TRANS_UNIT 255 +#define MAX_PACKET_PAYLOAD 184 /* fits in SX126x 255-byte FIFO with headers */ +#define MAX_PATH_SIZE 64 /* max hops * max hash size */ +#define MAX_TRANS_UNIT 255 /* SX126x FIFO limit */ #define MAX_GROUP_DATA_LENGTH (MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE - 3) #define MESH_DEBUG_PRINT(...) #define MESH_DEBUG_PRINTLN(...) - -namespace mesh { - -#define BD_STARTUP_NORMAL 0 -#define BD_STARTUP_RX_PACKET 1 - -} /* namespace mesh */ diff --git a/zephcore/include/mesh/Packet.h b/zephcore/include/mesh/Packet.h index a9a893d..11c0d64 100644 --- a/zephcore/include/mesh/Packet.h +++ b/zephcore/include/mesh/Packet.h @@ -51,7 +51,7 @@ public: uint16_t transport_codes[2]; uint8_t path[MAX_PATH_SIZE]; uint8_t payload[MAX_PACKET_PAYLOAD]; - int8_t _snr; + int8_t _snr; /* SNR * 4 (quarter-dB fixed point) */ void calculatePacketHash(uint8_t *dest_hash) const; uint8_t getRouteType() const { return header & PH_ROUTE_MASK; } @@ -61,11 +61,12 @@ public: uint8_t getPayloadType() const { return (header >> PH_TYPE_SHIFT) & PH_TYPE_MASK; } uint8_t getPayloadVer() const { return (header >> PH_VER_SHIFT) & PH_VER_MASK; } + /* path_len layout: bits[7:6] = hash_size - 1, bits[5:0] = hop count */ uint8_t getPathHashSize() const { return (path_len >> 6) + 1; } - uint8_t getPathHashCount() const { return path_len & 63; } + uint8_t getPathHashCount() const { return path_len & 0x3F; } uint8_t getPathByteLen() const { return getPathHashCount() * getPathHashSize(); } - void setPathHashCount(uint8_t n) { path_len &= ~63; path_len |= n; } - void setPathHashSizeAndCount(uint8_t sz, uint8_t n) { path_len = ((sz - 1) << 6) | (n & 63); } + void setPathHashCount(uint8_t n) { path_len &= ~0x3F; path_len |= n; } + void setPathHashSizeAndCount(uint8_t sz, uint8_t n) { path_len = ((sz - 1) << 6) | (n & 0x3F); } static uint8_t copyPath(uint8_t *dest, const uint8_t *src, uint8_t path_len); static size_t writePath(uint8_t *dest, const uint8_t *src, uint8_t path_len); diff --git a/zephcore/src/ContentionTracker.cpp b/zephcore/src/ContentionTracker.cpp index 02237f9..6012089 100644 --- a/zephcore/src/ContentionTracker.cpp +++ b/zephcore/src/ContentionTracker.cpp @@ -17,11 +17,10 @@ ContentionTracker::ContentionTracker() memset(_ring, 0, sizeof(_ring)); } -/* FNV-1a hash of payload_type + first 8 bytes of payload. - * Cheap and sufficient for 16-entry correlation. */ +/* FNV-1a over payload_type + first 8 payload bytes */ uint32_t ContentionTracker::computePacketHash32(const Packet *pkt) { - uint32_t h = 0x811c9dc5u; /* FNV offset basis */ + uint32_t h = 0x811c9dc5u; /* FNV-1a offset basis */ uint8_t t = pkt->getPayloadType(); h = (h ^ t) * 0x01000193u; int n = pkt->payload_len < 8 ? pkt->payload_len : 8; @@ -50,14 +49,13 @@ void ContentionTracker::finalizeEntry(int idx) int32_t diff = (int32_t)sample_x256 - (int32_t)_ema_x256; if (_finalized_count < WARMUP_PACKETS) { - /* During warmup, seed the EMA directly */ + /* Warmup: seed EMA with fast convergence */ if (_finalized_count == 0) { _ema_x256 = sample_x256; } else { _ema_x256 = (uint32_t)((int32_t)_ema_x256 + (diff >> 1)); } } else { - /* Normal EMA update: ema += (sample - ema) >> shift */ _ema_x256 = (uint32_t)((int32_t)_ema_x256 + (diff >> EMA_SHIFT)); } @@ -69,7 +67,7 @@ void ContentionTracker::trackRetransmit(uint32_t hash32, uint32_t now_ms) { _last_retransmit_ms = now_ms; - /* If ring is full, finalize the oldest active entry */ + /* Evict oldest if ring slot occupied */ if (_ring[_next_idx].active) { finalizeEntry(_next_idx); } @@ -91,7 +89,6 @@ bool ContentionTracker::recordDupeIfTracked(uint32_t hash32, uint32_t now_ms) Entry &e = _ring[idx]; - /* Check if entry has expired */ if (now_ms - e.first_seen_ms > WINDOW_MS) { finalizeEntry(idx); return false; @@ -111,7 +108,7 @@ uint16_t ContentionTracker::getReactiveHeadroom(uint32_t hash32, uint32_t airtim uint32_t per_dupe = (uint32_t)(_backoff_multiplier * (float)airtime_ms); if (per_dupe == 0) return 0; - /* Hard cap on total reactive extension per packet */ + /* Hard cap: REACTIVE_HARD_CAP_MS total extension per packet */ if (_ring[idx].reactive_added_ms >= REACTIVE_HARD_CAP_MS) return 0; uint32_t remaining = REACTIVE_HARD_CAP_MS - _ring[idx].reactive_added_ms; @@ -130,14 +127,13 @@ void ContentionTracker::addReactiveExtension(uint32_t hash32, uint16_t added_ms) void ContentionTracker::tick(uint32_t now_ms) { - /* Finalize expired entries */ for (int i = 0; i < RING_SIZE; i++) { if (_ring[i].active && now_ms - _ring[i].first_seen_ms > WINDOW_MS) { finalizeEntry(i); } } - /* Staleness decay: if no retransmit in 5 minutes, decay toward 0 */ + /* Decay EMA toward 0 if no retransmit in STALE_MS */ if (_last_retransmit_ms != 0 && now_ms - _last_retransmit_ms > STALE_MS) { if (_ema_x256 > 0) { _ema_x256 -= _ema_x256 >> EMA_SHIFT; @@ -152,7 +148,7 @@ float ContentionTracker::getContentionEstimate() const float ContentionTracker::getFloodDelayFactor() const { - if (!isWarmedUp()) return 0.5f; + if (!isWarmedUp()) return 0.5f; /* conservative default before warmup */ float est = getContentionEstimate(); if (est <= 0.0f) return MIN_FLOOD_FACTOR; diff --git a/zephcore/src/Dispatcher.cpp b/zephcore/src/Dispatcher.cpp index fd9d3f6..d7bbe64 100644 --- a/zephcore/src/Dispatcher.cpp +++ b/zephcore/src/Dispatcher.cpp @@ -12,7 +12,6 @@ LOG_MODULE_REGISTER(zephcore_dispatcher, CONFIG_ZEPHCORE_LORA_LOG_LEVEL); #if IS_ENABLED(CONFIG_ZEPHCORE_PACKET_LOGGING) -/* Payload types for [src -> dest] logging */ #define PAYLOAD_TYPE_REQ 0x00 #define PAYLOAD_TYPE_RESPONSE 0x01 #define PAYLOAD_TYPE_TXT_MSG 0x02 @@ -21,7 +20,7 @@ LOG_MODULE_REGISTER(zephcore_dispatcher, CONFIG_ZEPHCORE_LORA_LOG_LEVEL); namespace mesh { -#define MAX_RX_DELAY_MILLIS 32000 +#define MAX_RX_DELAY_MILLIS 32000 /* upper bound for score-based RX delay */ Dispatcher::Dispatcher(Radio &radio, MillisecondClock &ms, PacketManager &mgr) : _radio(&radio), _ms(&ms), _mgr(&mgr) @@ -54,7 +53,7 @@ void Dispatcher::begin() uint8_t Dispatcher::getDutyCyclePercent() const { - return 10; + return 10; /* EU 868 default: 10% duty cycle */ } bool Dispatcher::isAdminPacket(const Packet *pkt) @@ -66,8 +65,7 @@ bool Dispatcher::isAdminPacket(const Packet *pkt) int Dispatcher::calcRxDelay(float score, uint32_t air_time) const { - /* Lookup table: (10^(0.85 - i*0.1) - 1) for i=0..10 (score 0.0 to 1.0) - * Replaces powf() to save ~1.9KB flash. Linear interpolation between entries. */ + /* LUT: 10^(0.85 - i*0.1) - 1, i=0..10; replaces powf() (~1.9KB saved) */ static const float lut[11] = { 6.0793f, 4.6236f, 3.4674f, 2.5489f, 1.8184f, 1.2389f, 0.7783f, 0.4125f, 0.1220f, -0.1089f, -0.2921f @@ -83,12 +81,12 @@ int Dispatcher::calcRxDelay(float score, uint32_t air_time) const uint32_t Dispatcher::getCADFailRetryDelay() const { - return 200; + return 200; /* ms between CAD retries; ~2 LoRa symbol periods at SF8/62.5k */ } uint32_t Dispatcher::getCADFailMaxDuration() const { - return 4000; + return 4000; /* ms; ~20 retry attempts before giving up */ } void Dispatcher::loop() @@ -130,14 +128,10 @@ void Dispatcher::loop() void Dispatcher::maintenanceLoop() { - /* Noise floor calibration — one EMA tick per housekeeping cycle */ _radio->triggerNoiseFloorCalibrate(getInterferenceThreshold()); - /* RX mode watchdog — detect if radio is stuck in neither RX nor TX. - * Count TX time as "active" so rapid consecutive relays on a busy - * repeater don't falsely trigger the flag: the housekeeping timer - * (5 s) can miss brief RX windows between TXes, leaving - * radio_nonrx_start stale and firing the watchdog spuriously. */ + /* RX mode watchdog: TX counts as "active" to avoid false triggers + * when the 5s housekeeping timer misses brief RX windows. */ bool is_active = _radio->isInRecvMode() || !_radio->isSendComplete(); if (is_active != prev_isrecv_mode) { prev_isrecv_mode = is_active; @@ -145,11 +139,11 @@ void Dispatcher::maintenanceLoop() radio_nonrx_start = (uint32_t)_ms->getMillis(); } } - if (!is_active && (uint32_t)_ms->getMillis() - radio_nonrx_start > 8000) { + if (!is_active && (uint32_t)_ms->getMillis() - radio_nonrx_start > 8000) { /* 8s stall threshold */ _err_flags |= ERR_EVENT_STARTRX_TIMEOUT; } - /* AGC reset — periodic warm sleep + recalibration */ + /* Periodic AGC recalibration */ if (getAGCResetInterval() > 0 && millisHasNowPassed(next_agc_reset_time)) { _radio->resetAGC(); next_agc_reset_time = futureMillis(getAGCResetInterval()); @@ -175,7 +169,7 @@ bool Dispatcher::tryParsePacket(Packet *pkt, const uint8_t *raw, int len) pkt->path_len = raw[i++]; uint8_t path_mode = pkt->path_len >> 6; - if (path_mode == 3) { // Reserved for future + if (path_mode == 3) { /* reserved path mode */ LOG_WRN("tryParsePacket: unsupported path mode: 3"); return false; } @@ -200,14 +194,13 @@ bool Dispatcher::tryParsePacket(Packet *pkt, const uint8_t *raw, int len) void Dispatcher::checkRecv() { - /* Drain ALL queued LoRa packets per wake. - * k_event is a bitfield (not a counter), so multiple ISR arrivals - * may only produce one wake. We must empty the ring each time. */ + /* k_event is a bitfield — multiple ISR arrivals coalesce into one + * wake, so drain the entire ring each time. */ for (;;) { uint8_t raw[MAX_TRANS_UNIT + 1]; int len = _radio->recvRaw(raw, MAX_TRANS_UNIT); if (len <= 0) { - break; /* ring empty — done */ + break; } logRxRaw(_radio->getLastSNR(), _radio->getLastRSSI(), raw, len); @@ -222,7 +215,7 @@ void Dispatcher::checkRecv() uint32_t air_time = 0; if (tryParsePacket(pkt, raw, len)) { - pkt->_snr = (int8_t)(_radio->getLastSNR() * 4.0f); + pkt->_snr = (int8_t)(_radio->getLastSNR() * 4.0f); /* x4 fixed-point SNR */ score = _radio->packetScore(_radio->getLastSNR(), len); air_time = _radio->getEstAirtimeFor(len); rx_air_time += air_time;