diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index a074910..f410053 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -210,7 +210,7 @@ set(ZEPHCORE_COMMON_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/zephcore_com # Detect platform from board name and add platform-specific common config # For boards with qualifiers like "wio_tracker_l1/nrf52840", check both the full BOARD # string and the BOARD_QUALIFIERS which contains just the qualifier (e.g., "nrf52840") -if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wismesh_tag" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "thinknode_m1" OR BOARD MATCHES "thinknode_m3" OR BOARD MATCHES "thinknode_m6") +if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wismesh_tag" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "thinknode_m1" OR BOARD MATCHES "thinknode_m3" OR BOARD MATCHES "thinknode_m6" OR BOARD MATCHES "promicro_lr2021") set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf") elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*nrf52.*") set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf") @@ -395,6 +395,18 @@ if(CONFIG_ZEPHCORE_RADIO_LR1110) target_include_directories(app PRIVATE ${ZEPHYR_DIR}/drivers/lora/lr11xx ) +elseif(CONFIG_ZEPHCORE_RADIO_LR2021) + message(STATUS "ZephCore Radio: LR2021 (Zephyr LoRa driver)") + target_sources(app PRIVATE + adapters/radio/LR2021Radio.cpp + ) + target_include_directories(app PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/adapters/radio/lr20xx + ) + get_filename_component(ZEPHYR_DIR_LR20 ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr ABSOLUTE) + target_include_directories(app PRIVATE + ${ZEPHYR_DIR_LR20}/drivers/lora/lr20xx + ) else() # Default: SX126x via native Zephyr LoRa driver message(STATUS "ZephCore Radio: SX126x (native Zephyr driver)") diff --git a/zephcore/Kconfig b/zephcore/Kconfig index 8f04283..b787332 100644 --- a/zephcore/Kconfig +++ b/zephcore/Kconfig @@ -241,6 +241,15 @@ config ZEPHCORE_RADIO_LR1110 TCXO, RF switch, and PA config are in the device tree. For any board with an LR1110, LR1120, or LR1121 radio. +config ZEPHCORE_RADIO_LR2021 + bool "LR2021 (custom driver)" + select LORA + select SPI + help + Semtech LR2021 (LoRa Plus, 4th-gen) via custom ZephCore driver. + Supports sub-GHz + 2.4 GHz ISM + NTN/SATCOM. + TCXO, RF switch, and PA config are in the device tree. + endchoice config ZEPHCORE_DEFAULT_TX_POWER_DBM diff --git a/zephcore/adapters/radio/LR2021Radio.cpp b/zephcore/adapters/radio/LR2021Radio.cpp new file mode 100644 index 0000000..41cb0de --- /dev/null +++ b/zephcore/adapters/radio/LR2021Radio.cpp @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * LR2021 hardware hooks for LoRaRadioBase. + */ + +#include "LR2021Radio.h" +#include + +/* LR20xx driver extension API */ +extern "C" { +#include "lr20xx_lora.h" +} + +#include +LOG_MODULE_REGISTER(lr2021_radio, CONFIG_ZEPHCORE_LORA_LOG_LEVEL); + +namespace mesh { + +K_THREAD_STACK_DEFINE(lr20xx_tx_wait_stack, TX_WAIT_THREAD_STACK_SIZE); + +LR2021Radio::LR2021Radio(const struct device *lora_dev, MainBoard &board, + NodePrefs *prefs) + : LoRaRadioBase(lora_dev, board, prefs) +{ +} + +void LR2021Radio::begin() +{ + startTxThread(lr20xx_tx_wait_stack, + K_THREAD_STACK_SIZEOF(lr20xx_tx_wait_stack)); + LoRaRadioBase::begin(); +} + +/* ── Hardware primitives ──────────────────────────────────────────────── */ + +void LR2021Radio::hwConfigure(const struct lora_modem_config &cfg) +{ + int ret = lora_config(_dev, const_cast(&cfg)); + if (ret < 0) { + LOG_ERR("lora_config failed: %d", ret); + } +} + +void LR2021Radio::hwStartReceive() +{ + int ret = lora_recv_async(_dev, rxCallbackStatic, this); + if (ret < 0) { + LOG_ERR("lora_recv_async failed: %d", ret); + atomic_set(&_in_recv_mode, 0); + return; + } + atomic_set(&_in_recv_mode, 1); + + if (_rx_duty_cycle_enabled) { + lr20xx_set_rx_duty_cycle(_dev, true); + } +} + +void LR2021Radio::hwCancelReceive() +{ + lora_recv_async(_dev, NULL, NULL); +} + +int LR2021Radio::hwSendAsync(uint8_t *buf, uint32_t len, + struct k_poll_signal *sig) +{ + return lora_send_async(_dev, buf, len, sig); +} + +int16_t LR2021Radio::hwGetCurrentRSSI() +{ + return lr20xx_get_rssi_inst(_dev); +} + +bool LR2021Radio::hwIsPreambleDetected() +{ + return lr20xx_is_receiving(_dev); +} + +void LR2021Radio::hwSetRxBoost(bool enable) +{ + lr20xx_set_rx_boost(_dev, enable); +} + +void LR2021Radio::hwSetRxDutyCycle(bool enable) +{ + lr20xx_set_rx_duty_cycle(_dev, enable); +} + +void LR2021Radio::hwResetAGC() +{ + lr20xx_reset_agc(_dev); +} + +} /* namespace mesh */ diff --git a/zephcore/adapters/radio/LR2021Radio.h b/zephcore/adapters/radio/LR2021Radio.h new file mode 100644 index 0000000..06aa6b4 --- /dev/null +++ b/zephcore/adapters/radio/LR2021Radio.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * ZephCore Radio adapter for LR2021 using Zephyr LoRa driver + * + * Thin wrapper around LoRaRadioBase — only hardware-specific hooks. + */ + +#pragma once + +#include "LoRaRadioBase.h" + +namespace mesh { + +class LR2021Radio : public LoRaRadioBase { +public: + LR2021Radio(const struct device *lora_dev, MainBoard &board, + NodePrefs *prefs = nullptr); + + void begin() override; + +protected: + /* Hardware primitives */ + void hwConfigure(const struct lora_modem_config &cfg) override; + void hwStartReceive() override; + void hwCancelReceive() override; + int hwSendAsync(uint8_t *buf, uint32_t len, + struct k_poll_signal *sig) override; + int16_t hwGetCurrentRSSI() override; + bool hwIsPreambleDetected() override; + void hwSetRxBoost(bool enable) override; + void hwSetRxDutyCycle(bool enable) override; + void hwResetAGC() override; +}; + +} /* namespace mesh */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_driver_version.c b/zephcore/adapters/radio/lr20xx/lr20xx_driver_version.c new file mode 100644 index 0000000..289bbcf --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_driver_version.c @@ -0,0 +1,82 @@ +/*! + * @file lr20xx_driver_version.c + * + * @brief Placeholder to keep the version of LR20XX driver. + * + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr20xx_driver_version.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +const char* lr20xx_driver_version_get_version_string( void ) +{ + return ( const char* ) LR20XX_DRIVER_VERSION; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_driver_version.h b/zephcore/adapters/radio/lr20xx/lr20xx_driver_version.h new file mode 100644 index 0000000..708cd96 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_driver_version.h @@ -0,0 +1,85 @@ +/*! + * @file lr20xx_driver_version.h + * + * @brief Placeholder to keep the version of LR20XX driver. + * + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_DRIVER_VERSION_H +#define LR20XX_DRIVER_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Value of driver version string + */ +#define LR20XX_DRIVER_VERSION "v1.3.4" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * @brief Get version of driver as string + * + * @return String describing driver version + */ +const char* lr20xx_driver_version_get_version_string( void ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_DRIVER_VERSION_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_hal.h b/zephcore/adapters/radio/lr20xx/lr20xx_hal.h new file mode 100644 index 0000000..4bf1f1a --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_hal.h @@ -0,0 +1,179 @@ +/*! + * @file lr20xx_hal.h + * + * @brief Hardware Abstraction Layer (HAL) interface for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_HAL_H +#define LR20XX_HAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief LR20XX HAL status + */ +typedef enum lr20xx_hal_status_e +{ + LR20XX_HAL_STATUS_OK = 0, + LR20XX_HAL_STATUS_ERROR = 3, +} lr20xx_hal_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Reset the radio + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr20xx_hal_status_t lr20xx_hal_reset( const void* context ); + +/*! + * @brief Wake the radio up. + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr20xx_hal_status_t lr20xx_hal_wakeup( const void* context ); + +/*! + * @brief Radio data transfer - write + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [in] data Pointer to the buffer to be transmitted + * @param [in] data_length Buffer size to be transmitted + * + * @returns Operation status + */ +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 ); + +/*! + * @brief Radio data transfer - read + * + * @remark This is a two-step radio read operation. It consists of writing the command, releasing then re-asserting the + * NSS line, then reading a discarded dummy byte followed by data_length bytes of response data from the transceiver. + * While reading the dummy bytes and the response data, the implementation of this function must ensure that only zero + * bytes (NOP) are written to the SPI bus. + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + * + * @remark Some hardware SPI implementations write arbitrary values on the MOSI line while reading. If this is done on + * the LR20XX, non-zero values may be interpreted as commands. This driver does not exploit this functionality, and + * expects that zeros be sent on the MOSI line when this command is reading the command response data. + */ +lr20xx_hal_status_t lr20xx_hal_read( const void* context, const uint8_t* command, const uint16_t command_length, + uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Direct read from the SPI bus + * + * @remark Unlike @ref lr20xx_hal_read, this is a simple direct SPI bus SS/read/nSS operation. While reading the + * response data, the implementation of this function must ensure that only zero bytes (NOP) are written to the SPI bus. + * + * @remark Formerly, that function depended on a lr20xx_hal_write_read API function, which required bidirectional SPI + * communication. Given that all other radio functionality can be implemented with unidirectional SPI, it has been + * decided to make this HAL API change to simplify implementation requirements. + * + * @remark Only required by the @ref lr20xx_system_get_status + * + * @param [in] context Radio implementation parameters + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + */ +lr20xx_hal_status_t lr20xx_hal_direct_read( const void* context, uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Radio data transfer - read + * + * @remark This is a one-step radio read operation. It consists of writing the command then reading data_length bytes of + * response data from the transceiver. + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + * + * @remark Some hardware SPI implementations write arbitrary values on the MOSI line while reading. If this is done on + * the LR20XX, non-zero values may be interpreted as commands. This driver does not exploit this functionality, and + * expects that zeros be sent on the MOSI line when this command is reading the command response data. + */ +lr20xx_hal_status_t lr20xx_hal_direct_read_fifo( const void* context, const uint8_t* command, + const uint16_t command_length, uint8_t* data, + const uint16_t data_length ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_HAL_H diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.c b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.c new file mode 100644 index 0000000..de8128f --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.c @@ -0,0 +1,396 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * LR20xx HAL implementation for Zephyr - ZephCore + * + * Based on Semtech SWDR001 lr20xx_driver and the ZephCore lr11xx HAL. + */ + +#include "lr20xx_hal_zephyr.h" +#include "lr20xx_hal.h" + +#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. */ +#define LR20XX_BUSY_TIMEOUT_MS 3000 + +/* Static state for DIO1 interrupt handling */ +static struct gpio_callback dio1_gpio_cb; +static lr20xx_dio1_callback_t dio1_user_cb = NULL; +static void *dio1_user_data = NULL; + +/* BUSY pin interrupt — wakes wait_on_busy() via semaphore instead of polling */ +static struct gpio_callback busy_gpio_cb; +static K_SEM_DEFINE(busy_sem, 0, 1); + +static void busy_isr_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(pins); + k_sem_give(&busy_sem); +} + +/* Track the last SPI opcode for debugging BUSY stuck */ +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. + */ +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; + } + + 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 LR20XX_HAL_STATUS_OK; + } + + int ret = k_sem_take(&busy_sem, K_MSEC(LR20XX_BUSY_TIMEOUT_MS)); + gpio_pin_interrupt_configure_dt(&ctx->busy, GPIO_INT_DISABLE); + + if (ret == -EAGAIN) { + LOG_ERR("BUSY timeout! last_op=0x%04x sent_at=%lld (%lld ms ago) DIO1=%d", + last_opcode, last_cmd_time, + k_uptime_get() - last_cmd_time, + gpio_pin_get_dt(&ctx->dio1)); + return LR20XX_HAL_STATUS_ERROR; + } + + return LR20XX_HAL_STATUS_OK; +} + +/** + * @brief Check device ready, wake from sleep if needed. + */ +static lr20xx_hal_status_t check_device_ready(struct lr20xx_hal_context *ctx) +{ + if (!ctx->radio_is_sleeping) { + 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) */ + + 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) +{ + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(pins); + + if (dio1_user_cb) { + dio1_user_cb(dio1_user_data); + } +} + +/* 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) */ + 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) { + LOG_ERR("Failed to add BUSY callback: %d", ret); + 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) { + LOG_ERR("Failed to add DIO1 callback: %d", ret); + return ret; + } + + LOG_INF("LR20xx HAL initialized"); + return 0; +} + +void lr20xx_hal_set_dio1_callback(struct lr20xx_hal_context *ctx, + lr20xx_dio1_callback_t cb, void *user_data) +{ + ARG_UNUSED(ctx); + dio1_user_cb = cb; + dio1_user_data = user_data; +} + +void lr20xx_hal_enable_dio1_irq(struct lr20xx_hal_context *ctx) +{ + gpio_pin_interrupt_configure_dt(&ctx->dio1, GPIO_INT_EDGE_RISING); +} + +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) +{ + struct lr20xx_hal_context *ctx = (struct lr20xx_hal_context *)context; + int ret; + + /* Track opcode for BUSY timeout diagnostics */ + if (command_length >= 2) { + last_opcode = ((uint16_t)command[0] << 8) | command[1]; + } + last_cmd_time = k_uptime_get(); + + if (check_device_ready(ctx) != LR20XX_HAL_STATUS_OK) { + LOG_ERR("hal_write: device not ready, op=0x%04x", last_opcode); + return LR20XX_HAL_STATUS_ERROR; + } + + const struct spi_buf tx_bufs[] = { + { .buf = (uint8_t *)command, .len = command_length }, + { .buf = (uint8_t *)data, .len = data_length }, + }; + const struct spi_buf_set tx = { + .buffers = tx_bufs, + .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) { + LOG_ERR("SPI write failed: %d", ret); + return LR20XX_HAL_STATUS_ERROR; + } + + /* Check for sleep command: opcode 0x0127 (LR2021 SetSleep) */ + if (command_length >= 2 && command[0] == 0x01 && command[1] == 0x27) { + ctx->radio_is_sleeping = true; + k_busy_wait(1000); /* 1ms for sleep transition */ + return LR20XX_HAL_STATUS_OK; + } + + return wait_on_busy(ctx); +} + +lr20xx_hal_status_t lr20xx_hal_read(const void *context, const uint8_t *command, + const uint16_t command_length, + uint8_t *data, const uint16_t data_length) +{ + struct lr20xx_hal_context *ctx = (struct lr20xx_hal_context *)context; + int ret; + + /* Track opcode for BUSY timeout diagnostics */ + if (command_length >= 2) { + last_opcode = ((uint16_t)command[0] << 8) | command[1]; + } + last_cmd_time = k_uptime_get(); + + if (check_device_ready(ctx) != LR20XX_HAL_STATUS_OK) { + LOG_ERR("hal_read: device not ready, op=0x%04x", last_opcode); + 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 }; + + gpio_pin_set_dt(&ctx->nss, 1); + ret = spi_write(ctx->spi_dev, &ctx->spi_cfg, &tx); + gpio_pin_set_dt(&ctx->nss, 0); + + if (ret < 0) { + LOG_ERR("SPI write (cmd) failed: %d", ret); + return LR20XX_HAL_STATUS_ERROR; + } + + if (data_length == 0) { + 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; + } + + /* LR20xx returns 1 dummy byte + data */ + uint8_t dummy; + const struct spi_buf rx_bufs[] = { + { .buf = &dummy, .len = 1 }, + { .buf = data, .len = data_length }, + }; + const struct spi_buf_set rx = { .buffers = rx_bufs, .count = 2 }; + + gpio_pin_set_dt(&ctx->nss, 1); + ret = spi_read(ctx->spi_dev, &ctx->spi_cfg, &rx); + gpio_pin_set_dt(&ctx->nss, 0); + + if (ret < 0) { + LOG_ERR("SPI read failed: %d", ret); + return LR20XX_HAL_STATUS_ERROR; + } + + return LR20XX_HAL_STATUS_OK; +} + +lr20xx_hal_status_t lr20xx_hal_direct_read(const void *context, uint8_t *data, + const uint16_t data_length) +{ + struct lr20xx_hal_context *ctx = (struct lr20xx_hal_context *)context; + int ret; + + if (check_device_ready(ctx) != LR20XX_HAL_STATUS_OK) { + return LR20XX_HAL_STATUS_ERROR; + } + + const struct spi_buf rx_buf = { .buf = data, .len = data_length }; + const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1 }; + + gpio_pin_set_dt(&ctx->nss, 1); + ret = spi_read(ctx->spi_dev, &ctx->spi_cfg, &rx); + gpio_pin_set_dt(&ctx->nss, 0); + + if (ret < 0) { + LOG_ERR("SPI direct read failed: %d", ret); + return LR20XX_HAL_STATUS_ERROR; + } + + return LR20XX_HAL_STATUS_OK; +} + +lr20xx_hal_status_t lr20xx_hal_direct_read_fifo(const void *context, + const uint8_t *command, + const uint16_t command_length, + uint8_t *data, + const uint16_t data_length) +{ + struct lr20xx_hal_context *ctx = (struct lr20xx_hal_context *)context; + int ret; + + if (check_device_ready(ctx) != LR20XX_HAL_STATUS_OK) { + 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. */ + const struct spi_buf tx_bufs[] = { + { .buf = (uint8_t *)command, .len = command_length }, + { .buf = NULL, .len = data_length }, + }; + const struct spi_buf rx_bufs[] = { + { .buf = NULL, .len = command_length }, + { .buf = data, .len = data_length }, + }; + const struct spi_buf_set tx_set = { .buffers = tx_bufs, .count = 2 }; + const struct spi_buf_set rx_set = { .buffers = rx_bufs, .count = 2 }; + + gpio_pin_set_dt(&ctx->nss, 1); + ret = spi_transceive(ctx->spi_dev, &ctx->spi_cfg, &tx_set, &rx_set); + gpio_pin_set_dt(&ctx->nss, 0); + + if (ret < 0) { + LOG_ERR("SPI FIFO read failed: %d", ret); + return LR20XX_HAL_STATUS_ERROR; + } + + return LR20XX_HAL_STATUS_OK; +} + +lr20xx_hal_status_t lr20xx_hal_reset(const void *context) +{ + struct lr20xx_hal_context *ctx = (struct lr20xx_hal_context *)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, 0); /* Deassert reset */ + + /* Wait 300ms for internal LR20xx firmware boot */ + k_msleep(300); + + LOG_INF("LR20xx reset complete, BUSY=%d", gpio_pin_get_dt(&ctx->busy)); + + ctx->radio_is_sleeping = false; + + return wait_on_busy(ctx); +} + +lr20xx_hal_status_t lr20xx_hal_wakeup(const void *context) +{ + struct lr20xx_hal_context *ctx = (struct lr20xx_hal_context *)context; + return check_device_ready(ctx); +} diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.h b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.h new file mode 100644 index 0000000..e55df72 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_hal_zephyr.h @@ -0,0 +1,83 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * LR20xx HAL implementation for Zephyr - ZephCore + * + * Zephyr hardware context for the Semtech lr20xx_driver HAL interface. + */ + +#ifndef LR20XX_HAL_ZEPHYR_H +#define LR20XX_HAL_ZEPHYR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#include "lr20xx_hal.h" + +/** + * @brief LR20xx HAL context for Zephyr + * + * 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. + */ +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 */ + + /* State tracking */ + volatile bool radio_is_sleeping; +}; + +/** + * @brief Initialize HAL context GPIOs + * + * Must be called before any other HAL functions. + * + * @param ctx HAL context with gpio specs already filled in + * @return 0 on success, negative errno on failure + */ +int lr20xx_hal_init(struct lr20xx_hal_context *ctx); + +/** + * @brief GPIO callback type for DIO1 interrupt + */ +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 + */ +void lr20xx_hal_set_dio1_callback(struct lr20xx_hal_context *ctx, + lr20xx_dio1_callback_t cb, void *user_data); + +/** @brief Enable DIO1 edge interrupt */ +void lr20xx_hal_enable_dio1_irq(struct lr20xx_hal_context *ctx); + +/** @brief Disable DIO1 interrupt */ +void lr20xx_hal_disable_dio1_irq(struct lr20xx_hal_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* LR20XX_HAL_ZEPHYR_H */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.c b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.c new file mode 100644 index 0000000..77208b1 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.c @@ -0,0 +1,798 @@ +/*! + * @file lr20xx_radio_common.c + * + * @brief Radio common driver implementation for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr20xx_radio_common.h" +#include "lr20xx_regmem.h" +#include "lr20xx_workarounds.h" +#include "lr20xx_hal.h" +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/** + * @brief Internal RTC frequency + */ +#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 + */ +#define LR20XX_RADIO_COMMON_FRONT_END_CALIBRATION_STEP_IN_HZ ( 4000000u ) + +/** + * Register address holding the LQI value + */ +#define LR20XX_RADIO_COMMON_REGISTER_LQI ( 0xF30C38 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR20XX_RADIO_COMMON_CALIBRATE_FRONT_END_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_SET_RF_FREQ_CMD_LENGTH ( 2 + 4 ) +#define LR20XX_RADIO_COMMON_SET_RX_PATH_CMD_LENGTH ( 2 + 2 ) +#define LR20XX_RADIO_COMMON_SET_PA_CFG_CMD_LENGTH ( 2 + 3 ) +#define LR20XX_RADIO_COMMON_SET_TX_PARAMS_CMD_LENGTH ( 2 + 2 ) +#define LR20XX_RADIO_COMMON_SET_RSSI_CALIBRATION_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_SET_PKT_TYPE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_GET_PKT_TYPE_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_SET_RX_TIMEOUT_STOP_EVENT_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_RESET_RX_STATS_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_GET_RX_STATS_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_GET_RSSI_INST_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_SET_RX_CMD_LENGTH ( 2 + 3 ) +#define LR20XX_RADIO_COMMON_SET_RX_WITH_DEFAULT_TIMEOUT_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_SET_TX_CMD_LENGTH ( 2 + 3 ) +#define LR20XX_RADIO_COMMON_SET_TX_WITH_DEFAULT_TIMEOUT_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_SET_TX_TEST_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_SEL_PA_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_SET_RX_DUTY_CYCLE_CMD_LENGTH ( 2 + 7 ) +#define LR20XX_RADIO_COMMON_CONFIGURE_AUTO_TX_RX_CMD_LENGTH ( 2 + 8 ) +#define LR20XX_RADIO_COMMON_GET_RX_PACKET_LENGTH_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_SET_DEFAULT_RX_TX_TIMEOUT_CMD_LENGTH ( 2 + 6 ) +#define LR20XX_RADIO_COMMON_SET_TIMESTAMP_SOURCE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_GET_TIMESTAMP_VALUE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_SET_CCA_CMD_LENGTH ( 2 + 3 ) +#define LR20XX_RADIO_COMMON_GET_CCA_RESULT_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_COMMON_SET_AGC_GAIN_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_COMMON_SET_CAD_PARAMS_CMD_LENGTH ( 2 + 8 ) +#define LR20XX_RADIO_COMMON_SET_CAD_CMD_LENGTH ( 2 ) + +#define LR20XX_RADIO_COMMON_RSSI_CALIBRATION_SINGLE_LENGTH ( 81u ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for radio related operations + */ +enum +{ + LR20XX_RADIO_COMMON_CALIBRATE_FRONT_END_OC = 0x0123, + LR20XX_RADIO_COMMON_SET_RF_FREQ_OC = 0x0200, + LR20XX_RADIO_COMMON_SET_RX_PATH_OC = 0x0201, + LR20XX_RADIO_COMMON_SET_PA_CFG_OC = 0x0202, + LR20XX_RADIO_COMMON_SET_TX_PARAMS_OC = 0x0203, + LR20XX_RADIO_COMMON_SET_RSSI_CALIBRATION_OC = 0x0205, + LR20XX_RADIO_COMMON_SET_RX_TX_FALLBACK_MODE_OC = 0x0206, + LR20XX_RADIO_COMMON_SET_PKT_TYPE_OC = 0x0207, + LR20XX_RADIO_COMMON_GET_PKT_TYPE_OC = 0x0208, + LR20XX_RADIO_COMMON_SET_RX_TIMEOUT_STOP_EVENT_OC = 0x0209, + LR20XX_RADIO_COMMON_RESET_RX_STATS_OC = 0x020A, + LR20XX_RADIO_COMMON_GET_RSSI_INST_OC = 0x020B, + LR20XX_RADIO_COMMON_SET_RX_OC = 0x020C, + LR20XX_RADIO_COMMON_SET_TX_OC = 0x020D, + LR20XX_RADIO_COMMON_SET_TX_TEST_MODE_OC = 0x020E, + LR20XX_RADIO_COMMON_SEL_PA_OC = 0x020F, + LR20XX_RADIO_COMMON_SET_RX_DUTY_CYCLE_OC = 0x0210, + LR20XX_RADIO_COMMON_CONFIGURE_AUTO_TX_RX = 0x0211, + LR20XX_RADIO_COMMON_GET_RX_PACKET_LENGTH_OC = 0x0212, + LR20XX_RADIO_COMMON_SET_DEFAULT_RX_TX_TIMEOUT_OC = 0x0215, + LR20XX_RADIO_COMMON_SET_TIMESTAMP_SOURCE_OC = 0x0216, + LR20XX_RADIO_COMMON_GET_TIMESTAMP_VALUE_OC = 0x0217, + LR20XX_RADIO_COMMON_SET_CCA_OC = 0x0218, + LR20XX_RADIO_COMMON_GET_CCA_RESULT_OC = 0x0219, + LR20XX_RADIO_COMMON_SET_AGC_GAIN_OC = 0x021A, + LR20XX_RADIO_COMMON_SET_CAD_PARAMETERS_OC = 0x021B, + LR20XX_RADIO_COMMON_SET_CAD_OC = 0x021C, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +uint8_t* lr20xx_radio_common_serialize_rssi_calibration_table( + uint8_t* array, const lr20xx_radio_common_rssi_calibration_gain_table_t* rssi_calibration_table ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_CALIBRATE_FRONT_END_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_CALIBRATE_FRONT_END_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_CALIBRATE_FRONT_END_OC >> 0 ) + }; + + uint8_t raw_front_end_calibration_values[6] = { 0 }; + for( uint8_t rx_path_frequency_index = 0; rx_path_frequency_index < n_front_end_calibration_values; + rx_path_frequency_index++ ) + { + raw_front_end_calibration_values[rx_path_frequency_index * 2] = + ( uint8_t ) ( ( uint16_t )( front_end_calibration_values[rx_path_frequency_index] ) >> 8 ); + raw_front_end_calibration_values[rx_path_frequency_index * 2 + 1] = + ( uint8_t ) ( ( uint16_t )( front_end_calibration_values[rx_path_frequency_index] ) ); + } + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_CALIBRATE_FRONT_END_CMD_LENGTH, + raw_front_end_calibration_values, + ( uint16_t )( n_front_end_calibration_values * 2 ) ); +} + +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 ) +{ + lr20xx_radio_common_raw_front_end_calibration_value_t raw_calibration_values[3] = { 0 }; + + for( uint8_t front_end_calibration_value_index = 0; + front_end_calibration_value_index < n_front_end_calibration_structures; front_end_calibration_value_index++ ) + { + 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 + 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 ); + raw_calibration_values[front_end_calibration_value_index] = + ( uint16_t ) ( ( ( rx_path == LR20XX_RADIO_COMMON_RX_PATH_HF ) ? 0x8000u : 0x0000u ) | freq_4mhz ); + } + + return lr20xx_radio_common_calibrate_front_end( context, raw_calibration_values, + n_front_end_calibration_structures ); +} + +uint32_t lr20xx_radio_common_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ) +{ + return ( uint32_t ) ( time_in_ms * LR20XX_RTC_FREQ_IN_HZ / 1000 ); +} + +lr20xx_status_t lr20xx_radio_common_set_rf_freq( const void* context, uint32_t freq_in_hz ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RF_FREQ_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RF_FREQ_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RF_FREQ_OC >> 0 ), + ( uint8_t ) ( freq_in_hz >> 24 ), + ( uint8_t ) ( freq_in_hz >> 16 ), + ( uint8_t ) ( freq_in_hz >> 8 ), + ( uint8_t ) ( freq_in_hz >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_RF_FREQ_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RX_PATH_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_PATH_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_PATH_OC >> 0 ), + ( uint8_t ) rx_path, + ( uint8_t ) boost_mode, + }; + + const lr20xx_status_t write_status = + ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_RX_PATH_CMD_LENGTH, 0, 0 ); + + if( write_status != LR20XX_STATUS_OK ) + { + return write_status; + } + else + { + return LR20XX_WORKAROUNDS_CONDITIONAL_APPLY_AUTOMATIC_DCDC_CONFIGURE( context ); + } +} + +lr20xx_status_t lr20xx_radio_common_set_pa_cfg( const void* context, const lr20xx_radio_common_pa_cfg_t* pa_cfg ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_PA_CFG_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_PA_CFG_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_PA_CFG_OC >> 0 ), + ( uint8_t ) ( ( ( uint8_t ) pa_cfg->pa_sel << 7 ) + ( uint8_t ) pa_cfg->pa_lf_mode ), + ( uint8_t ) ( ( pa_cfg->pa_lf_duty_cycle << 4 ) + pa_cfg->pa_lf_slices ), + pa_cfg->pa_hf_duty_cycle, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_PA_CFG_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_TX_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_PARAMS_OC >> 0 ), + ( uint8_t ) power_half_dbm, + ( uint8_t ) ramp_time, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_TX_PARAMS_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RSSI_CALIBRATION_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RSSI_CALIBRATION_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RSSI_CALIBRATION_OC >> 0 ), + ( uint8_t ) ( ( ( rssi_cal_table_lf == NULL ) ? 0x00 : 0x01 ) | ( ( rssi_cal_table_hf == NULL ) ? 0x00 : 0x02 ) ) + }; + + uint8_t raw_rssi_table[LR20XX_RADIO_COMMON_RSSI_CALIBRATION_SINGLE_LENGTH * 2] = { 0 }; + uint8_t* raw_rssi_table_pointer = raw_rssi_table; + uint16_t raw_rssi_table_length = 0; + + if( rssi_cal_table_lf != NULL ) + { + raw_rssi_table_pointer = + lr20xx_radio_common_serialize_rssi_calibration_table( raw_rssi_table_pointer, rssi_cal_table_lf ); + raw_rssi_table_length = + ( uint16_t )( raw_rssi_table_length + LR20XX_RADIO_COMMON_RSSI_CALIBRATION_SINGLE_LENGTH ); + } + if( rssi_cal_table_hf != NULL ) + { + raw_rssi_table_pointer = + lr20xx_radio_common_serialize_rssi_calibration_table( raw_rssi_table_pointer, rssi_cal_table_hf ); + raw_rssi_table_length = + ( uint16_t )( raw_rssi_table_length + LR20XX_RADIO_COMMON_RSSI_CALIBRATION_SINGLE_LENGTH ); + } + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_RSSI_CALIBRATION_CMD_LENGTH, + raw_rssi_table, raw_rssi_table_length ); +} + +lr20xx_status_t lr20xx_radio_common_set_rx_tx_fallback_mode( const void* context, + const lr20xx_radio_common_fallback_modes_t fallback_mode ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_TX_FALLBACK_MODE_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_TX_FALLBACK_MODE_OC >> 0 ), + ( uint8_t ) fallback_mode, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, + LR20XX_RADIO_COMMON_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_set_pkt_type( const void* context, lr20xx_radio_common_pkt_type_t pkt_type ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_PKT_TYPE_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_PKT_TYPE_OC >> 0 ), + ( uint8_t ) pkt_type, + }; + + const lr20xx_status_t write_status = + ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_PKT_TYPE_CMD_LENGTH, 0, 0 ); + + if( write_status != LR20XX_STATUS_OK ) + { + return write_status; + } + else + { + return LR20XX_WORKAROUNDS_CONDITIONAL_APPLY_AUTOMATIC_DCDC_RESET( context ); + } +} + +lr20xx_status_t lr20xx_radio_common_get_pkt_type( const void* context, lr20xx_radio_common_pkt_type_t* pkt_type ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_GET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_PKT_TYPE_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_PKT_TYPE_OC >> 0 ), + }; + + uint8_t pkt_type_raw = 0; + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_COMMON_GET_PKT_TYPE_CMD_LENGTH, &pkt_type_raw, 1 ); + + if( status == LR20XX_STATUS_OK ) + { + *pkt_type = ( lr20xx_radio_common_pkt_type_t ) pkt_type_raw; + } + return status; +} + +lr20xx_status_t lr20xx_radio_common_set_rx_timeout_stop_event( const void* context, + const bool is_stopped_on_preamble_detection ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RX_TIMEOUT_STOP_EVENT_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_TIMEOUT_STOP_EVENT_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_TIMEOUT_STOP_EVENT_OC >> 0 ), + ( is_stopped_on_preamble_detection == true ) ? 0x01 : 0x00, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, + LR20XX_RADIO_COMMON_SET_RX_TIMEOUT_STOP_EVENT_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_reset_rx_stats( const void* context ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_RESET_RX_STATS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_RESET_RX_STATS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_RESET_RX_STATS_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_RESET_RX_STATS_CMD_LENGTH, 0, + 0 ); +} + +lr20xx_status_t lr20xx_radio_common_get_rssi_inst( const void* context, int16_t* rssi_in_dbm, uint8_t* half_dbm_count ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_GET_RSSI_INST_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_RSSI_INST_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_RSSI_INST_OC >> 0 ), + }; + uint8_t rssi_raw[2] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_COMMON_GET_RSSI_INST_CMD_LENGTH, rssi_raw, 2 ); + + if( status == LR20XX_STATUS_OK ) + { + *rssi_in_dbm = ( int16_t )( -( ( int16_t )( rssi_raw[0] ) ) ); + if( half_dbm_count != NULL ) + { + *half_dbm_count = rssi_raw[1] & 0x01; + } + } + + return status; +} + +lr20xx_status_t lr20xx_radio_common_set_rx( const void* context, const uint32_t timeout_in_ms ) +{ + return lr20xx_radio_common_set_rx_with_timeout_in_rtc_step( + context, lr20xx_radio_common_convert_time_in_ms_to_rtc_step( timeout_in_ms ) ); +} + +lr20xx_status_t lr20xx_radio_common_set_rx_with_timeout_in_rtc_step( const void* context, + const uint32_t timeout_in_rtc_step ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RX_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_OC >> 8 ), ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_OC >> 0 ), + ( uint8_t ) ( timeout_in_rtc_step >> 16 ), ( uint8_t ) ( timeout_in_rtc_step >> 8 ), + ( uint8_t ) ( timeout_in_rtc_step >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_RX_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_set_rx_with_default_timeout( const void* context ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RX_WITH_DEFAULT_TIMEOUT_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, + LR20XX_RADIO_COMMON_SET_RX_WITH_DEFAULT_TIMEOUT_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_set_tx( const void* context, const uint32_t timeout_in_ms ) +{ + return lr20xx_radio_common_set_tx_with_timeout_in_rtc_step( + context, lr20xx_radio_common_convert_time_in_ms_to_rtc_step( timeout_in_ms ) ); +} + +lr20xx_status_t lr20xx_radio_common_set_tx_with_timeout_in_rtc_step( const void* context, + const uint32_t timeout_in_rtc_step ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_TX_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_OC >> 8 ), ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_OC >> 0 ), + ( uint8_t ) ( timeout_in_rtc_step >> 16 ), ( uint8_t ) ( timeout_in_rtc_step >> 8 ), + ( uint8_t ) ( timeout_in_rtc_step >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_TX_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_set_tx_with_default_timeout( const void* context ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_TX_WITH_DEFAULT_TIMEOUT_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, + LR20XX_RADIO_COMMON_SET_TX_WITH_DEFAULT_TIMEOUT_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_set_tx_test_mode( const void* context, lr20xx_radio_common_tx_test_mode_t mode ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_TX_TEST_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_TEST_MODE_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TX_TEST_MODE_OC >> 0 ), + ( uint8_t ) mode, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_TX_TEST_MODE_CMD_LENGTH, 0, + 0 ); +} + +lr20xx_status_t lr20xx_radio_common_select_pa( const void* context, lr20xx_radio_common_pa_selection_t sel ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SEL_PA_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SEL_PA_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SEL_PA_OC >> 0 ), + ( uint8_t ) sel, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SEL_PA_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + return lr20xx_radio_common_set_rx_duty_cycle_with_timing_in_rtc_step( + context, lr20xx_radio_common_convert_time_in_ms_to_rtc_step( rx_period_in_ms ), + lr20xx_radio_common_convert_time_in_ms_to_rtc_step( sleep_period_in_ms ), mode ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_RX_DUTY_CYCLE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_DUTY_CYCLE_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_RX_DUTY_CYCLE_OC >> 0 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 16 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 8 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 0 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 16 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 8 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 0 ), + ( uint8_t ) ( mode << 4 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_RX_DUTY_CYCLE_CMD_LENGTH, 0, + 0 ); +} + +lr20xx_status_t lr20xx_radio_common_configure_auto_tx_rx( + const void* context, const lr20xx_radio_common_auto_tx_rx_configuration_t* configuration ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_CONFIGURE_AUTO_TX_RX_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_COMMON_CONFIGURE_AUTO_TX_RX >> 8 ), + ( uint8_t )( LR20XX_RADIO_COMMON_CONFIGURE_AUTO_TX_RX >> 0 ), + ( uint8_t )( ( ( uint8_t ) configuration->condition ) | + ( ( configuration->disable_on_failure == true ) ? 0x80 : 0x00 ) ), + ( uint8_t )( configuration->tx_rx_timeout_in_rtc_step >> 16 ), + ( uint8_t )( configuration->tx_rx_timeout_in_rtc_step >> 8 ), + ( uint8_t )( configuration->tx_rx_timeout_in_rtc_step >> 0 ), + ( uint8_t )( configuration->delay_in_tick >> 24 ), + ( uint8_t )( configuration->delay_in_tick >> 16 ), + ( uint8_t )( configuration->delay_in_tick >> 8 ), + ( uint8_t )( configuration->delay_in_tick >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_CONFIGURE_AUTO_TX_RX_CMD_LENGTH, + 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_get_rx_packet_length( const void* context, uint16_t* pkt_len ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_GET_RX_PACKET_LENGTH_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_RX_PACKET_LENGTH_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_RX_PACKET_LENGTH_OC >> 0 ), + }; + + uint8_t pkt_len_loc[2] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_COMMON_GET_RX_PACKET_LENGTH_CMD_LENGTH, pkt_len_loc, 2 ); + + if( status == LR20XX_STATUS_OK ) + { + *pkt_len = ( uint16_t ) ( ( ( ( uint16_t ) pkt_len_loc[0] ) << 8 ) + ( uint16_t ) pkt_len_loc[1] ); + } + + return 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 ) +{ + return lr20xx_radio_common_set_default_rx_tx_timeout_in_rtc_step( + context, lr20xx_radio_common_convert_time_in_ms_to_rtc_step( rx_timeout_in_ms ), + lr20xx_radio_common_convert_time_in_ms_to_rtc_step( tx_timeout_in_ms ) ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_DEFAULT_RX_TX_TIMEOUT_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_DEFAULT_RX_TX_TIMEOUT_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_DEFAULT_RX_TX_TIMEOUT_OC >> 0 ), + ( uint8_t ) ( rx_timeout_in_rtc_step >> 16 ), + ( uint8_t ) ( rx_timeout_in_rtc_step >> 8 ), + ( uint8_t ) ( rx_timeout_in_rtc_step >> 0 ), + ( uint8_t ) ( tx_timeout_in_rtc_step >> 16 ), + ( uint8_t ) ( tx_timeout_in_rtc_step >> 8 ), + ( uint8_t ) ( tx_timeout_in_rtc_step >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, + LR20XX_RADIO_COMMON_SET_DEFAULT_RX_TX_TIMEOUT_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_TIMESTAMP_SOURCE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TIMESTAMP_SOURCE_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_TIMESTAMP_SOURCE_OC >> 0 ), + ( uint8_t ) ( ( ( uint8_t ) cfg_slot << 4 ) + ( uint8_t ) source ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_TIMESTAMP_SOURCE_CMD_LENGTH, + 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_GET_TIMESTAMP_VALUE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_TIMESTAMP_VALUE_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_TIMESTAMP_VALUE_OC >> 0 ), + ( uint8_t ) cfg_slot, + }; + + uint8_t elapsed_time_raw[4] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_COMMON_GET_TIMESTAMP_VALUE_CMD_LENGTH, elapsed_time_raw, 4 ); + + if( status == LR20XX_STATUS_OK ) + { + *elapsed_time_in_tick = ( ( uint32_t ) elapsed_time_raw[0] << 24 ) + + ( ( uint32_t ) elapsed_time_raw[1] << 16 ) + ( ( uint32_t ) elapsed_time_raw[2] << 8 ) + + ( ( uint32_t ) elapsed_time_raw[3] << 0 ); + } + + return status; +} + +lr20xx_status_t lr20xx_radio_common_set_cca( const void* context, const uint32_t duration ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_CCA_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_CCA_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_CCA_OC >> 0 ), + ( uint8_t ) ( duration >> 16 ), + ( uint8_t ) ( duration >> 8 ), + ( uint8_t ) ( duration >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_CCA_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_get_cca_result( const void* context, lr20xx_radio_common_cca_res_t* cca_res ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_GET_CCA_RESULT_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_CCA_RESULT_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_GET_CCA_RESULT_OC >> 0 ), + }; + + uint8_t cca_res_raw[4] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_COMMON_GET_RX_PACKET_LENGTH_CMD_LENGTH, cca_res_raw, 4 ); + + if( status == LR20XX_STATUS_OK ) + { + cca_res->min = ( int16_t )( -( ( int16_t ) cca_res_raw[0] ) ); + cca_res->max = ( int16_t )( -( ( int16_t ) cca_res_raw[1] ) ); + cca_res->avg = ( int16_t )( -( ( int16_t ) cca_res_raw[2] ) ); + } + + return status; +} + +lr20xx_status_t lr20xx_radio_common_set_agc_gain( const void* context, lr20xx_radio_common_gain_step_t gain ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_AGC_GAIN_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_AGC_GAIN_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_AGC_GAIN_OC >> 0 ), + ( uint8_t ) gain, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_AGC_GAIN_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_set_cad_params( const void* context, + const lr20xx_radio_common_cad_params_t* params ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_CAD_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_CAD_PARAMETERS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_CAD_PARAMETERS_OC >> 0 ), + ( uint8_t ) ( params->timeout >> 16 ), + ( uint8_t ) ( params->timeout >> 8 ), + ( uint8_t ) ( params->timeout >> 0 ), + params->threshold, + ( uint8_t ) params->exit_mode, + ( uint8_t ) ( params->tx_rx_timeout >> 16 ), + ( uint8_t ) ( params->tx_rx_timeout >> 8 ), + ( uint8_t ) ( params->tx_rx_timeout >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_CAD_PARAMS_CMD_LENGTH, 0, + 0 ); +} + +lr20xx_status_t lr20xx_radio_common_set_cad( const void* context ) +{ + const uint8_t cbuffer[LR20XX_RADIO_COMMON_SET_CAD_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_CAD_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_COMMON_SET_CAD_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_COMMON_SET_CAD_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_common_get_lqi( const void* context, lr20xx_radio_common_lqi_value_t* lqi ) +{ + uint32_t raw_register_value = 0; + const lr20xx_status_t read_register_status = + lr20xx_regmem_read_regmem32( context, LR20XX_RADIO_COMMON_REGISTER_LQI, &raw_register_value, 1 ); + if( read_register_status == LR20XX_STATUS_OK ) + { + // The LQI is on the 8 LSBits of the register value + const uint8_t raw_lqi_value = ( uint8_t ) raw_register_value; + + // The raw LQI is given as counter of 1/4 dB. So divide by 4 to get the integer part, and the 2 LSBits give the + // number of 1/4 counts + lqi->lqi_db = ( raw_lqi_value / 4 ); + lqi->lqi_quarter_db_counter = ( raw_lqi_value & 0x03 ); + } + return read_register_status; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint8_t* lr20xx_radio_common_serialize_rssi_calibration_item( + uint8_t* array, const lr20xx_radio_common_rssi_calibration_gain_item_t* rssi_calibration_item ) +{ + array[0] = ( uint8_t ) ( rssi_calibration_item->gain_value >> 8 ); + array[1] = ( uint8_t ) rssi_calibration_item->gain_value; + array[2] = rssi_calibration_item->noise_figure; + return array + 3; +} + +uint8_t* lr20xx_radio_common_serialize_rssi_calibration_table( + uint8_t* array, const lr20xx_radio_common_rssi_calibration_gain_table_t* rssi_calibration_table ) +{ + if( rssi_calibration_table != NULL ) + { + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g1 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g2 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g3 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g4 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g5 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g6 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g7 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g8 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g9 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g10 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g11 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost0 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost1 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost2 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost3 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost4 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost5 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost6 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g12_boost7 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost0 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost1 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost2 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost3 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost4 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost5 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost6 ); + array = lr20xx_radio_common_serialize_rssi_calibration_item( array, &rssi_calibration_table->g13_boost7 ); + } + else + { + // Do nothing on purpose + } + return array; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.h new file mode 100644 index 0000000..8ba0b70 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common.h @@ -0,0 +1,655 @@ +/*! + * @file lr20xx_radio_common.h + * + * @brief Radio common driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_COMMON_H +#define LR20XX_RADIO_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr20xx_status.h" +#include "lr20xx_radio_common_types.h" +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +lr20xx_status_t lr20xx_radio_common_get_lqi( const void* context, lr20xx_radio_common_lqi_value_t* lqi ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_COMMON_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_common_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common_types.h new file mode 100644 index 0000000..4e567d9 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_common_types.h @@ -0,0 +1,404 @@ +/*! + * @file lr20xx_radio_common_types.h + * + * @brief Radio common driver types for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_COMMON_TYPES_H +#define LR20XX_RADIO_COMMON_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Rx path values + */ +typedef enum +{ + LR20XX_RADIO_COMMON_RX_PATH_LF = 0x00, //!< Low-frequency Rx path + LR20XX_RADIO_COMMON_RX_PATH_HF = 0x01, //!< High-frequency Rx path +} lr20xx_radio_common_rx_path_t; + +/** + * @brief Raw front end calibration value + * + * MSBit is the Rx path selection: + * - 0: LF path + * - 1: HF path + * + * The remaining 15 LSbits are the frequency expressed as 4MHz steps. + * + * For instance: + * - 0x80E1 means 900MHz on HF path + * - 0x00E1 means 900MHz on LF path + * + */ +typedef uint16_t lr20xx_radio_common_raw_front_end_calibration_value_t; + +/** + * @brief Helper structure for front end calibration value + * + * @see lr20xx_radio_common_raw_front_end_calibration_value_t + */ +typedef struct +{ + lr20xx_radio_common_rx_path_t rx_path; //!< The RX path to calibrate + uint32_t frequency_in_hertz; //!< The frequency to calibrate +} lr20xx_radio_common_front_end_calibration_value_t; + +/*! + * @brief Rx path boost configuration values + */ +typedef enum +{ + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_NONE = 0x00, //!< Boost deactivated + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_1 = 0x01, //!< Boost mode 1 + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_2 = 0x02, //!< Boost mode 2 + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_3 = 0x03, //!< Boost mode 3 + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_4 = 0x04, //!< Boost mode 4 + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_5 = 0x05, //!< Boost mode 5 + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_6 = 0x06, //!< Boost mode 6 + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_7 = 0x07, //!< Boost mode 7 +} lr20xx_radio_common_rx_path_boost_mode_t; + +/*! + * @brief Power Amplifier Selection values + */ +typedef enum +{ + LR20XX_RADIO_COMMON_PA_SEL_LF = 0x00, //!< Low-frequency Power Amplifier + LR20XX_RADIO_COMMON_PA_SEL_HF = 0x01, //!< High-frequency Power Amplifier +} lr20xx_radio_common_pa_selection_t; + +/*! + * @brief Power Amplifier Low-Frequency mode + */ +typedef enum lr20xx_radio_common_pa_lf_mode_e +{ + LR20XX_RADIO_COMMON_PA_LF_MODE_FSM = 0x00, //!< Full Single-ended Mode +} lr20xx_radio_common_pa_lf_mode_t; + +/*! + * @brief Ramping time for PA + * + * This parameter is the ramping time of the PA. A high value improves spectral quality. + */ +typedef enum +{ + LR20XX_RADIO_COMMON_RAMP_2_US = 0x00, //!< 2 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_4_US = 0x01, //!< 4 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_8_US = 0x02, //!< 8 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_16_US = 0x03, //!< 16 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_32_US = 0x04, //!< 32 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_48_US = 0x05, //!< 48 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_64_US = 0x06, //!< 64 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_80_US = 0x07, //!< 80 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_96_US = 0x08, //!< 96 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_112_US = 0x09, //!< 112 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_128_US = 0x0A, //!< 128 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_144_US = 0x0B, //!< 144 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_160_US = 0x0C, //!< 160 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_176_US = 0x0D, //!< 176 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_192_US = 0x0E, //!< 192 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_208_US = 0x0F, //!< 208 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_240_US = 0x10, //!< 240 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_272_US = 0x11, //!< 272 us Ramp Time + LR20XX_RADIO_COMMON_RAMP_304_US = 0x12, //!< 304 us Ramp Time +} lr20xx_radio_common_ramp_time_t; + +/*! + * @brief Chip mode after leaving transmission or reception mode + */ +typedef enum lr20xx_radio_common_fallback_modes_e +{ + LR20XX_RADIO_FALLBACK_STDBY_RC = 0x01, //!< Standby RC (Default) + LR20XX_RADIO_FALLBACK_STDBY_XOSC = 0x02, //!< Standby XOSC + LR20XX_RADIO_FALLBACK_FS = 0x03 //!< FS +} lr20xx_radio_common_fallback_modes_t; + +/*! + * @brief Packet type values + */ +typedef enum +{ + LR20XX_RADIO_COMMON_PKT_TYPE_LORA = 0x00, //!< LoRa packet engine (default) + LR20XX_RADIO_COMMON_PKT_TYPE_FSK = 0x02, //!< FSK packet engine - configuration compatible with + //!< existing transceivers (SX127x / SX126x / SX128x / LR11xx) + LR20XX_RADIO_COMMON_PKT_TYPE_BLUETOOTH_LE = 0x03, //!< Bluetooth_LE packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_RTTOF = 0x04, //!< RTToF packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_FLRC = 0x05, //!< FLRC packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_BPSK = 0x06, //!< BPSK packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_LRFHSS = 0x07, //!< LR-FHSS packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_WM_BUS = 0x08, //!< Wireless M-Bus packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_WI_SUN = 0x09, //!< Wi-SUN packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_OOK = 0x0A, //!< OOK packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_Z_WAVE = 0x0C, //!< Z-Wave packet engine + LR20XX_RADIO_COMMON_PKT_TYPE_OQPSK_15_4 = 0x0D, //!< OQPSK 15.4 packet engine +} lr20xx_radio_common_pkt_type_t; + +/*! + * @brief RX Duty Cycle Modes + */ +typedef enum +{ + LR20XX_RADIO_COMMON_RX_DUTY_CYCLE_MODE_RX = 0x00, //!< LoRa/GFSK: Uses Rx for listening to packets + LR20XX_RADIO_COMMON_RX_DUTY_CYCLE_MODE_CAD = 0x01, //!< LoRa only: Uses CAD to listen for over-the-air activity +} lr20xx_radio_common_rx_duty_cycle_mode_t; + +/*! + * @brief Timestamp configuration slot + */ +typedef enum +{ + LR20XX_RADIO_COMMON_TIMESTAMP_CFG_SLOT_0 = 0x00, //!< Timestamp source configuration slot 0 + LR20XX_RADIO_COMMON_TIMESTAMP_CFG_SLOT_1 = 0x01, //!< Timestamp source configuration slot 1 + LR20XX_RADIO_COMMON_TIMESTAMP_CFG_SLOT_2 = 0x02, //!< Timestamp source configuration slot 2 +} lr20xx_radio_common_timestamp_cfg_slot_t; + +/*! + * @brief Timestamp source + */ +typedef enum +{ + LR20XX_RADIO_COMMON_TIMESTAMP_SOURCE_NONE = 0x00, //!< Timestamp deactivated + LR20XX_RADIO_COMMON_TIMESTAMP_SOURCE_TX_DONE = 0x01, //!< Timestamp triggered on last payload bit/symbol sent + LR20XX_RADIO_COMMON_TIMESTAMP_SOURCE_RX_DONE = 0x02, //!< Timestamp triggered on last payload bit/symbol received + LR20XX_RADIO_COMMON_TIMESTAMP_SOURCE_SYNC = 0x03, //!< Timestamp triggered on last syncword bit/symbol received + LR20XX_RADIO_COMMON_TIMESTAMP_SOURCE_HEADER = + 0x04, //!< Timestamp triggered on last header bit/symbol received (LoRa only) +} lr20xx_radio_common_timestamp_source_t; + +/*! + * @brief Tx test modes + */ +typedef enum +{ + LR20XX_RADIO_COMMON_TX_TEST_MODE_NORMAL = + 0x00, //!< Equivalent to lr20xx_radio_common_set_tx_with_timeout_in_rtc_step with @p timeout_in_ms set to 0 + LR20XX_RADIO_COMMON_TX_TEST_MODE_INFINITE_PREAMBLE = + 0x01, //!< Generate an infinite preamble (not available with LR-FHSS) + LR20XX_RADIO_COMMON_TX_TEST_MODE_CONTINUOUS_WAVE = 0x02, //!< Generate continuous wave (not available with LR-FHSS) + LR20XX_RADIO_COMMON_TX_TEST_MODE_PRBS9 = + 0x03, //!< Generate a PseudoRandom Binary Sequence (not available with LoRa nor LR-FHSS) +} lr20xx_radio_common_tx_test_mode_t; + +/*! + * @brief Structure to define RSSI calibration gain item + */ +typedef struct +{ + uint16_t gain_value; //!< Gain value expressed on 10 bits fix point decimal format 8.2 + uint8_t noise_figure; //!< Noise figure value expressed on 8 bits unsigned fix point decimal format 6.2 +} lr20xx_radio_common_rssi_calibration_gain_item_t; + +/*! + * @brief Structure to define RSSI calibration gain table for one RF path + */ +typedef struct +{ + lr20xx_radio_common_rssi_calibration_gain_item_t g1; + lr20xx_radio_common_rssi_calibration_gain_item_t g2; + lr20xx_radio_common_rssi_calibration_gain_item_t g3; + lr20xx_radio_common_rssi_calibration_gain_item_t g4; + lr20xx_radio_common_rssi_calibration_gain_item_t g5; + lr20xx_radio_common_rssi_calibration_gain_item_t g6; + lr20xx_radio_common_rssi_calibration_gain_item_t g7; + lr20xx_radio_common_rssi_calibration_gain_item_t g8; + lr20xx_radio_common_rssi_calibration_gain_item_t g9; + lr20xx_radio_common_rssi_calibration_gain_item_t g10; + lr20xx_radio_common_rssi_calibration_gain_item_t g11; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost0; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost1; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost2; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost3; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost4; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost5; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost6; + lr20xx_radio_common_rssi_calibration_gain_item_t g12_boost7; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost0; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost1; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost2; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost3; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost4; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost5; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost6; + lr20xx_radio_common_rssi_calibration_gain_item_t g13_boost7; +} lr20xx_radio_common_rssi_calibration_gain_table_t; + +/*! + * @brief Gain step + */ +typedef enum lr20xx_radio_common_gain_step_e +{ + LR20XX_RADIO_COMMON_GAIN_STEP_AUTO = 0x00, //!< Enable Automatic gain control (AGC) + LR20XX_RADIO_COMMON_GAIN_STEP_G1 = 0x01, //!< Gain set to G1 + LR20XX_RADIO_COMMON_GAIN_STEP_G2 = 0x02, //!< Gain set to G2 + LR20XX_RADIO_COMMON_GAIN_STEP_G3 = 0x03, //!< Gain set to G3 + LR20XX_RADIO_COMMON_GAIN_STEP_G4 = 0x04, //!< Gain set to G4 + LR20XX_RADIO_COMMON_GAIN_STEP_G5 = 0x05, //!< Gain set to G5 + LR20XX_RADIO_COMMON_GAIN_STEP_G6 = 0x06, //!< Gain set to G6 + LR20XX_RADIO_COMMON_GAIN_STEP_G7 = 0x07, //!< Gain set to G7 + LR20XX_RADIO_COMMON_GAIN_STEP_G8 = 0x08, //!< Gain set to G8 + LR20XX_RADIO_COMMON_GAIN_STEP_G9 = 0x09, //!< Gain set to G9 + LR20XX_RADIO_COMMON_GAIN_STEP_G10 = 0x0A, //!< Gain set to G10 + LR20XX_RADIO_COMMON_GAIN_STEP_G11 = 0x0B, //!< Gain set to G11 + LR20XX_RADIO_COMMON_GAIN_STEP_G12 = 0x0C, //!< Gain set to G12 + LR20XX_RADIO_COMMON_GAIN_STEP_G13 = 0x0D, //!< Gain set to G13 +} lr20xx_radio_common_gain_step_t; + +/** + * @brief Exit mode of LoRa Channel Activity Detection (CAD) operation + */ +typedef enum +{ + LR20XX_RADIO_COMMON_CAD_EXIT_MODE_FALLBACK = 0x00, //!< The chip goes to the configured fallback mode after CAD + //!< operation, no matter what the CAD result is + LR20XX_RADIO_COMMON_CAD_EXIT_MODE_TX = + 0x01, //!< If the CAD operation does not detect an activity, the chip enters in TX mode + LR20XX_RADIO_COMMON_CAD_EXIT_MODE_RX = + 0x02, //!< If the CAD operation detects an activity, the chip enters in RX mode +} lr20xx_radio_common_cad_exit_mode_t; + +/** + * @brief Condition that triggers automatic Tx (or Rx) after Rx (or Tx) operation + * + */ +typedef enum +{ + LR20XX_RADIO_COMMON_AUTO_TX_RX_OFF = 0x00, //!< Disable Auto Tx (or Rx) after Rx (or Tx) operation + LR20XX_RADIO_COMMON_AUTO_TX_RX_ALWAYS = 0x01, //!< Always trigger Tx (or Rx) operation after Rx (or Tx) operation + LR20XX_RADIO_COMMON_AUTO_TX_RX_RX_DONE_ONLY = + 0x02, //!< Trigger Tx operation only if Rx operation terminates with CRC Ok. Similar to @ref + //!< LR20XX_RADIO_COMMON_AUTO_TX_RX_ALWAYS when used for automatic Rx after Tx situation +} lr20xx_radio_common_auto_tx_rx_conditions_t; + +/** + * @brief Configuration of automatic Tx/Rx feature + */ +typedef struct +{ + lr20xx_radio_common_auto_tx_rx_conditions_t + condition; //!< The condition for executing the automatic Tx (or Rx) operation after Rx (or Tx) + bool disable_on_failure; //!< Set to @p true to disable the automatic Tx (or Rx) on Timeout, or on invalid packet + //!< received with @p condition sets to @ref LR20XX_RADIO_COMMON_AUTO_TX_RX_RX_DONE_ONLY + uint32_t delay_in_tick; //!< The delay between the Rx (or Tx) termination, and the trig of the automatic Tx (or Rx) + //!< operation. Expressed in ticks of 32MHz clock. + uint32_t tx_rx_timeout_in_rtc_step; //!< The timeout to apply on the automatic Tx (or Rx) operation. Expressed + //!< in 32.768kHz RTC step. Possible values are the same as documented for + //!< lr20xx_radio_common_set_tx_with_timeout_in_rtc_step or + //!< lr20xx_radio_common_set_rx_with_timeout_in_rtc_step +} lr20xx_radio_common_auto_tx_rx_configuration_t; + +/*! + * @brief Configuration of Power Amplifier + */ +typedef struct lr20xx_radio_common_pa_cfg_s +{ + lr20xx_radio_common_pa_selection_t pa_sel; //!< Power Amplifier selection + lr20xx_radio_common_pa_lf_mode_t pa_lf_mode; //!< Power Amplifier Low-Frequency mode. If pa_sel is unused set to @p + //!< LR20XX_RADIO_COMMON_PA_LF_MODE_FSM + uint8_t pa_lf_duty_cycle; //!< Power Amplifier - Low-frequency duty cycle. If pa_sel is unused set to 6 + uint8_t pa_lf_slices; //!< Power Amplifier - Low-frequency number of slices. If pa_sel is unused set to 7 + uint8_t pa_hf_duty_cycle; //!< Power Amplifier - High-frequency duty cycle. If pa_sel is unused set to 16 +} lr20xx_radio_common_pa_cfg_t; + +/*! + * @brief Clear Channel Assessment results + */ +typedef struct lr20xx_radio_common_cca_res_s +{ + int16_t min; //!< CCA min - in dBm + int16_t max; //!< CCA max - in dBm + int16_t avg; //!< CCA average - in dBm +} lr20xx_radio_common_cca_res_t; + +/*! + * @brief CAD parameters + */ +typedef struct lr20xx_radio_common_cad_params_s +{ + uint32_t timeout; //!< Timeout in 32MHz step + uint8_t threshold; //!< RSSI threshold in -dBm + lr20xx_radio_common_cad_exit_mode_t exit_mode; //!< Exit mode + uint32_t tx_rx_timeout; //!< Tx / Rx timeout in 32kHz step +} lr20xx_radio_common_cad_params_t; + +/** + * @brief Link Quality Indicator (LQI) value + * + * The LQI value is a positive value that is defined as the margin between detection sensitivity threshold and detection + * peak that trigger the detection of a signal. + * + * Here it is given as an integer part (in dB), and a decimal part (in 1/4dB). The actual decimal value can then be + * obtained with: \f$LQI_{dB} = lqi\_db + 0.25 \times lqi\_quarter\_db\_counter \f$. + */ +typedef struct lr20xx_radio_common_lqi_value_s +{ + uint8_t lqi_db; //!< The integer part of LQI (in dB) + uint8_t lqi_quarter_db_counter; //!< The decimal part of LQI (in 1/4dB) +} lr20xx_radio_common_lqi_value_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_COMMON_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.c b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.c new file mode 100644 index 0000000..618f8f5 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.c @@ -0,0 +1,265 @@ +/*! + * @file lr20xx_radio_fifo.c + * + * @brief Radio FiFo driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr20xx_radio_fifo.h" +#include "lr20xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR20XX_RADIO_FIFO_READ_RX_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_FIFO_WRITE_TX_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_FIFO_CLEAR_IRQ_FLAGS_CMD_LENGTH ( 2 + 2 ) +#define LR20XX_RADIO_FIFO_CFG_IRQ_CMD_LENGTH ( 2 + 10 ) +#define LR20XX_RADIO_FIFO_GET_IRQ_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_FIFO_GET_RX_LEVEL_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_FIFO_GET_TX_LEVEL_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_FIFO_CLEAR_RX_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_FIFO_CLEAR_TX_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_FIFO_GET_AND_CLEAR_IRQ_FLAGS_CMD_LENGTH ( 2 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for register and memory related operations + */ +enum +{ + LR20XX_RADIO_FIFO_READ_RX_OC = 0x0001, + LR20XX_RADIO_FIFO_WRITE_TX_OC = 0x0002, + LR20XX_RADIO_FIFO_CLEAR_FIFO_IRQ_FLAGS_OC = 0x0114, + LR20XX_RADIO_FIFO_CFG_IRQ_OC = 0x011A, + LR20XX_RADIO_FIFO_GET_IRQ_OC = 0x011B, + LR20XX_RADIO_FIFO_GET_RX_LEVEL_OC = 0x011C, + LR20XX_RADIO_FIFO_GET_TX_LEVEL_OC = 0x011D, + LR20XX_RADIO_FIFO_CLEAR_RX_OC = 0x011E, + LR20XX_RADIO_FIFO_GET_AND_CLEAR_IRQ_FLAGS_OC = 0x012E, + LR20XX_RADIO_FIFO_CLEAR_TX_OC = 0x011F, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr20xx_status_t lr20xx_radio_fifo_read_rx( const void* context, uint8_t* buffer, const uint16_t length ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_READ_RX_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_READ_RX_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_READ_RX_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_direct_read_fifo( context, cbuffer, LR20XX_RADIO_FIFO_READ_RX_CMD_LENGTH, + buffer, length ); +} + +lr20xx_status_t lr20xx_radio_fifo_write_tx( const void* context, const uint8_t* buffer, const uint16_t length ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_WRITE_TX_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_WRITE_TX_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_WRITE_TX_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_FIFO_WRITE_TX_CMD_LENGTH, buffer, + length ); +} + +lr20xx_status_t lr20xx_radio_fifo_clear_rx( const void* context ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_CLEAR_RX_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_CLEAR_RX_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_CLEAR_RX_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_FIFO_CLEAR_RX_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_fifo_clear_tx( const void* context ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_CLEAR_TX_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_CLEAR_TX_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_CLEAR_TX_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_FIFO_CLEAR_TX_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_fifo_get_rx_level( const void* context, uint16_t* fifo_level ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_GET_RX_LEVEL_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_GET_RX_LEVEL_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_GET_RX_LEVEL_OC >> 0 ), + }; + uint8_t fifo_level_uint8[2] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_FIFO_GET_RX_LEVEL_CMD_LENGTH, fifo_level_uint8, 2 ); + + if( status == LR20XX_STATUS_OK ) + { + *fifo_level = ( uint16_t )( ( ( uint16_t ) fifo_level_uint8[0] << 8 ) + fifo_level_uint8[1] ); + } + + return status; +} + +lr20xx_status_t lr20xx_radio_fifo_get_tx_level( const void* context, uint16_t* fifo_level ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_GET_TX_LEVEL_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_GET_TX_LEVEL_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_GET_TX_LEVEL_OC >> 0 ), + }; + uint8_t fifo_level_uint8[2] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_FIFO_GET_TX_LEVEL_CMD_LENGTH, fifo_level_uint8, 2 ); + + if( status == LR20XX_STATUS_OK ) + { + *fifo_level = ( uint16_t )( ( ( uint16_t ) fifo_level_uint8[0] << 8 ) + fifo_level_uint8[1] ); + } + + return status; +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_CFG_IRQ_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_CFG_IRQ_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_CFG_IRQ_OC >> 0 ), + rx_fifo_irq_enable, + tx_fifo_irq_enable, + ( uint8_t )( rx_fifo_high_threshold >> 8 ), + ( uint8_t )( rx_fifo_high_threshold >> 0 ), + ( uint8_t )( tx_fifo_low_threshold >> 8 ), + ( uint8_t )( tx_fifo_low_threshold >> 0 ), + ( uint8_t )( rx_fifo_low_threshold >> 8 ), + ( uint8_t )( rx_fifo_low_threshold >> 0 ), + ( uint8_t )( tx_fifo_high_threshold >> 8 ), + ( uint8_t )( tx_fifo_high_threshold >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_FIFO_CFG_IRQ_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_CLEAR_IRQ_FLAGS_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_CLEAR_FIFO_IRQ_FLAGS_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_CLEAR_FIFO_IRQ_FLAGS_OC >> 0 ), + rx_fifo_flags_to_clear, + tx_fifo_flags_to_clear, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_FIFO_CLEAR_IRQ_FLAGS_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_GET_IRQ_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_GET_IRQ_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_GET_IRQ_OC >> 0 ), + }; + + uint8_t rbuffer[2] = { 0 }; + + const lr20xx_status_t status = + ( lr20xx_status_t ) lr20xx_hal_read( context, cbuffer, LR20XX_RADIO_FIFO_GET_IRQ_CMD_LENGTH, rbuffer, 2 ); + + if( status == LR20XX_STATUS_OK ) + { + *rx_fifo_flags = rbuffer[0]; + *tx_fifo_flags = rbuffer[1]; + } + + return 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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_FIFO_GET_AND_CLEAR_IRQ_FLAGS_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_FIFO_GET_AND_CLEAR_IRQ_FLAGS_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_FIFO_GET_AND_CLEAR_IRQ_FLAGS_OC >> 0 ), + }; + uint8_t flags[2] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_RADIO_FIFO_GET_AND_CLEAR_IRQ_FLAGS_CMD_LENGTH, flags, 2 ); + + if( status == LR20XX_STATUS_OK ) + { + *rx_fifo_flags = flags[0]; + *tx_fifo_flags = flags[1]; + } + + return status; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.h new file mode 100644 index 0000000..241d40b --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo.h @@ -0,0 +1,209 @@ +/*! + * @file lr20xx_radio_fifo.h + * + * @brief Radio FiFo driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_FIFO_H +#define LR20XX_RADIO_FIFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr20xx_status.h" +#include "lr20xx_radio_fifo_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_FIFO_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo_types.h new file mode 100644 index 0000000..fd70373 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fifo_types.h @@ -0,0 +1,96 @@ +/*! + * @file lr20xx_radio_fifo_types.h + * + * @brief Radio FIFO driver types for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_FIFO_TYPES_H +#define LR20XX_RADIO_FIFO_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief FIFO flags + */ +enum lr20xx_radio_fifo_flag_e +{ + LR20XX_RADIO_FIFO_FLAG_NONE = ( 0 << 0 ), + LR20XX_RADIO_FIFO_FLAG_EMPTY = ( 1 << 0 ), + LR20XX_RADIO_FIFO_FLAG_THRESHOLD_LOW = ( 1 << 1 ), + LR20XX_RADIO_FIFO_FLAG_THRESHOLD_HIGH = ( 1 << 2 ), + LR20XX_RADIO_FIFO_FLAG_FULL = ( 1 << 3 ), + LR20XX_RADIO_FIFO_FLAG_OVERFLOW = ( 1 << 4 ), + LR20XX_RADIO_FIFO_FLAG_UNDERFLOW = ( 1 << 5 ), +}; + +/** + * @brief FIFO flag type re-definition + * + * @see lr20xx_radio_fifo_flag_e + */ +typedef uint8_t lr20xx_radio_fifo_flag_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_FIFO_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc.h new file mode 100644 index 0000000..a4b01fb --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc.h @@ -0,0 +1,206 @@ +/*! + * @file lr20xx_radio_flrc.h + * + * @brief Radio FLRC driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_FLRC_H +#define LR20XX_RADIO_FLRC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr20xx_status.h" +#include "lr20xx_radio_flrc_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_FLRC_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc_types.h new file mode 100644 index 0000000..6bdfcc9 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_flrc_types.h @@ -0,0 +1,250 @@ +/*! + * @file lr20xx_radio_flrc_types.h + * + * @brief FLRC radio types driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_FLRC_TYPES_H +#define LR20XX_RADIO_FLRC_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/** + * @brief Length in bytes of the FLRC short syncword + * + */ +#define LR20XX_RADIO_FLRC_SHORT_SYNCWORD_LENGTH ( 2 ) + +/** + * @brief Length in bytes of the FLRC syncword + * + */ +#define LR20XX_RADIO_FLRC_SYNCWORD_LENGTH ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Combinations of bitrate and bandwidth for FLRC packet type + * + * @remark The bitrate is in Mb/s and the bandwidth in MHz (DSB) + */ +typedef enum lr20xx_radio_flrc_br_bw_e +{ + LR20XX_RADIO_FLRC_BR_2_600_BW_2_666 = 0x00, //!< 2.600 Mb/s, 2.666 MHz + LR20XX_RADIO_FLRC_BR_2_080_BW_2_222 = 0x01, //!< 2.080 Mb/s, 2.222 MHz + LR20XX_RADIO_FLRC_BR_1_300_BW_1_333 = 0x02, //!< 1.300 Mb/s, 1.333 MHz + LR20XX_RADIO_FLRC_BR_1_040_BW_1_333 = 0x03, //!< 1.040 Mb/s, 1.333 MHz + LR20XX_RADIO_FLRC_BR_0_650_BW_0_740 = 0x04, //!< 0.650 Mb/s, 0.740 MHz + LR20XX_RADIO_FLRC_BR_0_520_BW_0_571 = 0x05, //!< 0.520 Mb/s, 0.571 MHz + LR20XX_RADIO_FLRC_BR_0_325_BW_0_357 = 0x06, //!< 0.325 Mb/s, 0.357 MHz + LR20XX_RADIO_FLRC_BR_0_260_BW_0_307 = 0x07, //!< 0.260 Mb/s, 0.307 MHz +} lr20xx_radio_flrc_br_bw_t; + +/** + * @brief Coding rates for FLRC packet type + */ +typedef enum lr20xx_radio_flrc_cr_e +{ + LR20XX_RADIO_FLRC_CR_1_2 = 0x00, //!< Coding rate 1/2 + LR20XX_RADIO_FLRC_CR_3_4 = 0x01, //!< Coding rate 3/4 + LR20XX_RADIO_FLRC_CR_NONE = 0x02, //!< Coding rate 1 (no FEC) + LR20XX_RADIO_FLRC_CR_2_3 = 0x03, //!< Coding rate 2/3 +} lr20xx_radio_flrc_cr_t; + +/** + * @brief Modulation shaping values for FLRC packet type + */ +typedef enum lr20xx_radio_flrc_pulse_shape_e +{ + LR20XX_RADIO_FLRC_PULSE_SHAPE_OFF = 0x00, + LR20XX_RADIO_FLRC_PULSE_SHAPE_BT_05 = 0x05, + LR20XX_RADIO_FLRC_PULSE_SHAPE_BT_1 = 0x07, +} lr20xx_radio_flrc_pulse_shape_t; + +/** + * @brief Preamble lengths for FLRC packet type + */ +typedef enum lr20xx_radio_flrc_preamble_len_e +{ + LR20XX_RADIO_FLRC_PREAMBLE_LEN_04_BITS = 0x00, + LR20XX_RADIO_FLRC_PREAMBLE_LEN_08_BITS = 0x01, + LR20XX_RADIO_FLRC_PREAMBLE_LEN_12_BITS = 0x02, + LR20XX_RADIO_FLRC_PREAMBLE_LEN_16_BITS = 0x03, + LR20XX_RADIO_FLRC_PREAMBLE_LEN_20_BITS = 0x04, + LR20XX_RADIO_FLRC_PREAMBLE_LEN_24_BITS = 0x05, + LR20XX_RADIO_FLRC_PREAMBLE_LEN_28_BITS = 0x06, + LR20XX_RADIO_FLRC_PREAMBLE_LEN_32_BITS = 0x07, +} lr20xx_radio_flrc_preamble_len_t; + +/** + * @brief Combinations of SyncWord correlators activated for FLRC packet types + * + * @remark Each syncword (1,2 or 3) is configured thanks to @ref lr20xx_radio_flrc_set_syncword + */ +typedef enum lr20xx_radio_flrc_rx_match_sync_word_e +{ + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_OFF = 0x00, //!< No syncword match + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_1 = 0x01, //!< Match syncword #1 + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_2 = 0x02, //!< Match syncword #2 + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_1_OR_2 = 0x03, //!< Match syncword #1 or #2 + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_3 = 0x04, //!< Match syncword #3 + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_1_OR_3 = 0x05, //!< Match syncword #1 or #3 + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_2_OR_3 = 0x06, //!< Match syncword #2 or #3 + LR20XX_RADIO_FLRC_RX_MATCH_SYNCWORD_1_OR_2_OR_3 = 0x07, //!< Match syncword #1 or #2 or #3 +} lr20xx_radio_flrc_rx_match_sync_word_t; + +/** + * @brief Syncword lengths for FLRC packet type + */ +typedef enum lr20xx_radio_flrc_sync_word_len_e +{ + LR20XX_RADIO_FLRC_SYNCWORD_LENGTH_OFF = 0x00, + LR20XX_RADIO_FLRC_SYNCWORD_LENGTH_2_BYTES = 0x01, + LR20XX_RADIO_FLRC_SYNCWORD_LENGTH_4_BYTES = 0x02, +} lr20xx_radio_flrc_sync_word_len_t; + +/** + * @brief Configuration of syncwords for Tx operations + * + */ +typedef enum lr20xx_radio_flrc_tx_syncword_e +{ + LR20XX_RADIO_FLRC_TX_SYNCWORD_NONE = 0x00, //!< Do not use syncword for Tx operation + LR20XX_RADIO_FLRC_TX_SYNCWORD_1 = 0x01, //!< Use syncword 1 + LR20XX_RADIO_FLRC_TX_SYNCWORD_2 = 0x02, //!< Use syncword 2 + LR20XX_RADIO_FLRC_TX_SYNCWORD_3 = 0x03, //!< Use syncword 3 +} lr20xx_radio_flrc_tx_syncword_t; + +/** + * @brief Packet length mode for FLRC packet type + */ +typedef enum lr20xx_radio_flrc_pkt_len_modes_e +{ + LR20XX_RADIO_FLRC_PKT_VAR_LEN = 0x00, + LR20XX_RADIO_FLRC_PKT_FIX_LEN = 0x01, +} lr20xx_radio_flrc_pkt_len_modes_t; + +/** + * @brief CRC lengths for FLRC packet type + */ +typedef enum lr20xx_radio_flrc_crc_types_e +{ + LR20XX_RADIO_FLRC_CRC_OFF = 0x00, + LR20XX_RADIO_FLRC_CRC_2_BYTES = 0x01, + LR20XX_RADIO_FLRC_CRC_3_BYTES = 0x02, + LR20XX_RADIO_FLRC_CRC_4_BYTES = 0x03, +} lr20xx_radio_flrc_crc_types_t; + +/** + * @brief Modulation configuration for LoRa packet + * + */ +typedef struct lr20xx_radio_flrc_mod_params_s +{ + lr20xx_radio_flrc_br_bw_t br_bw; //!< Bitrate & bandwidth + lr20xx_radio_flrc_cr_t cr; //!< Coding rate + lr20xx_radio_flrc_pulse_shape_t shape; //!< Shaping +} lr20xx_radio_flrc_mod_params_t; + +/** + * @brief Packet parameters for FLRC packet type + */ +typedef struct lr20xx_radio_flrc_pkt_params_s +{ + lr20xx_radio_flrc_preamble_len_t preamble_len; //!< FLRC preamble length + lr20xx_radio_flrc_sync_word_len_t sync_word_len; //!< FLRC syncword length + lr20xx_radio_flrc_tx_syncword_t tx_syncword; //!< FLRC syncword to use for Tx operation + lr20xx_radio_flrc_rx_match_sync_word_t match_sync_word; //!< FLRC syncword matcher + lr20xx_radio_flrc_pkt_len_modes_t header_type; //!< FLRC header type + uint16_t pld_len_in_bytes; //!< FLRC payload length in byte - in [6:511] (note that for SX1280 compatibility, range + //!< is [6:127]). If a length error is detected while + //!< lr20xx_radio_flrc_pkt_params_t.header_type == LR20XX_RADIO_FLRC_PKT_VAR_LEN, the + //!< IRQ LR20XX_SYSTEM_IRQ_LEN_ERROR is raised, but the device remains in Rx mode. + lr20xx_radio_flrc_crc_types_t crc_type; //!< FLRC CRC type configuration +} lr20xx_radio_flrc_pkt_params_t; + +/** + * @brief FLRC statistics of received packets + */ +typedef struct lr20xx_radio_flrc_rx_stats_s +{ + uint16_t received_packets; //!< Number of received packets + uint16_t crc_errors; //!< Number of received packets with CRC error + uint16_t length_errors; //!< Number of received packets with length error +} lr20xx_radio_flrc_rx_stats_t; + +/** + * @brief Packet status parameters for FLRC packet types + */ +typedef struct lr20xx_radio_flrc_pkt_status_s +{ + uint16_t packet_length_bytes; //!< Length of last received packet in bytes + uint8_t syncword_index; //!< Syncword index of the last received packet - in [1, 2, 3] + int16_t rssi_avg_in_dbm; //!< RSSI in dBm - averaged over the last received packet + uint8_t rssi_avg_half_dbm_count; //!< Count of 0.5 dBm to subtract to rssi_avg_in_dbm value in dBm + int16_t rssi_sync_in_dbm; //!< RSSI in dBm - averaged over the last received packet + uint8_t rssi_sync_half_dbm_count; //!< Count of 0.5 dBm to subtract to rssi_sync_in_dbm value in dBm +} lr20xx_radio_flrc_pkt_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_FLRC_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_fsk_common_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fsk_common_types.h new file mode 100644 index 0000000..fb75b62 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_fsk_common_types.h @@ -0,0 +1,172 @@ +/*! + * @file lr20xx_radio_fsk_common_types.h + * + * @brief Radio FSK common driver types for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_FSK_COMMON_TYPES_H +#define LR20XX_RADIO_FSK_COMMON_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Reception Double Side Bandwidth (DSB) value for FSK modulation + */ +typedef enum +{ + LR20XX_RADIO_FSK_COMMON_RX_BW_3_500_HZ = 0xE7, //!< RX Bandwidth 3.5 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_4_200_HZ = 0xA7, //!< RX Bandwidth 4.2 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_4_300_HZ = 0xDF, //!< RX Bandwidth 4.3 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_4_500_HZ = 0x67, //!< RX Bandwidth 4.5 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_4_800_HZ = 0x27, //!< RX Bandwidth 4.8 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_5_200_HZ = 0x9F, //!< RX Bandwidth 5.2 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_5_600_HZ = 0x5F, //!< RX Bandwidth 5.6 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_5_800_HZ = 0xD7, //!< RX Bandwidth 5.8 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_6_000_HZ = 0x1F, //!< RX Bandwidth 6 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_6_900_HZ = 0xE6, //!< RX Bandwidth 6.9 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_7_400_HZ = 0x57, //!< RX Bandwidth 7.4 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_8_000_HZ = 0x17, //!< RX Bandwidth 8 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_8_300_HZ = 0xA6, //!< RX Bandwidth 8.3 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_8_700_HZ = 0xDE, //!< RX Bandwidth 8.7 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_8_900_HZ = 0x66, //!< RX Bandwidth 8.9 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_9_600_HZ = 0x26, //!< RX Bandwidth 9.6 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_10_000_HZ = 0x9E, //!< RX Bandwidth 10 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_11_000_HZ = 0x5E, //!< RX Bandwidth 11 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_12_000_HZ = 0x1E, //!< RX Bandwidth 12 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_13_000_HZ = 0xE5, //!< RX Bandwidth 13 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_14_000_HZ = 0x56, //!< RX Bandwidth 14 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_16_000_HZ = 0xA5, //!< RX Bandwidth 16 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_17_000_HZ = 0x65, //!< RX Bandwidth 17 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_19_000_HZ = 0x25, //!< RX Bandwidth 19 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_20_000_HZ = 0x9D, //!< RX Bandwidth 20 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_22_000_HZ = 0x5D, //!< RX Bandwidth 22 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_23_000_HZ = 0xD5, //!< RX Bandwidth 23 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_24_000_HZ = 0x1D, //!< RX Bandwidth 24 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_27_000_HZ = 0xE4, //!< RX Bandwidth 27 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_29_000_HZ = 0x55, //!< RX Bandwidth 29 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_32_000_HZ = 0x15, //!< RX Bandwidth 32 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_33_000_HZ = 0xA4, //!< RX Bandwidth 33 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_34_000_HZ = 0xDC, //!< RX Bandwidth 34 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_35_000_HZ = 0x64, //!< RX Bandwidth 35 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_38_000_HZ = 0x24, //!< RX Bandwidth 38 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_41_000_HZ = 0x9C, //!< RX Bandwidth 41 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_44_000_HZ = 0x5C, //!< RX Bandwidth 44 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_46_000_HZ = 0xD4, //!< RX Bandwidth 46 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_48_000_HZ = 0x1C, //!< RX Bandwidth 48 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_55_000_HZ = 0xE3, //!< RX Bandwidth 55 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_59_000_HZ = 0x54, //!< RX Bandwidth 59 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_64_000_HZ = 0x14, //!< RX Bandwidth 64 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_66_000_HZ = 0xA3, //!< RX Bandwidth 66 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_69_000_HZ = 0xDB, //!< RX Bandwidth 69 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_71_000_HZ = 0x63, //!< RX Bandwidth 71 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_76_000_HZ = 0x23, //!< RX Bandwidth 76 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_83_000_HZ = 0x9B, //!< RX Bandwidth 83 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_89_000_HZ = 0x5B, //!< RX Bandwidth 89 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_92_000_HZ = 0xD3, //!< RX Bandwidth 92 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_96_000_HZ = 0x1B, //!< RX Bandwidth 96 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_111_000_HZ = 0xE2, //!< RX Bandwidth 111 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_119_000_HZ = 0x53, //!< RX Bandwidth 119 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_128_000_HZ = 0x13, //!< RX Bandwidth 128 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_133_000_HZ = 0xA2, //!< RX Bandwidth 133 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_138_000_HZ = 0xDA, //!< RX Bandwidth 138 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_142_000_HZ = 0x62, //!< RX Bandwidth 142 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_153_000_HZ = 0x22, //!< RX Bandwidth 153 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_166_000_HZ = 0x9A, //!< RX Bandwidth 166 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_178_000_HZ = 0x5A, //!< RX Bandwidth 178 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_185_000_HZ = 0xD2, //!< RX Bandwidth 185 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_192_000_HZ = 0x1A, //!< RX Bandwidth 192 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_222_000_HZ = 0xE1, //!< RX Bandwidth 222 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_238_000_HZ = 0x52, //!< RX Bandwidth 238 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_256_000_HZ = 0x12, //!< RX Bandwidth 256 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_266_000_HZ = 0xA1, //!< RX Bandwidth 266 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_277_000_HZ = 0xD9, //!< RX Bandwidth 277 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_285_000_HZ = 0x61, //!< RX Bandwidth 285 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_307_000_HZ = 0x21, //!< RX Bandwidth 307 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_333_000_HZ = 0x99, //!< RX Bandwidth 333 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_357_000_HZ = 0x59, //!< RX Bandwidth 357 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_370_000_HZ = 0xD1, //!< RX Bandwidth 370 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_384_000_HZ = 0x19, //!< RX Bandwidth 384 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_444_000_HZ = 0xE0, //!< RX Bandwidth 444 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_476_000_HZ = 0x51, //!< RX Bandwidth 476 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_512_000_HZ = 0x11, //!< RX Bandwidth 512 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_533_000_HZ = 0xA0, //!< RX Bandwidth 533 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_555_000_HZ = 0xD8, //!< RX Bandwidth 555 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_571_000_HZ = 0x60, //!< RX Bandwidth 571 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_615_000_HZ = 0x20, //!< RX Bandwidth 615 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_666_000_HZ = 0x98, //!< RX Bandwidth 666 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_714_000_HZ = 0x58, //!< RX Bandwidth 714 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_740_000_HZ = 0xD0, //!< RX Bandwidth 740 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_769_000_HZ = 0x18, //!< RX Bandwidth 769 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_888_000_HZ = 0x90, //!< RX Bandwidth 888 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_1_111_000_HZ = 0xC8, //!< RX Bandwidth 1111 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_1_333_000_HZ = 0x88, //!< RX Bandwidth 1333 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_2_222_000_HZ = 0xC0, //!< RX Bandwidth 2222 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_2_666_000_HZ = 0x80, //!< RX Bandwidth 2666 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_2_857_000_HZ = 0x40, //!< RX Bandwidth 2857 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_3_076_000_HZ = 0x00, //!< RX Bandwidth 3076 kHz + LR20XX_RADIO_FSK_COMMON_RX_BW_AUTO = + 0xFF, //!< RX Bandwidth automatic choice - limited to Wi-SUN, Wireless M-BUS, Wi-SUN, and Z-Wave +} lr20xx_radio_fsk_common_bw_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_FSK_COMMON_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.c b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.c new file mode 100644 index 0000000..d9bba41 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.c @@ -0,0 +1,648 @@ +/*! + * @file lr20xx_radio_lora.c + * + * @brief Radio LoRa driver implementation for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr20xx_radio_lora.h" +#include "lr20xx_hal.h" +#include "lr20xx_workarounds.h" +#include + +/* + * ----------------------------------------------------------------------------- + * --- 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_SET_SIDE_DETECTOR_CONFIGURE_CAD_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_LORA_SET_MODULATION_PARAMS_CMD_LENGTH ( 2 + 2 ) +#define LR20XX_RADIO_LORA_SET_PACKET_PARAMS_CMD_LENGTH ( 2 + 4 ) +#define LR20XX_RADIO_LORA_SET_LORA_SEARCH_SYMBOLS_CMD_LENGTH ( 2 + 2 ) +#define LR20XX_RADIO_LORA_SET_SYNCWORD_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTORS_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_SYNCWORD_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_LORA_CONFIGURE_CAD_PARAMS_CMD_LENGTH ( 2 + 7 ) +#define LR20XX_RADIO_LORA_SET_CAD_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_LORA_GET_RX_STATISTICS_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_LORA_GET_PACKET_STATUS_CMD_LENGTH ( 2 ) +#define LR20XX_RADIO_LORA_SET_ADDRESS_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_RADIO_LORA_SET_FREQ_HOP_CMD_LENGTH ( 2 + 2 ) + +#define LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTOR_CAD_TEMP_LENGTH \ + ( 3 * LR20XX_RADIO_LORA_CAD_SIDE_DETECTOR_CONFIGURATION_LENGTH ) + +#define LR20XX_RADIO_LORA_GET_RX_STATISTICS_RBUFFER_LENGTH ( 8 ) +#define LR20XX_RADIO_LORA_GET_PACKET_STATUS_RBUFFER_LENGTH ( 6 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for radio related operations + */ +enum +{ + LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_CONFIGURE_CAD_OC = 0x021E, + LR20XX_RADIO_LORA_SET_MODULATION_PARAMS_OC = 0x0220, + LR20XX_RADIO_LORA_SET_PACKET_PARAMS_OC = 0x0221, + LR20XX_RADIO_LORA_SET_LORA_SEARCH_SYMBOLS_OC = 0x0222, + LR20XX_RADIO_LORA_SET_SYNCWORD_OC = 0x0223, + LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTORS_OC = 0x0224, + LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_SYNCWORD_OC = 0x0225, + LR20XX_RADIO_LORA_CONFIGURE_CAD_PARAMS_OC = 0x0227, + LR20XX_RADIO_LORA_SET_CAD_OC = 0x0228, + LR20XX_RADIO_LORA_GET_RX_STATISTICS_OC = 0x0229, + LR20XX_RADIO_LORA_GET_PACKET_STATUS_OC = 0x022A, + LR20XX_RADIO_LORA_SET_ADDRESS_OC = 0x022B, + LR20XX_RADIO_LORA_SET_FREQ_HOP_OC = 0x022C, +}; + +typedef enum +{ + SEARCH_SYMBOL_FORMAT_NUMBER = 0x00, + SEARCH_SYMBOL_FORMAT_MANTISSA = 0x01, +} search_symbol_format_t; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +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 + */ +static uint8_t radio_lora_side_detector_cfg_to_byte( const lr20xx_radio_lora_side_detector_cfg_t* side_detector_cfg ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_CONFIGURE_CAD_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_CONFIGURE_CAD_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_CONFIGURE_CAD_OC >> 0 ), + }; + + uint8_t side_detect_temp[LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTOR_CAD_TEMP_LENGTH] = { 0 }; + for( uint8_t index_side_detector = 0; index_side_detector < n_side_detector_cad_configurations; + index_side_detector++ ) + { + const unsigned int local_side_detect_temp_index = + index_side_detector * LR20XX_RADIO_LORA_CAD_SIDE_DETECTOR_CONFIGURATION_LENGTH; + const lr20xx_radio_lora_side_detector_cad_configuration_t local_cad_side_detector_configuration = + side_detector_cad_configurations[index_side_detector]; + side_detect_temp[local_side_detect_temp_index] = local_cad_side_detector_configuration.pnr_delta; + side_detect_temp[local_side_detect_temp_index + 1] = local_cad_side_detector_configuration.det_peak; + } + + const uint16_t n_side_detect_temp = + ( uint16_t )( n_side_detector_cad_configurations * LR20XX_RADIO_LORA_CAD_SIDE_DETECTOR_CONFIGURATION_LENGTH ); + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, + LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_CONFIGURE_CAD_CMD_LENGTH, + side_detect_temp, n_side_detect_temp ); +} + +lr20xx_status_t lr20xx_radio_lora_set_modulation_params( const void* context, + const lr20xx_radio_lora_mod_params_t* mod_params ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_MODULATION_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_MODULATION_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_MODULATION_PARAMS_OC >> 0 ), + ( uint8_t ) ( ( mod_params->sf << 4 ) + mod_params->bw ), + ( uint8_t ) ( ( mod_params->cr << 4 ) + mod_params->ppm ), + }; + + const lr20xx_status_t write_status = ( lr20xx_status_t ) lr20xx_hal_write( + context, cbuffer, LR20XX_RADIO_LORA_SET_MODULATION_PARAMS_CMD_LENGTH, 0, 0 ); + + if( write_status != LR20XX_STATUS_OK ) + { + return write_status; + } + else + { + return LR20XX_WORKAROUNDS_CONDITIONAL_APPLY_AUTOMATIC_DCDC_CONFIGURE( context ); + } +} + +lr20xx_status_t lr20xx_radio_lora_set_packet_params( const void* context, + const lr20xx_radio_lora_pkt_params_t* pkt_params ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_PACKET_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_PACKET_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_PACKET_PARAMS_OC >> 0 ), + ( uint8_t ) ( pkt_params->preamble_len_in_symb >> 8 ), + ( uint8_t ) ( pkt_params->preamble_len_in_symb >> 0 ), + pkt_params->pld_len_in_bytes, + ( uint8_t ) ( ( pkt_params->pkt_mode << 2 ) + ( pkt_params->crc << 1 ) + ( pkt_params->iq << 0 ) ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_LORA_SET_PACKET_PARAMS_CMD_LENGTH, 0, + 0 ); +} + +lr20xx_status_t lr20xx_radio_lora_configure_timeout_by_number_of_symbols( const void* context, uint16_t n_symbols ) +{ + if( n_symbols <= 255 ) + { + return abstract_search_symbols( context, ( uint8_t ) n_symbols, SEARCH_SYMBOL_FORMAT_NUMBER ); + } + else + { + uint8_t exp = 0; + uint8_t mant = 0; + + lr20xx_radio_convert_nb_symb_to_mant_exp( n_symbols, &mant, &exp ); + + return lr20xx_radio_lora_configure_timeout_by_mantissa_exponent_symbols( context, mant, exp ); + } +} + +lr20xx_status_t lr20xx_radio_lora_configure_timeout_by_mantissa_exponent_symbols( const void* context, uint8_t mantissa, + uint8_t exponent ) +{ + const uint8_t n_symbols_mantissa_format = ( uint8_t ) ( ( mantissa << 3 ) + exponent ); + return abstract_search_symbols( context, n_symbols_mantissa_format, SEARCH_SYMBOL_FORMAT_MANTISSA ); +} + +uint16_t lr20xx_radio_convert_nb_symb_to_mant_exp( const uint16_t nb_symbol, uint8_t* mant, uint8_t* exp ) +{ + uint8_t exp_loc = 0; + uint16_t mant_loc = ( uint16_t ) ( ( nb_symbol + 1 ) >> 1 ); + + while( mant_loc > 31 ) + { + mant_loc = ( uint16_t ) ( ( mant_loc + 3 ) >> 2 ); + exp_loc++; + } + + *mant = ( uint8_t ) mant_loc; + *exp = exp_loc; + + return ( uint16_t ) ( mant_loc << ( 2 * exp_loc + 1 ) ); +} + +lr20xx_status_t lr20xx_radio_lora_set_syncword( const void* context, uint8_t syncword ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_SYNCWORD_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_SYNCWORD_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_SYNCWORD_OC >> 0 ), + syncword, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_LORA_SET_SYNCWORD_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTORS_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTORS_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTORS_OC >> 0 ), + }; + + uint8_t dbuffer[3]; + + for( uint8_t i = 0; i < n_side_detector_cfgs; i++ ) + { + const lr20xx_radio_lora_side_detector_cfg_t local_side_detector = side_detector_cfgs[i]; + dbuffer[i] = radio_lora_side_detector_cfg_to_byte( &local_side_detector ); + } + + return ( lr20xx_status_t ) lr20xx_hal_write( + context, cbuffer, LR20XX_RADIO_LORA_CONFIGURE_SIDE_DETECTORS_CMD_LENGTH, dbuffer, n_side_detector_cfgs ); +} + +lr20xx_status_t lr20xx_radio_lora_set_side_detector_syncwords( const void* context, const uint8_t* syncword, + uint8_t n_syncword ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_SYNCWORD_CMD_LENGTH] = { + ( uint8_t )( LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_SYNCWORD_OC >> 8 ), + ( uint8_t )( LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_SYNCWORD_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( + context, cbuffer, LR20XX_RADIO_LORA_SET_SIDE_DETECTOR_SYNCWORD_CMD_LENGTH, syncword, n_syncword ); +} + +lr20xx_status_t lr20xx_radio_lora_configure_cad_params( const void* context, + const lr20xx_radio_lora_cad_params_t* cad_params ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_CONFIGURE_CAD_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_CONFIGURE_CAD_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_CONFIGURE_CAD_PARAMS_OC >> 0 ), + cad_params->cad_symb_nb, + ( uint8_t ) cad_params->pnr_delta, + ( uint8_t ) cad_params->cad_exit_mode, + ( uint8_t ) ( cad_params->cad_timeout_in_pll_step >> 16 ), + ( uint8_t ) ( cad_params->cad_timeout_in_pll_step >> 8 ), + ( uint8_t ) ( cad_params->cad_timeout_in_pll_step >> 0 ), + cad_params->cad_detect_peak, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_LORA_CONFIGURE_CAD_PARAMS_CMD_LENGTH, 0, + 0 ); +} + +lr20xx_status_t lr20xx_radio_lora_set_cad( const void* context ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_CAD_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_CAD_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_CAD_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_LORA_SET_CAD_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_radio_lora_get_rx_statistics( const void* context, + lr20xx_radio_lora_rx_statistics_t* statistics ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_GET_RX_STATISTICS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_GET_RX_STATISTICS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_GET_RX_STATISTICS_OC >> 0 ), + }; + + uint8_t rbuffer[LR20XX_RADIO_LORA_GET_RX_STATISTICS_RBUFFER_LENGTH] = { 0 }; + + const lr20xx_status_t status = + ( lr20xx_status_t ) lr20xx_hal_read( context, cbuffer, LR20XX_RADIO_LORA_GET_RX_STATISTICS_CMD_LENGTH, rbuffer, + LR20XX_RADIO_LORA_GET_RX_STATISTICS_RBUFFER_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + statistics->n_received_packets = read_2_bytes_msbf( rbuffer + 0 ); + statistics->n_crc_errors = read_2_bytes_msbf( rbuffer + 2 ); + statistics->n_header_errors = read_2_bytes_msbf( rbuffer + 4 ); + statistics->n_false_synchronisation = read_2_bytes_msbf( rbuffer + 6 ); + } + + return status; +} + +lr20xx_status_t lr20xx_radio_lora_get_packet_status( const void* context, + lr20xx_radio_lora_packet_status_t* pkt_status ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_GET_PACKET_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_GET_PACKET_STATUS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_GET_PACKET_STATUS_OC >> 0 ), + }; + + uint8_t rbuffer[LR20XX_RADIO_LORA_GET_PACKET_STATUS_RBUFFER_LENGTH] = { 0 }; + + const lr20xx_status_t status = + ( lr20xx_status_t ) lr20xx_hal_read( context, cbuffer, LR20XX_RADIO_LORA_GET_PACKET_STATUS_CMD_LENGTH, rbuffer, + LR20XX_RADIO_LORA_GET_PACKET_STATUS_RBUFFER_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + pkt_status->crc = ( lr20xx_radio_lora_crc_t ) ( ( rbuffer[0] >> 4 ) & 0x01 ); + pkt_status->cr = ( lr20xx_radio_lora_cr_t ) ( rbuffer[0] & 0x0F ); + pkt_status->packet_length_bytes = rbuffer[1]; + pkt_status->snr_pkt_raw = ( int8_t ) rbuffer[2]; + pkt_status->rssi_pkt_in_dbm = ( int16_t ) ( -( ( int16_t ) rbuffer[3] ) ); + pkt_status->rssi_signal_pkt_in_dbm = ( int16_t ) ( -( ( int16_t ) rbuffer[4] ) ); + pkt_status->detector = ( rbuffer[5] >> 2 ) & 0x0F; + pkt_status->rssi_pkt_half_dbm_count = ( rbuffer[5] >> 1 ) & 0x01; + pkt_status->rssi_signal_pkt_half_dbm_count = ( rbuffer[5] >> 0 ) & 0x01; + } + + return status; +} + +lr20xx_status_t lr20xx_radio_lora_set_address( const void* context, uint8_t address_offset, uint8_t address_length, + const uint8_t* address ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_ADDRESS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_ADDRESS_OC >> 8 ), ( uint8_t ) ( LR20XX_RADIO_LORA_SET_ADDRESS_OC >> 0 ), + ( uint8_t ) ( ( address_length << 4 ) + address_offset ) + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_LORA_SET_ADDRESS_CMD_LENGTH, address, + address_length ); +} + +lr20xx_status_t lr20xx_radio_lora_set_freq_hop( const void* context, const lr20xx_radio_lora_hopping_cfg_t* cfg ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_FREQ_HOP_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_FREQ_HOP_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_FREQ_HOP_OC >> 0 ), + ( uint8_t ) ( ( cfg->hop_ctrl << 6 ) + ( uint8_t ) ( cfg->hop_period >> 8 ) ), + ( uint8_t ) ( cfg->hop_period >> 0 ), + }; + + uint8_t freq_hop_table[160]; + + for( uint8_t i = 0; i < cfg->nb_freq_hop; i++ ) + { + freq_hop_table[4 * i + 0] = ( uint8_t ) ( cfg->freq_hop[i] >> 24 ); + freq_hop_table[4 * i + 1] = ( uint8_t ) ( cfg->freq_hop[i] >> 16 ); + freq_hop_table[4 * i + 2] = ( uint8_t ) ( cfg->freq_hop[i] >> 8 ); + freq_hop_table[4 * i + 3] = ( uint8_t ) ( cfg->freq_hop[i] >> 0 ); + } + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_LORA_SET_FREQ_HOP_CMD_LENGTH, + freq_hop_table, ( uint16_t )( cfg->nb_freq_hop * 4u ) ); +} + +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 ) +{ + const int32_t pld_len_in_bytes = pkt_p->pld_len_in_bytes; + const int32_t sf = mod_p->sf; + const bool pld_is_fix = pkt_p->pkt_mode == LR20XX_RADIO_LORA_PKT_IMPLICIT; + + int32_t fine_synch = ( sf <= 6 ) ? 1 : 0; + bool long_interleaving = ( mod_p->cr > 4 ); + + int32_t total_bytes_nb = pld_len_in_bytes + ( ( pkt_p->crc == LR20XX_RADIO_LORA_CRC_ENABLED ) ? 2 : 0 ); + int32_t tx_bits_symbol = sf - 2 * ( mod_p->ppm != 0 ? 1 : 0 ); + + int32_t ceil_numerator; + int32_t ceil_denominator; + + int32_t symbols_nb_data; + int32_t tx_infobits_header; + int32_t tx_infobits_payload; + + if( long_interleaving ) + { + const int32_t fec_rate_numerator = 4; + const int32_t fec_rate_denominator = ( mod_p->cr + ( mod_p->cr == 7 ? 1 : 0 ) ); + + if( pld_is_fix ) + { + int32_t tx_bits_symbol_start = sf - 2 + 2 * fine_synch; + if( 8 * total_bytes_nb * fec_rate_denominator <= 7 * fec_rate_numerator * tx_bits_symbol_start ) + { + ceil_numerator = 8 * total_bytes_nb * fec_rate_denominator; + ceil_denominator = fec_rate_numerator * tx_bits_symbol_start; + } + else + { + int32_t tx_codedbits_header = tx_bits_symbol_start * 8; + ceil_numerator = 8 * fec_rate_numerator * tx_bits_symbol + 8 * total_bytes_nb * fec_rate_denominator - + fec_rate_numerator * tx_codedbits_header; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = ( sf * 4 + fine_synch * 8 - 28 ) & ~0x07; + if( tx_infobits_header < 8 * total_bytes_nb ) + { + if( tx_infobits_header > 8 * pld_len_in_bytes ) + { + tx_infobits_header = 8 * pld_len_in_bytes; + } + } + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + if( tx_infobits_payload < 0 ) + { + tx_infobits_payload = 0; + } + + ceil_numerator = tx_infobits_payload * fec_rate_denominator + 8 * fec_rate_numerator * tx_bits_symbol; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = sf * 4 + fine_synch * 8 - 8; + + if( !pld_is_fix ) + { + tx_infobits_header -= 20; + } + + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + + if( tx_infobits_payload < 0 ) tx_infobits_payload = 0; + + ceil_numerator = tx_infobits_payload; + ceil_denominator = 4 * tx_bits_symbol; + } + + symbols_nb_data = ( ( ceil_numerator + ceil_denominator - 1 ) / ceil_denominator ); + if( !long_interleaving ) + { + symbols_nb_data = symbols_nb_data * ( mod_p->cr + 4 ) + 8; + } + const int32_t intermed = pkt_p->preamble_len_in_symb + 4 + 2 * fine_synch + symbols_nb_data; + + return ( uint32_t ) ( ( 4 * intermed + 1 ) * ( 1 << ( sf - 2 ) ) ) - 1; +} + +uint32_t lr20xx_radio_lora_get_bw_in_hz( lr20xx_radio_lora_bw_t bw ) +{ + uint32_t bw_in_hz = 0; + + switch( bw ) + { + case LR20XX_RADIO_LORA_BW_31: + bw_in_hz = 31250UL; + break; + case LR20XX_RADIO_LORA_BW_41: + bw_in_hz = 41667UL; + break; + case LR20XX_RADIO_LORA_BW_62: + bw_in_hz = 62500UL; + break; + case LR20XX_RADIO_LORA_BW_83: + bw_in_hz = 83340UL; + break; + case LR20XX_RADIO_LORA_BW_101: + bw_in_hz = 101563UL; + break; + case LR20XX_RADIO_LORA_BW_125: + bw_in_hz = 125000UL; + break; + case LR20XX_RADIO_LORA_BW_250: + bw_in_hz = 250000UL; + break; + case LR20XX_RADIO_LORA_BW_500: + bw_in_hz = 500000UL; + break; + case LR20XX_RADIO_LORA_BW_203: + bw_in_hz = 203000UL; + break; + case LR20XX_RADIO_LORA_BW_406: + bw_in_hz = 406000UL; + break; + case LR20XX_RADIO_LORA_BW_812: + bw_in_hz = 812000UL; + break; + case LR20XX_RADIO_LORA_BW_1000: + bw_in_hz = 1000000UL; + break; + } + + return bw_in_hz; +} + +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 ) +{ + 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: + case LR20XX_RADIO_LORA_BW_500: + { + ppm_offset = LR20XX_RADIO_LORA_NO_PPM; + break; + } + case LR20XX_RADIO_LORA_BW_250: + { + if( sf == LR20XX_RADIO_LORA_SF11 ) + { + ppm_offset = LR20XX_RADIO_LORA_NO_PPM; + } + else if( sf == LR20XX_RADIO_LORA_SF12 ) + { + ppm_offset = LR20XX_RADIO_LORA_PPM_1_4; + } + break; + } + case LR20XX_RADIO_LORA_BW_812: + case LR20XX_RADIO_LORA_BW_406: + case LR20XX_RADIO_LORA_BW_203: + { + ppm_offset = LR20XX_RADIO_LORA_PPM_1_4; + break; + } + default: + { + // Empty on purpose + } + } + } + return ppm_offset; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +lr20xx_status_t abstract_search_symbols( const void* context, uint8_t n_symbols, search_symbol_format_t format ) +{ + const uint8_t cbuffer[LR20XX_RADIO_LORA_SET_LORA_SEARCH_SYMBOLS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_LORA_SEARCH_SYMBOLS_OC >> 8 ), + ( uint8_t ) ( LR20XX_RADIO_LORA_SET_LORA_SEARCH_SYMBOLS_OC >> 0 ), + n_symbols, + ( uint8_t ) format, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_RADIO_LORA_SET_LORA_SEARCH_SYMBOLS_CMD_LENGTH, + 0, 0 ); +} + +uint16_t read_2_bytes_msbf( const uint8_t* buffer ) +{ + return ( uint16_t ) ( ( ( ( uint16_t ) buffer[0] ) << 8 ) + ( ( ( uint16_t ) buffer[1] ) << 0 ) ); +} + +uint8_t radio_lora_side_detector_cfg_to_byte( const lr20xx_radio_lora_side_detector_cfg_t* side_detector_cfg ) +{ + return ( uint8_t ) ( ( side_detector_cfg->sf << 4 ) + ( side_detector_cfg->ppm << 2 ) + side_detector_cfg->iq ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.h new file mode 100644 index 0000000..df2fc09 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora.h @@ -0,0 +1,483 @@ +/*! + * @file lr20xx_radio_lora.h + * + * @brief Radio LoRa driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_LORA_H +#define LR20XX_RADIO_LORA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr20xx_status.h" +#include "lr20xx_radio_lora_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +lr20xx_radio_lora_ppm_t lr20xx_radio_lora_get_recommended_ppm_offset( lr20xx_radio_lora_sf_t sf, + lr20xx_radio_lora_bw_t bw ); +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_LORA_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora_types.h new file mode 100644 index 0000000..7d55c7d --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_radio_lora_types.h @@ -0,0 +1,335 @@ +/*! + * @file lr20xx_radio_lora_types.h + * + * @brief LoRa radio types driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_RADIO_LORA_TYPES_H +#define LR20XX_RADIO_LORA_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/** + * @brief LoRa syncword value for LoRaWAN public networks + */ +#define LR20XX_RADIO_LORA_SYNCWORD_LORAWAN_PUBLIC_NETWORK ( 0x34 ) + +/** + * @brief LoRa syncword value for LoRaWAN private networks + */ +#define LR20XX_RADIO_LORA_SYNCWORD_LORAWAN_PRIVATE_NETWORK ( 0x12 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LoRa Spreading Factor + */ +typedef enum +{ + LR20XX_RADIO_LORA_SF5 = 0x05, //!< Spreading factor 5 + LR20XX_RADIO_LORA_SF6 = 0x06, //!< Spreading factor 6 + LR20XX_RADIO_LORA_SF7 = 0x07, //!< Spreading factor 7 + LR20XX_RADIO_LORA_SF8 = 0x08, //!< Spreading factor 8 + LR20XX_RADIO_LORA_SF9 = 0x09, //!< Spreading factor 9 + LR20XX_RADIO_LORA_SF10 = 0x0A, //!< Spreading factor 10 + LR20XX_RADIO_LORA_SF11 = 0x0B, //!< Spreading factor 11 + LR20XX_RADIO_LORA_SF12 = 0x0C, //!< Spreading factor 12 +} lr20xx_radio_lora_sf_t; + +/** + * @brief LoRa Bandwidth + */ +typedef enum +{ + LR20XX_RADIO_LORA_BW_31 = 0x02, //!< Bandwidth 31.25 kHz + LR20XX_RADIO_LORA_BW_41 = 0x0A, //!< Bandwidth 41.67 kHz + LR20XX_RADIO_LORA_BW_83 = 0x0B, //!< Bandwidth 83.34 kHz + LR20XX_RADIO_LORA_BW_62 = 0x03, //!< Bandwidth 62.50 kHz + LR20XX_RADIO_LORA_BW_101 = 0x0C, //!< Bandwidth 101.5625 kHz + LR20XX_RADIO_LORA_BW_125 = 0x04, //!< Bandwidth 125 kHz + LR20XX_RADIO_LORA_BW_203 = 0x0D, //!< Bandwidth 203 kHz + LR20XX_RADIO_LORA_BW_250 = 0x05, //!< Bandwidth 250 kHz + LR20XX_RADIO_LORA_BW_406 = 0x0E, //!< Bandwidth 406 kHz + LR20XX_RADIO_LORA_BW_500 = 0x06, //!< Bandwidth 500 kHz + LR20XX_RADIO_LORA_BW_812 = 0x0F, //!< Bandwidth 812 kHz + LR20XX_RADIO_LORA_BW_1000 = 0x07, //!< Bandwidth 1000 kHz +} lr20xx_radio_lora_bw_t; + +/** + * @brief LoRa Coding Rate + */ +typedef enum +{ + LR20XX_RADIO_LORA_NO_CR = 0x00, //!< No Coding Rate + LR20XX_RADIO_LORA_CR_4_5 = 0x01, //!< Short Interleaver Parity code + LR20XX_RADIO_LORA_CR_4_6 = 0x02, //!< Short Interleaver Hamming code 2/3 + LR20XX_RADIO_LORA_CR_4_7 = 0x03, //!< Short Interleaver Hamming code 7/5 + LR20XX_RADIO_LORA_CR_4_8 = 0x04, //!< Short Interleaver Hamming code 1/2 + LR20XX_RADIO_LORA_CR_LI_4_5 = 0x05, //!< Long Interleaver Parity code + LR20XX_RADIO_LORA_CR_LI_4_6 = 0x06, //!< Long Interleaver Hamming code 2/3 + LR20XX_RADIO_LORA_CR_LI_4_8 = 0x07, //!< Long Interleaver Hamming code 1/2 + LR20XX_RADIO_LORA_CR_LI_CONVOLUTIONAL_4_6 = 0x08, //!< Long Interleaver Convolutional code 2/3 + LR20XX_RADIO_LORA_CR_LI_CONVOLUTIONAL_4_8 = 0x09, //!< Long Interleaver Convolutional code 1/2 +} lr20xx_radio_lora_cr_t; + +/** + * @brief LoRa PPM Offset + */ +typedef enum +{ + LR20XX_RADIO_LORA_NO_PPM = 0x00, //!< No PPM offset: use full range of modulation + LR20XX_RADIO_LORA_PPM_1_4 = 0x01, //!< 1 bin every 4 +} lr20xx_radio_lora_ppm_t; + +/** + * @brief LoRa header packet configuration + */ +typedef enum +{ + LR20XX_RADIO_LORA_PKT_EXPLICIT = 0x00, //!< (aka. variable length packet) The packet is sent with a header + //!< containing payload length so the receiver adapts to the payload length + LR20XX_RADIO_LORA_PKT_IMPLICIT = + 0x01, //!< (aka. fixed length packet) The packet is sent without header so the receiver must be configured to + //!< receive the same payload length as the transmitted one +} lr20xx_radio_lora_pkt_mode_t; + +/** + * @brief LoRa Cyclic Redundancy Check packet configuration + */ +typedef enum +{ + LR20XX_RADIO_LORA_CRC_DISABLED = 0x00, //!< CRC is not appended to the packet sent over the air + LR20XX_RADIO_LORA_CRC_ENABLED = + 0x01, //!< CRC is appended to the packet sent over the air, and checked upon reception +} lr20xx_radio_lora_crc_t; + +/** + * @brief LoRa IQ packet configuration + */ +typedef enum +{ + LR20XX_RADIO_LORA_IQ_STANDARD = 0x00, //!< IQ standard + LR20XX_RADIO_LORA_IQ_INVERTED = 0x01, //!< IQ inverted +} lr20xx_radio_lora_iq_t; + +/** + * @brief Exit mode of LoRa Channel Activity Detection (CAD) operation + * + * Refer to @ref lr20xx_radio_common_set_rx_tx_fallback_mode for details regarding configuration of fallback mode. + * + * @see lr20xx_radio_common_set_rx_tx_fallback_mode + */ +typedef enum +{ + LR20XX_RADIO_LORA_CAD_EXIT_MODE_STANDBYRC = + 0x00, //!< The chip goes to fallback mode after CAD operation, no matter what the result of CAD is + LR20XX_RADIO_LORA_CAD_EXIT_MODE_RX = 0x01, //!< If the CAD operation detects an activity, the chip enters in RX + //!< 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 +} lr20xx_radio_lora_cad_exit_mode_t; + +/** + * @brief LoRa intra-packet frequency hopping control + */ +typedef enum +{ + LR20XX_RADIO_LORA_HOPPING_CTRL_DISABLED = 0x00, //!< LoRa intra-packet frequency hopping disabled + LR20XX_RADIO_LORA_HOPPING_CTRL_ENABLED = 0x01, //!< LoRa intra-packet frequency hopping enabled +} lr20xx_radio_lora_hopping_ctrl_t; + +/** + * @brief LoRa Channel Activity Detection (CAD) parameters + * + * Parameter @ref lr20xx_radio_lora_cad_params_s.cad_detect_peak is used to tune the sensitivity of Channel Activity + * Detection. It depends on Spreading Factor and @ref lr20xx_radio_lora_cad_params_s.cad_symb_nb. + * Increasing value of lr20xx_radio_lora_cad_params_s.cad_detect_peak decreases CAD sensitivity. + * Decreasing value of lr20xx_radio_lora_cad_params_s.cad_detect_peak increases CAD sensitivity, but increase the false + * detections. + * + * Recommended values for @ref lr20xx_radio_lora_cad_params_s.cad_detect_peak, depending on @ref + * lr20xx_radio_lora_cad_params_s.cad_symb_nb and the configured Spreading Factor are: + * + * | Spreading factor | 1 symbol | 2 symbols | 3 symbols | 4 symbols | + * | --------------------------- | -------- | --------- | --------- | --------- | + * | @ref LR20XX_RADIO_LORA_SF5 | 60 | 56 | 51 | 51 | + * | @ref LR20XX_RADIO_LORA_SF6 | 60 | 56 | 51 | 51 | + * | @ref LR20XX_RADIO_LORA_SF7 | 60 | 56 | 52 | 51 | + * | @ref LR20XX_RADIO_LORA_SF8 | 64 | 58 | 54 | 54 | + * | @ref LR20XX_RADIO_LORA_SF9 | 64 | 58 | 56 | 56 | + * | @ref LR20XX_RADIO_LORA_SF10 | 66 | 60 | 60 | 60 | + * | @ref LR20XX_RADIO_LORA_SF11 | 70 | 64 | 60 | 60 | + * | @ref LR20XX_RADIO_LORA_SF12 | 74 | 68 | 65 | 64 | + * + * The CAD can be configured in best-effort CAD operation to allow early CAD operation to stop if a clear non-detection + * is obtained. The best-effort CAD is enabled by setting @ref lr20xx_radio_lora_cad_params_s.pnr_delta to a non-zero + * value. The recommended @ref lr20xx_radio_lora_cad_params_s.pnr_delta value to use for best-effort CAD is 8, and 0 to + * disable the best-effort CAD. + * + * @ref lr20xx_radio_lora_cad_params_s.cad_timeout_in_pll_step is given in PPL step of 31.25us. + */ +typedef struct lr20xx_radio_lora_cad_params_s +{ + uint8_t cad_symb_nb; //!< Number of symbols to search for CAD operation + uint8_t pnr_delta; //!< Peak to Noise Ratio. Possible values are: + //!< - 0: then the exact number of requested symbols @p cad_symb_nb is used to determine the + //!< activity detection; + //!< - 8: best-effort CAD is activated. + lr20xx_radio_lora_cad_exit_mode_t cad_exit_mode; //!< Action taken automatically at the end of CAD operation + uint32_t cad_timeout_in_pll_step; //!< Timeout in PLL steps used while in exit mode, if applicable. Max value is + //!< 0x00FFFFFF PLL steps + uint8_t cad_detect_peak; //!< Ratio for CAD between correlator peak and average to identify a peak as a detection + //!< (default: 0x32) +} lr20xx_radio_lora_cad_params_t; + +/** + * @brief Packet parameters for LoRa packet + */ +typedef struct +{ + uint16_t preamble_len_in_symb; //!< LoRa Preamble length [symbols] + lr20xx_radio_lora_pkt_mode_t pkt_mode; //!< LoRa packet mode configuration + uint8_t pld_len_in_bytes; //!< LoRa Payload length [bytes] + lr20xx_radio_lora_crc_t crc; //!< LoRa CRC configuration + lr20xx_radio_lora_iq_t iq; //!< LoRa IQ configuration +} lr20xx_radio_lora_pkt_params_t; + +/** + * @brief Modulation configuration for LoRa packet + * + * Refer to @ref lr20xx_radio_lora_get_recommended_ppm_offset for recommended values concerning @p ppm field. + */ +typedef struct +{ + lr20xx_radio_lora_sf_t sf; //!< Spreading factor + lr20xx_radio_lora_bw_t bw; //!< Bandwidth + lr20xx_radio_lora_cr_t cr; //!< Coding rate + lr20xx_radio_lora_ppm_t ppm; //!< PPM offset +} lr20xx_radio_lora_mod_params_t; + +/** + * @brief Reception statistics for LoRa packet + */ +typedef struct lr20xx_radio_lora_rx_statistics_s +{ + uint16_t n_received_packets; //!< Number of received packets + uint16_t n_crc_errors; //!< Number of received packets with CRC error + uint16_t n_header_errors; //!< Number of received packets with header error (Rx configured in + //!< LR20XX_RADIO_LORA_PKT_EXPLICIT and header CRC check failed) + uint16_t n_false_synchronisation; //!< Number of false synchronisation (preamble detected but syncword not + //!< detected, probably preamble detected on noise) +} lr20xx_radio_lora_rx_statistics_t; + +/** + * @brief LoRa packet status fields + */ +typedef struct lr20xx_radio_lora_packet_status_s +{ + uint8_t packet_length_bytes; //!< Length of last received packet in bytes + lr20xx_radio_lora_crc_t crc; //!< CRC presence of the received packet + lr20xx_radio_lora_cr_t cr; //!< Coding rate of the received packet + uint8_t detector; //!< Identifier of detectors that received / detected the packet + int16_t rssi_pkt_in_dbm; //!< Average energy in dBm at the input of the chip over the last packet received + uint8_t rssi_pkt_half_dbm_count; //!< Count of 0.5 dBm to subtract to rssi_pkt_in_dbm value in dBm + int8_t snr_pkt_raw; //!< Estimation of the SNR on last packet received expressed in 0.25dB + int16_t + rssi_signal_pkt_in_dbm; //!< Estimation of the mean energy of the LoRa signal over the last packet received. + //!< Equivalent to rssi_pkt_in_dbm if snr_pkt_raw is positive, to rssi_pkt_in_dbm + //!< + (snr_pkt_raw/4) if snr_pkt_raw is negative + uint8_t rssi_signal_pkt_half_dbm_count; //!< Count of 0.5 dBm to subtract to rssi_signal_pkt_in_dbm value in dBm +} lr20xx_radio_lora_packet_status_t; + +/** + * @brief LoRa intra-packet hopping configuration + */ +typedef struct +{ + lr20xx_radio_lora_hopping_ctrl_t hop_ctrl; //!< LoRa intra-packet frequency hopping control + uint16_t hop_period; //!< Number of LoRa symbols between two RF frequency changes (valid values in [0:16383]) + uint32_t* freq_hop; //!< List of frequencies. It is up to the caller to ensure that the array pointed to contains + //!< at least nb_freq_hop items + uint8_t nb_freq_hop; //!< Number of frequencies in @ref freq_hop. Possible value in [0:40] included +} lr20xx_radio_lora_hopping_cfg_t; + +/** + * @brief Configuration structure of Channel Activity Detection (CAD) for side detectors + */ +typedef struct +{ + uint8_t pnr_delta; + uint8_t det_peak; +} lr20xx_radio_lora_side_detector_cad_configuration_t; + +/** + * @brief Configuration of LoRa side detector + */ +typedef struct +{ + lr20xx_radio_lora_sf_t sf; //!< Spreading factor + lr20xx_radio_lora_ppm_t ppm; //!< PPM offset + lr20xx_radio_lora_iq_t iq; //!< LoRa IQ configuration +} lr20xx_radio_lora_side_detector_cfg_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_RADIO_LORA_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_regmem.c b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.c new file mode 100644 index 0000000..88a4557 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.c @@ -0,0 +1,257 @@ +/*! + * @file lr20xx_regmem.c + * + * @brief Register/memory driver implementation for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr20xx_regmem.h" +#include "lr20xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR20XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH ( 2 + 3 ) +#define LR20XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH ( 2 + 3 + 4 + 4 ) +#define LR20XX_REGMEM_READ_REGMEM32_CMD_LENGTH ( 2 + 3 + 1 ) + +#define LR20XX_REGMEM_BUFFER_SIZE_MAX ( 256 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for register and memory related operations + */ +enum +{ + LR20XX_REGMEM_WRITE_REGMEM32_OC = 0x0104, + LR20XX_REGMEM_WRITE_REGMEM32_MASK_OC = 0x0105, + LR20XX_REGMEM_READ_REGMEM32_OC = 0x0106, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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! + */ +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! + */ +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! + */ +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! + */ +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)"! + */ +static void lr20xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr20xx_status_t lr20xx_regmem_write_regmem32( const void* context, const uint32_t address, const uint32_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR20XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH]; + uint8_t cdata[LR20XX_REGMEM_BUFFER_SIZE_MAX]; + + if( length > LR20XX_REGMEM_MAX_WRITE_READ_WORDS ) + { + return LR20XX_STATUS_ERROR; + } + + lr20xx_regmem_fill_cbuffer_cdata_opcode_address_data( cbuffer, cdata, LR20XX_REGMEM_WRITE_REGMEM32_OC, address, + buffer, length ); + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH, cdata, + ( uint16_t )( length * sizeof( uint32_t ) ) ); +} + +lr20xx_status_t lr20xx_regmem_write_regmem32_mask( const void* context, const uint32_t address, const uint32_t mask, + const uint32_t data ) +{ + uint8_t cbuffer[LR20XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH]; + + lr20xx_regmem_fill_cbuffer_opcode_address( cbuffer, LR20XX_REGMEM_WRITE_REGMEM32_MASK_OC, address ); + + cbuffer[5] = ( uint8_t ) ( mask >> 24 ); + cbuffer[6] = ( uint8_t ) ( mask >> 16 ); + cbuffer[7] = ( uint8_t ) ( mask >> 8 ); + cbuffer[8] = ( uint8_t ) ( mask >> 0 ); + + cbuffer[9] = ( uint8_t ) ( data >> 24 ); + cbuffer[10] = ( uint8_t ) ( data >> 16 ); + cbuffer[11] = ( uint8_t ) ( data >> 8 ); + cbuffer[12] = ( uint8_t ) ( data >> 0 ); + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_regmem_read_regmem32( const void* context, const uint32_t address, uint32_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR20XX_REGMEM_READ_REGMEM32_CMD_LENGTH]; + + if( length > LR20XX_REGMEM_MAX_WRITE_READ_WORDS ) + { + return LR20XX_STATUS_ERROR; + } + + lr20xx_regmem_fill_cbuffer_opcode_address_length( cbuffer, LR20XX_REGMEM_READ_REGMEM32_OC, address, length ); + + lr20xx_status_t status = + ( lr20xx_status_t ) lr20xx_hal_read( context, cbuffer, LR20XX_REGMEM_READ_REGMEM32_CMD_LENGTH, + ( uint8_t* ) buffer, ( uint16_t )( length * sizeof( uint32_t ) ) ); + + if( status == LR20XX_STATUS_OK ) + { + lr20xx_regmem_fill_out_buffer_from_raw_buffer( buffer, ( const uint8_t* ) buffer, length ); + } + + return status; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +void lr20xx_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ) +{ + cbuffer[0] = ( uint8_t ) ( opcode >> 8 ); + cbuffer[1] = ( uint8_t ) ( opcode >> 0 ); + + cbuffer[2] = ( uint8_t ) ( address >> 16 ); + cbuffer[3] = ( uint8_t ) ( address >> 8 ); + cbuffer[4] = ( uint8_t ) ( address >> 0 ); +} + +void lr20xx_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, + uint8_t length ) +{ + lr20xx_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + cbuffer[5] = length; +} + +void lr20xx_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ) +{ + for( uint16_t index = 0; index < data_length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } +} + +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 ) +{ + lr20xx_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + lr20xx_regmem_fill_cdata( cdata, data, data_length ); +} + +void lr20xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ) +{ + for( uint8_t out_index = 0; out_index < out_buffer_length; out_index++ ) + { + const uint8_t* raw_buffer_local = &raw_buffer[out_index * 4]; + + out_buffer[out_index] = ( ( uint32_t ) raw_buffer_local[0] << 24 ) + + ( ( uint32_t ) raw_buffer_local[1] << 16 ) + ( ( uint32_t ) raw_buffer_local[2] << 8 ) + + ( ( uint32_t ) raw_buffer_local[3] << 0 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_regmem.h b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.h new file mode 100644 index 0000000..188defd --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_regmem.h @@ -0,0 +1,131 @@ +/*! + * @file lr20xx_regmem.h + * + * @brief Register/memory driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_REGMEM_H +#define LR20XX_REGMEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr20xx_status.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Maximum number of words that can be written to / read from a LR20XX chip with regmem32 commands + */ +#define LR20XX_REGMEM_MAX_WRITE_READ_WORDS 32 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Write up to 32 words into register memory space of the transceiver. + * + * A word is 32-bit long. The writing operations write contiguously in register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start writing operation (only the 3 bytes LSB are relevant) + * @param [in] buffer The buffer of words to write into memory. Its size must be enough to contain length words. + * @param [in] length Number of words to write into memory + * + * @returns Operation status + * + * @see lr20xx_regmem_read_regmem32 + */ +lr20xx_status_t lr20xx_regmem_write_regmem32( const void* context, const uint32_t address, const uint32_t* buffer, + const uint8_t length ); + +/*! + * @brief Read-modify-write data at given register/memory address + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to be modified (only the 3 bytes LSB are relevant) + * @param [in] mask The mask to be applied on read data + * @param [in] data The data to be written + * + * @returns Operation status + */ +lr20xx_status_t lr20xx_regmem_write_regmem32_mask( const void* context, const uint32_t address, const uint32_t mask, + const uint32_t data ); + +/*! + * @brief Read up to 32 words into register memory space of the transceiver. + * + * A word is 32-bit long. The reading operations read contiguously from register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start reading operation (only the 3 bytes LSB are relevant) + * @param [in] length Number of words to read from memory + * @param [out] buffer Pointer to a words array to be filled with content from memory. Its size must be enough to + * contain at least length words. + * + * @returns Operation status + * + * @see lr20xx_regmem_write_regmem32 + */ +lr20xx_status_t lr20xx_regmem_read_regmem32( const void* context, const uint32_t address, uint32_t* buffer, + const uint8_t length ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_REGMEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_status.h b/zephcore/adapters/radio/lr20xx/lr20xx_status.h new file mode 100644 index 0000000..e9a9b69 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_status.h @@ -0,0 +1,88 @@ +/*! + * @file lr20xx_status.h + * + * @brief Status type definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_STATUS_H +#define LR20XX_STATUS_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/** + * @brief Helper macro that execute return call if status is not @ref LR20XX_STATUS_OK + * + */ +#define RETURN_STATUS_ON_NOT_OK( call ) \ + do \ + { \ + const lr20xx_status_t status = call; \ + if( status != LR20XX_STATUS_OK ) \ + { \ + return status; \ + } \ + } while( 0 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR20XX status + */ +typedef enum lr20xx_status_e +{ + LR20XX_STATUS_OK = 0, + LR20XX_STATUS_ERROR = 3, +} lr20xx_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LR20XX_STATUS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_system.c b/zephcore/adapters/radio/lr20xx/lr20xx_system.c new file mode 100644 index 0000000..4ea92a1 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_system.c @@ -0,0 +1,629 @@ +/*! + * @file lr20xx_system.c + * + * @brief System driver implementation for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr20xx_system.h" +#include "lr20xx_hal.h" +#include "lr20xx_workarounds.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR20XX_SYSTEM_GET_STATUS_CMD_LENGTH ( 2 ) +#define LR20XX_SYSTEM_GET_VERSION_CMD_LENGTH ( 2 ) +#define LR20XX_SYSTEM_GET_ERRORS_CMD_LENGTH ( 2 ) +#define LR20XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH ( 2 ) +#define LR20XX_SYSTEM_SET_DIO_FUNC_CMD_LENGTH ( 2 + 2 ) +#define LR20XX_SYSTEM_SET_DIO_RF_SWITCH_CFG_CMD_LENGTH ( 2 + 2 ) +#define LR20XX_SYSTEM_SET_DIO_IRQ_CFG_CMD_LENGTH ( 2 + 5 ) +#define LR20XX_SYSTEM_CLEAR_IRQ_STATUS_CMD_LENGTH ( 2 + 4 ) +#define LR20XX_SYSTEM_GET_AND_CLEAR_IRQ_STATUS_CMD_LENGTH ( 2 ) +#define LR20XX_SYSTEM_CFG_LF_CLK_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_SYSTEM_CFG_CLK_OUTPUT_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH ( 2 + 5 ) +#define LR20XX_SYSTEM_SET_REG_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_SYSTEM_CALIBRATE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_SYSTEM_GET_VBAT_CMD_LENGTH ( 3 ) +#define LR20XX_SYSTEM_GET_TEMP_CMD_LENGTH ( 3 ) +#define LR20XX_SYSTEM_GET_RANDOM_NUMBER_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_SYSTEM_SET_SLEEP_MODE_CMD_LENGTH ( 2 + 5 ) +#define LR20XX_SYSTEM_SET_STANDBY_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR20XX_SYSTEM_SET_FS_MODE_CMD_LENGTH ( 2 ) +#define LR20XX_SYSTEM_ADD_REGISTER_TO_RETENTION_MEM_CMD_LENGTH ( 2 + 4 ) +#define LR20XX_SYSTEM_CONFIGURE_XOSC_CMD_LENGTH ( 2 + 3 ) +#define LR20XX_SYSTEM_SET_EOL_CFG_CMD_LENGTH ( 2 + 1 ) +#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 ) + +static const lr20xx_system_dio_t dio_list[] = { + LR20XX_SYSTEM_DIO_5, LR20XX_SYSTEM_DIO_6, LR20XX_SYSTEM_DIO_7, LR20XX_SYSTEM_DIO_8, + LR20XX_SYSTEM_DIO_9, LR20XX_SYSTEM_DIO_10, LR20XX_SYSTEM_DIO_11, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for system related operations + */ +enum +{ + LR20XX_SYSTEM_GET_STATUS_OC = 0x0100, + LR20XX_SYSTEM_GET_VERSION_OC = 0x0101, + LR20XX_SYSTEM_GET_ERRORS_OC = 0x0110, + LR20XX_SYSTEM_CLEAR_ERRORS_OC = 0x0111, + LR20XX_SYSTEM_SET_DIO_FUNC_OC = 0x0112, + LR20XX_SYSTEM_SET_DIO_RF_SWITCH_CFG_OC = 0x0113, + LR20XX_SYSTEM_SET_DIO_IRQ_CFG_OC = 0x0115, + LR20XX_SYSTEM_CLEAR_IRQ_STATUS_OC = 0x0116, + LR20XX_SYSTEM_GET_AND_CLEAR_IRQ_STATUS_OC = 0x0117, + LR20XX_SYSTEM_CFG_LF_CLK_OC = 0x0118, + LR20XX_SYSTEM_CFG_CLK_OUTPUT_OC = 0x0119, + LR20XX_SYSTEM_SET_TCXO_MODE_OC = 0x0120, + LR20XX_SYSTEM_SET_REG_MODE_OC = 0x0121, + LR20XX_SYSTEM_CALIBRATE_OC = 0x0122, + LR20XX_SYSTEM_GET_VBAT_OC = 0x0124, + LR20XX_SYSTEM_GET_TEMP_OC = 0x0125, + LR20XX_SYSTEM_GET_RANDOM_NUMBER_OC = 0x0126, + LR20XX_SYSTEM_SET_SLEEP_MODE_OC = 0x0127, + LR20XX_SYSTEM_SET_STANDBY_MODE_OC = 0x0128, + LR20XX_SYSTEM_SET_FS_MODE_OC = 0x0129, + LR20XX_SYSTEM_ADD_REGISTER_TO_RETENTION_MEM_OC = 0x012A, + LR20XX_SYSTEM_SET_EOL_CFG_OC = 0x0130, + LR20XX_SYSTEM_CONFIGURE_XOSC_OC = 0x0131, + LR20XX_SYSTEM_SET_TEMP_COMP_CFG_OC = 0x0132, + LR20XX_SYSTEM_SET_NTC_PARAMS_OC = 0x0133, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +static void lr20xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr20xx_system_stat2_t* stat2 ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr20xx_status_t lr20xx_system_reset( const void* context ) +{ + return ( lr20xx_status_t ) lr20xx_hal_reset( context ); +} + +lr20xx_status_t lr20xx_system_wakeup( const void* context ) +{ + return ( lr20xx_status_t ) lr20xx_hal_wakeup( context ); +} + +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 ) +{ + uint8_t data[LR20XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH] = { 0 }; + const lr20xx_status_t status = + ( lr20xx_status_t ) lr20xx_hal_direct_read( context, data, LR20XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + lr20xx_system_convert_stat1_byte_to_enum( data[0], stat1 ); + lr20xx_system_convert_stat2_byte_to_enum( data[1], stat2 ); + if( irq_status != NULL ) + { + *irq_status = ( ( lr20xx_system_irq_mask_t ) data[2] << 24 ) + + ( ( lr20xx_system_irq_mask_t ) data[3] << 16 ) + + ( ( lr20xx_system_irq_mask_t ) data[4] << 8 ) + ( ( lr20xx_system_irq_mask_t ) data[5] << 0 ); + } + } + + return status; +} + +lr20xx_status_t lr20xx_system_clear_reset_status_info( const void* context ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_GET_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_GET_STATUS_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_GET_STATUS_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_GET_STATUS_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_get_version( const void* context, lr20xx_system_version_t* version ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_GET_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_GET_VERSION_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR20XX_SYSTEM_VERSION_LENGTH] = { 0x00 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_SYSTEM_GET_VERSION_CMD_LENGTH, rbuffer, LR20XX_SYSTEM_VERSION_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + version->major = rbuffer[0]; + version->minor = rbuffer[1]; + } + + return status; +} + +lr20xx_status_t lr20xx_system_get_errors( const void* context, lr20xx_system_errors_t* errors ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_GET_ERRORS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_GET_ERRORS_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_GET_ERRORS_OC >> 0 ), + }; + uint8_t rbuffer[LR20XX_SYSTEM_ERRORS_LENGTH] = { 0x00 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_SYSTEM_GET_ERRORS_CMD_LENGTH, rbuffer, LR20XX_SYSTEM_ERRORS_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + *errors = ( uint16_t ) ( ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1] ); + } + + return status; +} + +lr20xx_status_t lr20xx_system_clear_errors( const void* context ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_CLEAR_ERRORS_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_CLEAR_ERRORS_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH, 0, 0 ); +} + +uint8_t lr20xx_system_dio_get_count( void ) +{ + return sizeof( dio_list ) / sizeof( dio_list[0] ); +} + +bool lr20xx_system_dio_get_nth( uint8_t nth, lr20xx_system_dio_t* dio ) +{ + if( nth < lr20xx_system_dio_get_count( ) ) + { + *dio = dio_list[nth]; + return true; + } + else + { + return false; + } +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_DIO_FUNC_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_DIO_FUNC_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_DIO_FUNC_OC >> 0 ), + ( uint8_t ) ( dio ), + ( uint8_t ) ( ( ( uint8_t ) func << 4 ) + ( ( uint8_t ) drive << 0 ) ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_DIO_FUNC_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_DIO_RF_SWITCH_CFG_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_DIO_RF_SWITCH_CFG_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_DIO_RF_SWITCH_CFG_OC >> 0 ), + ( uint8_t ) ( dio ), + ( uint8_t ) ( rf_switch_cfg ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_DIO_RF_SWITCH_CFG_CMD_LENGTH, 0, + 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_DIO_IRQ_CFG_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_DIO_IRQ_CFG_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_DIO_IRQ_CFG_OC >> 0 ), + ( uint8_t ) ( dio ), + ( uint8_t ) ( irq_cfg >> 24 ), + ( uint8_t ) ( irq_cfg >> 16 ), + ( uint8_t ) ( irq_cfg >> 8 ), + ( uint8_t ) ( irq_cfg >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_DIO_IRQ_CFG_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_clear_irq_status( const void* context, const lr20xx_system_irq_mask_t irqs_to_clear ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_CLEAR_IRQ_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_CLEAR_IRQ_STATUS_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_CLEAR_IRQ_STATUS_OC >> 0 ), + ( uint8_t ) ( irqs_to_clear >> 24 ), + ( uint8_t ) ( irqs_to_clear >> 16 ), + ( uint8_t ) ( irqs_to_clear >> 8 ), + ( uint8_t ) ( irqs_to_clear >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_CLEAR_IRQ_STATUS_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_get_and_clear_irq_status( const void* context, lr20xx_system_irq_mask_t* irqs ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_GET_AND_CLEAR_IRQ_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_GET_AND_CLEAR_IRQ_STATUS_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_GET_AND_CLEAR_IRQ_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[LR20XX_SYSTEM_INTERRUPTS_LENGTH] = { 0x00 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_SYSTEM_GET_AND_CLEAR_IRQ_STATUS_CMD_LENGTH, rbuffer, LR20XX_SYSTEM_INTERRUPTS_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + *irqs = ( ( uint32_t ) rbuffer[0] << 24 ) + ( ( uint32_t ) rbuffer[1] << 16 ) + + ( ( uint32_t ) rbuffer[2] << 8 ) + ( ( uint32_t ) rbuffer[3] << 0 ); + } + + return status; +} + +lr20xx_status_t lr20xx_system_cfg_lfclk( const void* context, const lr20xx_system_lfclk_cfg_t lfclock_cfg ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_CFG_LF_CLK_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_CFG_LF_CLK_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_CFG_LF_CLK_OC >> 0 ), + ( uint8_t ) lfclock_cfg, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_CFG_LF_CLK_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_cfg_clk_output( const void* context, lr20xx_system_hf_clk_scaling_t hf_clk_scaling ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_CFG_CLK_OUTPUT_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_CFG_CLK_OUTPUT_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_CFG_CLK_OUTPUT_OC >> 0 ), + ( uint8_t ) hf_clk_scaling, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_CFG_CLK_OUTPUT_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_set_tcxo_mode( const void* context, const lr20xx_system_tcxo_supply_voltage_t tune, + const uint32_t start_delay_in_rtc_step ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_TCXO_MODE_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_TCXO_MODE_OC >> 0 ), + ( uint8_t ) tune, + ( uint8_t ) ( start_delay_in_rtc_step >> 24 ), + ( uint8_t ) ( start_delay_in_rtc_step >> 16 ), + ( uint8_t ) ( start_delay_in_rtc_step >> 8 ), + ( uint8_t ) ( start_delay_in_rtc_step >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_set_reg_mode( const void* context, const lr20xx_system_reg_mode_t reg_mode ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_REG_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_REG_MODE_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_REG_MODE_OC >> 0 ), + ( uint8_t ) reg_mode, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_REG_MODE_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_calibrate( const void* context, + const lr20xx_system_calibration_mask_t blocks_to_calibrate ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_CALIBRATE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_CALIBRATE_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_CALIBRATE_OC >> 0 ), + ( uint8_t ) blocks_to_calibrate, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_CALIBRATE_CMD_LENGTH, 0, 0 ); +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_GET_VBAT_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_GET_VBAT_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_GET_VBAT_OC >> 0 ), + ( uint8_t ) ( ( ( uint8_t ) format << 3 ) + ( uint8_t ) res ), + }; + uint8_t rbuffer[LR20XX_SYSTEM_MEASURE_LENGTH] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_SYSTEM_GET_VBAT_CMD_LENGTH, rbuffer, LR20XX_SYSTEM_MEASURE_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + *vbat = ( uint16_t ) ( ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1] ); + } + + return status; +} + +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 ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_GET_TEMP_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_GET_TEMP_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_GET_TEMP_OC >> 0 ), + ( uint8_t ) ( ( ( uint8_t ) src << 4 ) + ( ( uint8_t ) format << 3 ) + ( uint8_t ) res ), + }; + uint8_t rbuffer[LR20XX_SYSTEM_MEASURE_LENGTH] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_SYSTEM_GET_TEMP_CMD_LENGTH, rbuffer, LR20XX_SYSTEM_MEASURE_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + *temp = ( uint16_t ) ( ( ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1] ) >> 3 ); + } + + return status; +} + +lr20xx_status_t lr20xx_system_get_random_number( const void* context, + lr20xx_system_random_entropy_source_bitmask_t source, + uint32_t* random_number ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_GET_RANDOM_NUMBER_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_GET_RANDOM_NUMBER_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_GET_RANDOM_NUMBER_OC >> 0 ), + ( uint8_t ) source, + }; + + uint8_t buffer[LR20XX_SYSTEM_RANDOM_NUMBER_LENGTH] = { 0 }; + + const lr20xx_status_t status = ( lr20xx_status_t ) lr20xx_hal_read( + context, cbuffer, LR20XX_SYSTEM_GET_RANDOM_NUMBER_CMD_LENGTH, buffer, LR20XX_SYSTEM_RANDOM_NUMBER_LENGTH ); + + if( status == LR20XX_STATUS_OK ) + { + *random_number = ( ( uint32_t ) buffer[0] << 24 ) + ( ( uint32_t ) buffer[1] << 16 ) + + ( ( uint32_t ) buffer[2] << 8 ) + ( ( uint32_t ) buffer[3] << 0 ); + } + + return status; +} + +lr20xx_status_t lr20xx_system_set_sleep_mode( const void* context, const lr20xx_system_sleep_cfg_t* sleep_cfg, + const uint32_t sleep_time ) +{ + 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_time >> 24 ), + ( uint8_t ) ( sleep_time >> 16 ), + ( uint8_t ) ( sleep_time >> 8 ), + ( uint8_t ) ( sleep_time >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_SLEEP_MODE_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_set_standby_mode( const void* context, const lr20xx_system_standby_mode_t standby_mode ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_STANDBY_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_STANDBY_MODE_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_STANDBY_MODE_OC >> 0 ), + ( uint8_t ) standby_mode, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_STANDBY_MODE_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_set_fs_mode( const void* context ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_FS_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_FS_MODE_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_FS_MODE_OC >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_FS_MODE_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_add_register_to_retention_mem( const void* context, uint8_t slot, uint32_t address ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_ADD_REGISTER_TO_RETENTION_MEM_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_ADD_REGISTER_TO_RETENTION_MEM_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_ADD_REGISTER_TO_RETENTION_MEM_OC >> 0 ), + slot, + ( uint8_t ) ( address >> 16 ), + ( uint8_t ) ( address >> 8 ), + ( uint8_t ) ( address >> 0 ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, + LR20XX_SYSTEM_ADD_REGISTER_TO_RETENTION_MEM_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_set_lbd_cfg( const void* context, bool is_enabled, lr20xx_system_lbd_trim_t trim ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_EOL_CFG_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_EOL_CFG_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_EOL_CFG_OC >> 0 ), + ( uint8_t ) ( ( ( uint8_t ) trim << 1 ) | ( ( is_enabled == true ) ? 1 : 0 ) ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_EOL_CFG_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_configure_xosc( const void* context, uint8_t xta, uint8_t xtb, uint8_t wait_time_us ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_CONFIGURE_XOSC_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_CONFIGURE_XOSC_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_CONFIGURE_XOSC_OC >> 0 ), + xta, + xtb, + wait_time_us, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_CONFIGURE_XOSC_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_set_temp_comp_cfg( const void* context, lr20xx_system_temp_comp_mode_t mode, + bool is_ntc_en ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_TEMP_COMP_CFG_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_TEMP_COMP_CFG_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_TEMP_COMP_CFG_OC >> 0 ), + ( uint8_t ) ( ( ( ( is_ntc_en == false ) ? 0 : 1 ) << 2 ) + ( uint8_t ) mode ), + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_TEMP_COMP_CFG_CMD_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_system_set_ntc_params( const void* context, uint16_t ntc_r_ratio, uint16_t ntc_beta, + uint8_t delay ) +{ + const uint8_t cbuffer[LR20XX_SYSTEM_SET_NTC_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR20XX_SYSTEM_SET_NTC_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR20XX_SYSTEM_SET_NTC_PARAMS_OC >> 0 ), + ( uint8_t ) ( ntc_r_ratio >> 8 ), + ( uint8_t ) ( ntc_r_ratio >> 0 ), + ( uint8_t ) ( ntc_beta >> 8 ), + ( uint8_t ) ( ntc_beta >> 0 ), + delay, + }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_SYSTEM_SET_NTC_PARAMS_CMD_LENGTH, 0, 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static void lr20xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr20xx_system_stat1_t* stat1 ) +{ + if( stat1 != NULL ) + { + stat1->is_interrupt_active = ( ( stat1_byte & 0x01 ) == 1 ) ? true : false; + stat1->command_status = ( lr20xx_system_command_status_t ) ( stat1_byte >> 1 ); + } +} + +static void lr20xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr20xx_system_stat2_t* stat2 ) +{ + if( stat2 != NULL ) + { + stat2->chip_mode = ( lr20xx_system_chip_modes_t ) ( stat2_byte & 0x07 ); + stat2->reset_status = ( lr20xx_system_reset_status_t ) ( ( stat2_byte & 0xF0 ) >> 4 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_system.h b/zephcore/adapters/radio/lr20xx/lr20xx_system.h new file mode 100644 index 0000000..062ce47 --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_system.h @@ -0,0 +1,545 @@ +/*! + * @file lr20xx_system.h + * + * @brief System driver definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_SYSTEM_H +#define LR20XX_SYSTEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr20xx_system_types.h" +#include "lr20xx_status.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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. + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +lr20xx_status_t lr20xx_system_set_ntc_params( const void* context, uint16_t ntc_r_ratio, uint16_t ntc_beta, + uint8_t delay ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_SYSTEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_system_types.h b/zephcore/adapters/radio/lr20xx/lr20xx_system_types.h new file mode 100644 index 0000000..b0c7d3c --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_system_types.h @@ -0,0 +1,486 @@ +/*! + * @file lr20xx_system_types.h + * + * @brief System driver types for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_SYSTEM_TYPES_H +#define LR20XX_SYSTEM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Command status + */ +typedef enum lr20xx_system_command_status_e +{ + LR20XX_SYSTEM_CMD_STATUS_FAIL = 0x00, //!< Last command could not be executed + LR20XX_SYSTEM_CMD_STATUS_PERR = 0x01, //!< Last command could not be processed (wrong opcode, wrong argument) + LR20XX_SYSTEM_CMD_STATUS_OK = 0x02, //!< Last command processed successfully + LR20XX_SYSTEM_CMD_STATUS_DATA = 0x03, //!< Last command was a successfully processed read, and data is currently + //!< transmitted instead of IRQ status +} lr20xx_system_command_status_t; + +/** + * @brief Reset status + */ +typedef enum lr20xx_system_reset_status_e +{ + LR20XX_SYSTEM_RESET_STATUS_CLEARED = 0x00, //!< Reset status has been cleared + LR20XX_SYSTEM_RESET_STATUS_PWR_ON_BROWN_OUT = 0x01, //!< Reset triggered by power-on or brown-out + LR20XX_SYSTEM_RESET_STATUS_RESET_PIN = 0x02, //!< Reset triggered by reset pin + LR20XX_SYSTEM_RESET_STATUS_NSS_WAKEUP = 0x05, //!< Reset triggered by leaving sleep mode with NSS toggling + LR20XX_SYSTEM_RESET_STATUS_RTC_WAKEUP = 0x06, //!< Reset triggered by leaving sleep mode with RTC timeout +} lr20xx_system_reset_status_t; + +/** + * @brief Chip modes + */ +typedef enum +{ + LR20XX_SYSTEM_CHIP_MODE_SLEEP = 0x00, + LR20XX_SYSTEM_CHIP_MODE_STBY_RC = 0x01, + LR20XX_SYSTEM_CHIP_MODE_STBY_XOSC = 0x02, + LR20XX_SYSTEM_CHIP_MODE_FS = 0x03, + LR20XX_SYSTEM_CHIP_MODE_RX = 0x04, + LR20XX_SYSTEM_CHIP_MODE_TX = 0x05, +} lr20xx_system_chip_modes_t; + +/** + * @brief Status register 1 structure definition + */ +typedef struct lr20xx_system_stat1_s +{ + lr20xx_system_command_status_t command_status; //!< Command status + bool is_interrupt_active; //!< Indication of a still active interrupt +} lr20xx_system_stat1_t; + +/** + * @brief Status register 2 structure definition + */ +typedef struct lr20xx_system_stat2_s +{ + lr20xx_system_reset_status_t reset_status; //!< Source of the last reset + lr20xx_system_chip_modes_t chip_mode; //!< Current chip mode +} lr20xx_system_stat2_t; + +/** + * @brief Version structure definition + */ +typedef struct lr20xx_system_version_s +{ + uint8_t major; //!< Version major field + uint8_t minor; //!< Version minor field +} lr20xx_system_version_t; + +/** + * @brief Version structure definition + */ +typedef struct lr20xx_system_sleep_cfg_s +{ + bool is_clk_32k_enabled; //!< 32kHz clock state when entering sleep mode + bool is_ram_retention_enabled; //!< RAM retention state when entering sleep mode +} lr20xx_system_sleep_cfg_t; + +/** + * @brief Error flags + */ +enum lr20xx_system_errors_e +{ + LR20XX_SYSTEM_ERRORS_HF_XOSC_START_MASK = ( 1 << 0 ), //!< HF XOSC did not start correctly + LR20XX_SYSTEM_ERRORS_LF_XOSC_START_MASK = ( 1 << 1 ), //!< LF XOSC did not start correctly + LR20XX_SYSTEM_ERRORS_PLL_LOCK_MASK = ( 1 << 2 ), //!< PLL did not lock + LR20XX_SYSTEM_ERRORS_LF_RC_CALIB_MASK = ( 1 << 3 ), //!< Calibration of the LF RC clock failed + LR20XX_SYSTEM_ERRORS_HF_RC_CALIB_MASK = ( 1 << 4 ), //!< Calibration of the HF RC clock failed + LR20XX_SYSTEM_ERRORS_PLL_CALIB_MASK = ( 1 << 5 ), //!< Calibration of the min / max RF frequencies failed + LR20XX_SYSTEM_ERRORS_AAF_CALIB_MASK = ( 1 << 6 ), //!< Calibration of the anti-aliasing filter (AAF) failed + LR20XX_SYSTEM_ERRORS_IMG_CALIB_MASK = ( 1 << 7 ), //!< Calibration of the image rejection failed + LR20XX_SYSTEM_ERRORS_CHIP_BUSY_MASK = + ( 1 << 8 ), //!< DIO Tx or Rx trigger cannot be executed because chip was changing mode + LR20XX_SYSTEM_ERRORS_RXFREQ_NO_FRONT_END_CALIB_MASK = + ( 1 << 9 ), //!< Front end calibration not available (image rejection, Poly-Phase filter, ADC offset) for the + //!< configured RF frequency + LR20XX_SYSTEM_ERRORS_MEAS_UNIT_ADC_CALIB_MASK = ( 1 << 10 ), //!< Error during calibration of the measure unit ADC + LR20XX_SYSTEM_ERRORS_PA_OFFSET_CALIB_MASK = + ( 1 << 11 ), //!< Error during calibration of the Power Amplifier offset + LR20XX_SYSTEM_ERRORS_PPF_CALIB_MASK = ( 1 << 12 ), //!< Error during calibration of the Poly-Phase Filter (PPF) + LR20XX_SYSTEM_ERRORS_SRC_CALIB_MASK = + ( 1 << 13 ), //!< Error during calibration of the Self Reception Cancellation (SRC) + LR20XX_SYSTEM_ERRORS_SRC_SATURATION_CALIB_MASK = + ( 1 << 14 ), //!< RSSI saturation detected during SRC calibration. It may comes from an interferer. + LR20XX_SYSTEM_ERRORS_SRC_TOLERANCE_CALIB_MASK = ( 1 << 15 ), //!< SRC calibration values are out of tolerance +}; + +/** + * @brief Interrupt flags + */ +enum lr20xx_system_irq_e +{ + LR20XX_SYSTEM_IRQ_NONE = ( 0 << 0 ), //!< No interrupt + LR20XX_SYSTEM_IRQ_FIFO_RX = ( int ) ( 1u << 0 ), //!< RX FIFO threshold reached + LR20XX_SYSTEM_IRQ_FIFO_TX = ( int ) ( 1u << 1 ), //!< TX FIFO threshold reached + LR20XX_SYSTEM_IRQ_RTTOF_RESPONDER_REQUEST_VALID = + ( int ) ( 1u << 2 ), //!< Responder received a valid RTToF request + LR20XX_SYSTEM_IRQ_TX_TIMESTAMP = ( int ) ( 1u << 3 ), //!< Last bit sent timestamp + LR20XX_SYSTEM_IRQ_RX_TIMESTAMP = ( int ) ( 1u << 4 ), //!< Last bit received timestamp + LR20XX_SYSTEM_IRQ_PREAMBLE_DETECTED = ( int ) ( 1u << 5 ), //!< Preamble detected + LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID = + ( int ) ( 1u << 6 ), //!< Valid LoRa header received / Sync word received + LR20XX_SYSTEM_IRQ_CAD_DETECTED = ( int ) ( 1u << 7 ), //!< Activity detected during CAD operation + LR20XX_SYSTEM_IRQ_LORA_RX_HEADER_TIMESTAMP = + ( int ) ( 1u << 8 ), //!< Last bit of LoRa header received in explicit mode, + //!< asserted after 8 symbols of payload in implicit mode + LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR = ( int ) ( 1u << 9 ), //!< Erroneous LoRa header received + LR20XX_SYSTEM_IRQ_LOW_BATTERY = ( int ) ( 1u << 10 ), //!< Power supply level dropped below the threshold + LR20XX_SYSTEM_IRQ_PA_OVP_OCP = ( int ) ( 1u << 11 ), //!< Power amplifier over-current protection has triggered + LR20XX_SYSTEM_IRQ_ERROR = + ( int ) ( 1u << 16 ), //!< Error other than a command error occurred - call lr20xx_system_get_errors + LR20XX_SYSTEM_IRQ_CMD_ERROR = ( int ) ( 1u << 17 ), //!< Host command fail/error occurred + LR20XX_SYSTEM_IRQ_RX_DONE = ( int ) ( 1u << 18 ), //!< Packet received + LR20XX_SYSTEM_IRQ_TX_DONE = ( int ) ( 1u << 19 ), //!< Packet sent + LR20XX_SYSTEM_IRQ_CAD_DONE = ( int ) ( 1u << 20 ), //!< CAD operation done + LR20XX_SYSTEM_IRQ_TIMEOUT = ( int ) ( 1u << 21 ), //!< Timeout occurred during Rx or Tx operation + LR20XX_SYSTEM_IRQ_CRC_ERROR = ( int ) ( 1u << 22 ), //!< Packet received with wrong CRC + LR20XX_SYSTEM_IRQ_LEN_ERROR = ( int ) ( 1u << 23 ), //!< Length of the received packet higher than expected + LR20XX_SYSTEM_IRQ_ADDR_ERROR = ( int ) ( 1u << 24 ), //!< Received packet discarded - no address match + LR20XX_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP = ( int ) ( 1u << 25 ), //!< LR-FHSS intra-packet hopping occurred + LR20XX_SYSTEM_IRQ_LR_FHSS_RDY_FOR_NEW_FREQ_TABLE = + ( int ) ( 1u << 26 ), //!< A new LR-FHSS frequency table can be loaded + LR20XX_SYSTEM_IRQ_LR_FHSS_RDY_FOR_NEW_PAYLOAD = ( int ) ( 1u << 27 ), //!< A new LR-FHSS payload can be loaded + LR20XX_SYSTEM_IRQ_RTTOF_RESPONDER_RESPONSE_DONE = ( int ) ( 1u << 28 ), //!< Responder sent an RTToF response + LR20XX_SYSTEM_IRQ_RTTOF_RESPONDER_REQUEST_DISCARDED = + ( int ) ( 1u << 29 ), //!< Responder discarded the RTToF request - no address match + LR20XX_SYSTEM_IRQ_RTTOF_INITIATOR_EXCHANGE_VALID = + ( int ) ( 1u << 30 ), //!< Initiator received a valid RTToF response from a responder + LR20XX_SYSTEM_IRQ_RTTOF_INITIATOR_TIMEOUT = + ( int ) ( 1u << 31 ), //!< Initiator did not receive an RTToF response from a responder + LR20XX_SYSTEM_IRQ_ALL_MASK = + LR20XX_SYSTEM_IRQ_FIFO_RX | LR20XX_SYSTEM_IRQ_FIFO_TX | LR20XX_SYSTEM_IRQ_RTTOF_RESPONDER_REQUEST_VALID | + LR20XX_SYSTEM_IRQ_TX_TIMESTAMP | LR20XX_SYSTEM_IRQ_RX_TIMESTAMP | LR20XX_SYSTEM_IRQ_PREAMBLE_DETECTED | + LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID | LR20XX_SYSTEM_IRQ_CAD_DETECTED | + LR20XX_SYSTEM_IRQ_LORA_RX_HEADER_TIMESTAMP | LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR | + LR20XX_SYSTEM_IRQ_LOW_BATTERY | LR20XX_SYSTEM_IRQ_ERROR | LR20XX_SYSTEM_IRQ_CMD_ERROR | + LR20XX_SYSTEM_IRQ_RX_DONE | LR20XX_SYSTEM_IRQ_TX_DONE | LR20XX_SYSTEM_IRQ_CAD_DONE | LR20XX_SYSTEM_IRQ_TIMEOUT | + LR20XX_SYSTEM_IRQ_CRC_ERROR | LR20XX_SYSTEM_IRQ_LEN_ERROR | LR20XX_SYSTEM_IRQ_ADDR_ERROR | + LR20XX_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP | LR20XX_SYSTEM_IRQ_LR_FHSS_RDY_FOR_NEW_FREQ_TABLE | + LR20XX_SYSTEM_IRQ_LR_FHSS_RDY_FOR_NEW_PAYLOAD | LR20XX_SYSTEM_IRQ_RTTOF_RESPONDER_RESPONSE_DONE | + LR20XX_SYSTEM_IRQ_RTTOF_RESPONDER_REQUEST_DISCARDED | LR20XX_SYSTEM_IRQ_RTTOF_INITIATOR_EXCHANGE_VALID | + LR20XX_SYSTEM_IRQ_RTTOF_INITIATOR_TIMEOUT, +}; + +/** + * @brief Interrupt type re-definition + * + * @see lr20xx_system_irq_e + */ +typedef uint32_t lr20xx_system_irq_mask_t; + +/** + * @brief Calibration mask type re-definition + * + * The values are from @ref lr20xx_system_calibration_e + */ +typedef uint8_t lr20xx_system_calibration_mask_t; + +/** + * @brief Error type re-definition + * + * @see lr20xx_system_errors_e + */ +typedef uint16_t lr20xx_system_errors_t; + +/** + * @brief Configurable DIO pins + */ +typedef enum lr20xx_system_dio_e +{ + LR20XX_SYSTEM_DIO_5 = 0x05, //!< DIO5 + LR20XX_SYSTEM_DIO_6 = 0x06, //!< DIO6 + LR20XX_SYSTEM_DIO_7 = 0x07, //!< DIO7 + LR20XX_SYSTEM_DIO_8 = 0x08, //!< DIO8 + LR20XX_SYSTEM_DIO_9 = 0x09, //!< DIO9 + LR20XX_SYSTEM_DIO_10 = 0x0A, //!< DIO10 + LR20XX_SYSTEM_DIO_11 = 0x0B, //!< DIO11 +} lr20xx_system_dio_t; + +/** + * @brief DIO pin functions + */ +typedef enum lr20xx_system_dio_func_e +{ + LR20XX_SYSTEM_DIO_FUNC_NONE = 0x00, //!< No DIO function + LR20XX_SYSTEM_DIO_FUNC_IRQ = 0x01, //!< Interrupt request function + LR20XX_SYSTEM_DIO_FUNC_RF_SWITCH = 0x02, //!< RF switch control function + LR20XX_SYSTEM_DIO_FUNC_GPIO_LOW = 0x05, //!< Set DIO state to low + LR20XX_SYSTEM_DIO_FUNC_GPIO_HIGH = 0x06, //!< Set DIO state to high + LR20XX_SYSTEM_DIO_FUNC_HF_CLK_OUT = 0x07, //!< High frequency clock output function + LR20XX_SYSTEM_DIO_FUNC_LF_CLK_OUT = 0x08, //!< Low frequency clock output function + LR20XX_SYSTEM_DIO_FUNC_TX_TRIGGER = 0x09, //!< TX trigger function + LR20XX_SYSTEM_DIO_FUNC_RX_TRIGGER = 0x0A, //!< RX trigger function +} lr20xx_system_dio_func_t; + +/** + * @brief DIO pin drive modes + */ +typedef enum lr20xx_system_dio_drive_e +{ + LR20XX_SYSTEM_DIO_DRIVE_NONE = 0x00, //!< No pull + LR20XX_SYSTEM_DIO_DRIVE_PULL_DOWN = 0x01, //!< Pull down + LR20XX_SYSTEM_DIO_DRIVE_PULL_UP = 0x02, //!< Pull up + LR20XX_SYSTEM_DIO_DRIVE_AUTO = + 0x03, //!< If the DIO value in standby mode was ‘1’, it will be pulled-up, if it was ‘0’ it will be pulled-down +} lr20xx_system_dio_drive_t; + +/** + * @brief DIO RF switch states + */ +typedef enum lr20xx_system_dio_rf_switch_cfg_state_e +{ + LR20XX_SYSTEM_DIO_RF_SWITCH_CFG_STATE_LOW = 0x00, + LR20XX_SYSTEM_DIO_RF_SWITCH_CFG_STATE_HIGH = 0x01, +} lr20xx_system_dio_rf_switch_cfg_state_t; + +/** + * @brief RF switch configuration bitmask definition + */ +enum lr20xx_system_dio_rf_switch_cfg_e +{ + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_STANDBY = ( 1 << 0 ), + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_LF = ( 1 << 1 ), + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_TX_LF = ( 1 << 2 ), + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_HF = ( 1 << 3 ), + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_TX_HF = ( 1 << 4 ), + LR20XX_SYSTEM_DIO_RF_SWITCH_ALL_MASK = + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_STANDBY | LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_LF | + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_TX_LF | LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_HF | + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_TX_HF, +}; + +/** + * @brief RF switch configuration type + * + * @see lr20xx_system_dio_rf_switch_cfg_e + */ +typedef uint8_t lr20xx_system_dio_rf_switch_cfg_t; + +/** + * @brief Low-frequency clock modes + */ +typedef enum lr20xx_system_lfclk_cfg_e +{ + LR20XX_SYSTEM_LFCLK_RC = 0x00, //!< Use internal RC 32kHz (Default) + LR20XX_SYSTEM_LFCLK_EXT = 0x02, //!< Use externally provided 32kHz signal on DIO11 +} lr20xx_system_lfclk_cfg_t; + +/** + * @brief TCXO supply voltage values + */ +typedef enum +{ + LR20XX_SYSTEM_TCXO_CTRL_1_6V = 0x00, //!< Supply voltage = 1.6v + LR20XX_SYSTEM_TCXO_CTRL_1_7V = 0x01, //!< Supply voltage = 1.7v + LR20XX_SYSTEM_TCXO_CTRL_1_8V = 0x02, //!< Supply voltage = 1.8v + LR20XX_SYSTEM_TCXO_CTRL_2_2V = 0x03, //!< Supply voltage = 2.2v + LR20XX_SYSTEM_TCXO_CTRL_2_4V = 0x04, //!< Supply voltage = 2.4v + LR20XX_SYSTEM_TCXO_CTRL_2_7V = 0x05, //!< Supply voltage = 2.7v + LR20XX_SYSTEM_TCXO_CTRL_3_0V = 0x06, //!< Supply voltage = 3.0v + LR20XX_SYSTEM_TCXO_CTRL_3_3V = 0x07, //!< Supply voltage = 3.3v +} lr20xx_system_tcxo_supply_voltage_t; + +/** + * @brief Regulator modes + */ +typedef enum lr20xx_system_reg_mode_e +{ + LR20XX_SYSTEM_REG_MODE_LDO = 0x00, //!< (Default) Only use the Low-Dropout Regulator + LR20XX_SYSTEM_REG_MODE_DCDC = 0x02, //!< Switch on the DC-to-DC regulator in applicable chip modes +} lr20xx_system_reg_mode_t; + +/** + * @brief Calibration flags + */ +enum lr20xx_system_calibration_e +{ + LR20XX_SYSTEM_CALIB_LF_RC_MASK = ( 1 << 0 ), //!< Low Frequency clock RC + LR20XX_SYSTEM_CALIB_HF_RC_MASK = ( 1 << 1 ), //!< High Frequency clock RC + LR20XX_SYSTEM_CALIB_PLL_MASK = ( 1 << 2 ), //!< PLL + LR20XX_SYSTEM_CALIB_AAF_MASK = ( 1 << 3 ), //!< Anti aliasing filter bandwidth + LR20XX_SYSTEM_CALIB_MU_MASK = ( 1 << 5 ), //!< Measure unit ADC gain + LR20XX_SYSTEM_CALIB_PA_OFF_MASK = ( 1 << 6 ), //!< Power amplifier offset +}; + +/** + * @brief Value formats + */ +typedef enum lr20xx_system_value_format_e +{ + LR20XX_SYSTEM_VALUE_FORMAT_RAW = 0x00, + LR20XX_SYSTEM_VALUE_FORMAT_UNIT = 0x01, +} lr20xx_system_value_format_t; + +/** + * @brief Measurement resolution + */ +typedef enum lr20xx_system_meas_res_e +{ + LR20XX_SYSTEM_MEAS_RES_8_BITS = 0x00, + LR20XX_SYSTEM_MEAS_RES_9_BITS = 0x01, + LR20XX_SYSTEM_MEAS_RES_10_BITS = 0x02, + LR20XX_SYSTEM_MEAS_RES_11_BITS = 0x03, + LR20XX_SYSTEM_MEAS_RES_12_BITS = 0x04, + LR20XX_SYSTEM_MEAS_RES_13_BITS = 0x05, +} lr20xx_system_meas_res_t; + +/** + * @brief Source of the measured temperature + */ +typedef enum lr20xx_system_temp_src_e +{ + LR20XX_SYSTEM_TEMP_SRC_VBE = 0x00, + LR20XX_SYSTEM_TEMP_SRC_XOSC = 0x01, + 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. + */ +typedef enum +{ + LR20XX_SYSTEM_RANDOM_ENTROPY_SOURCE_PLL = 0x01, //!< PLL is used as entropy source. The chip automatically goes to + //!< FS mode when needed, and goes back to original mode afterward. + LR20XX_SYSTEM_RANDOM_ENTROPY_SOURCE_ADC = 0x02, //!< ADC is used as entropy source. The chip automatically goes to + //!< 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. + */ +typedef uint8_t lr20xx_system_random_entropy_source_bitmask_t; + +/** + * @brief Stand-by modes + */ +typedef enum lr20xx_system_standby_mode_e +{ + LR20XX_SYSTEM_STANDBY_MODE_RC = 0x00, + LR20XX_SYSTEM_STANDBY_MODE_XOSC = 0x01, +} lr20xx_system_standby_mode_t; + +/** + * @brief High Frequency Clock scaling values + */ +typedef enum +{ + LR20XX_SYSTEM_HF_CLK_SCALING_32_MHZ = 0x00, //!< Division by 1 - 32 MHz + LR20XX_SYSTEM_HF_CLK_SCALING_16_MHZ = 0x01, //!< Division by 2 - 16 MHz + LR20XX_SYSTEM_HF_CLK_SCALING_8_MHZ = 0x02, //!< Division by 4 - 8 MHz + LR20XX_SYSTEM_HF_CLK_SCALING_4_MHZ = 0x03, //!< Division by 8 - 4 MHz + LR20XX_SYSTEM_HF_CLK_SCALING_2_MHZ = 0x04, //!< Division by 16 - 2 MHz + LR20XX_SYSTEM_HF_CLK_SCALING_1_MHZ = 0x05, //!< Division by 32 - 1 MHz + LR20XX_SYSTEM_HF_CLK_SCALING_500_KHZ = 0x06, //!< Division by 64 - 500 kHz + LR20XX_SYSTEM_HF_CLK_SCALING_250_KHZ = 0x07, //!< Division by 128 - 250 kHz + LR20XX_SYSTEM_HF_CLK_SCALING_125_KHZ = 0x08, //!< Division by 256 - 125 kHz + LR20XX_SYSTEM_HF_CLK_SCALING_62500_HZ = 0x09, //!< Division by 512 - 62500 Hz + LR20XX_SYSTEM_HF_CLK_SCALING_31250_HZ = 0x0A, //!< Division by 1024 - 31250 Hz + LR20XX_SYSTEM_HF_CLK_SCALING_15625_HZ = 0x0B, //!< Division by 2048 - 15625 Hz + LR20XX_SYSTEM_HF_CLK_SCALING_7813_HZ = 0x0C, //!< Division by 4096 - 7812.5 Hz + LR20XX_SYSTEM_HF_CLK_SCALING_3906_HZ = 0x0D, //!< Division by 8192 - 3906.25 Hz + LR20XX_SYSTEM_HF_CLK_SCALING_1953_HZ = 0x0E, //!< Division by 16384 - 1953.125 Hz + LR20XX_SYSTEM_HF_CLK_SCALING_977_HZ = 0x0F, //!< Division by 32768 - 976.5625 Hz +} lr20xx_system_hf_clk_scaling_t; + +/** + * @brief Temperature compensation modes + */ +typedef enum lr20xx_system_temp_comp_mode_e +{ + LR20XX_SYSTEM_TEMP_COMP_MODE_DISABLED = 0x00, + LR20XX_SYSTEM_TEMP_COMP_MODE_RELATIVE = 0x01, + LR20XX_SYSTEM_TEMP_COMP_MODE_ABSOLUTE = 0x02, +} lr20xx_system_temp_comp_mode_t; + +/** + * @brief Low battery detector trimming used to configure threshold triggering a battery low interrupt + */ +typedef enum lr20xx_system_lbd_trim_e +{ + LR20XX_SYSTEM_LBD_TRIM_1_60_V = 0x00, //!< EoL threshold set to 1.60V + LR20XX_SYSTEM_LBD_TRIM_1_67_V = 0x01, //!< EoL threshold set to 1.67V + LR20XX_SYSTEM_LBD_TRIM_1_74_V = 0x02, //!< EoL threshold set to 1.74V + LR20XX_SYSTEM_LBD_TRIM_1_80_V = 0x03, //!< EoL threshold set to 1.80V + LR20XX_SYSTEM_LBD_TRIM_1_88_V = 0x04, //!< EoL threshold set to 1.88V - default value + LR20XX_SYSTEM_LBD_TRIM_1_95_V = 0x05, //!< EoL threshold set to 1.98V + LR20XX_SYSTEM_LBD_TRIM_2_00_V = 0x06, //!< EoL threshold set to 2.00V + LR20XX_SYSTEM_LBD_TRIM_2_10_V = 0x07, //!< EoL threshold set to 2.10V +} lr20xx_system_lbd_trim_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_SYSTEM_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.c b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.c new file mode 100644 index 0000000..2f3e38c --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.c @@ -0,0 +1,690 @@ +/*! + * @file lr20xx_workarounds.c + * + * @brief System driver workaround implementation for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2025. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr20xx_workarounds.h" +#include "lr20xx_hal.h" +#include "lr20xx_regmem.h" +#include "lr20xx_radio_fsk_common_types.h" +#include "lr20xx_system.h" +#include "lr20xx_radio_ook.h" +#include "lr20xx_radio_common.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_SYNCWORDS ( 7 ) +#define LR20XX_WORKAROUND_BLUETOOTH_LE_2MBPS_PREAMBLE_LENGTH_BUFFER_LENGTH ( 7 ) + +#define LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_REGISTER_ADDRESS ( 0x00F30C28 ) +#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_LORA_SX1276_COMPATIBILITY_REGISTER_ADDRESS ( 0x00F30A14 ) +#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_MASK ( 1 << 18 ) + +#define LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_ADDRESS ( 0x00F30E14 ) +#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_RSSI_MAX_GAIN_REGISTER_ADDRESS ( 0x00F301A4 ) +#define LR20XX_WORKAROUND_RTTOF_RSSI_POWER_OFFSET_REGISTER_ADDRESS ( 0x00F30128 ) + +#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_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_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_MASK ( 0x38 ) +#define LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_VALUE ( 0x30 ) + +#define LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_ADDRESS ( 0xF30134 ) +#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_RTTOF_EXTENDED_STUCK_ADDRESS ( 0x00F30B50 ) +#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 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +static lr20xx_status_t lr20xx_workaround_dcdc_get_rf_frequency( const void* context, uint32_t* frequency ); + +static uint32_t pll_step_to_hz( uint32_t pll_steps ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr20xx_status_t lr20xx_workarounds_bluetooth_le_phy_coded_syncwords( const void* context ) +{ + const uint8_t cbuffer[LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_SYNCWORDS] = { 0x02, 0x30, 0x01, 0x20, + 0x00, 0x09, 0x00 }; + + return ( lr20xx_status_t ) lr20xx_hal_write( context, cbuffer, LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_SYNCWORDS, + 0, 0 ); +} + +lr20xx_status_t lr20xx_workarounds_bluetooth_le_phy_coded_frequency_drift( const void* context ) +{ + return lr20xx_regmem_write_regmem32_mask( context, + LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_REGISTER_ADDRESS, + LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_REGISTER_MASK, + LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_VALUE ); +} + +lr20xx_status_t lr20xx_workarounds_bluetooth_le_phy_coded_frequency_drift_store_retention_mem( const void* context, + uint8_t slot ) +{ + return lr20xx_system_add_register_to_retention_mem( + context, slot, LR20XX_WORKAROUND_BLUETOOTH_LE_PHY_CODED_FREQUENCY_DRIFT_REGISTER_ADDRESS ); +} + +lr20xx_status_t lr20xx_workarounds_bluetooth_le_2mbps_preamble_length( const void* context ) +{ + const uint8_t cbuffer[LR20XX_WORKAROUND_BLUETOOTH_LE_2MBPS_PREAMBLE_LENGTH_BUFFER_LENGTH] = { 0x02, 0x30, 0x01, + 0x21, 0x00, 0x07, + 0x00 }; + + return ( lr20xx_status_t ) lr20xx_hal_write( + context, cbuffer, LR20XX_WORKAROUND_BLUETOOTH_LE_2MBPS_PREAMBLE_LENGTH_BUFFER_LENGTH, 0, 0 ); +} + +lr20xx_status_t lr20xx_workarounds_lora_enable_sx1276_compatibility_mode( const void* context ) +{ + return lr20xx_workaround_lora_sx1276_compatibility_write_value( context, true ); +} + +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 + if( get_sf_status == LR20XX_STATUS_OK ) + { + return lr20xx_workaround_lora_sx1276_compatibility_write_value( context, ( ( sf <= 6 ) ? true : false ) ); + } + else + { + return get_sf_status; + } +} + +lr20xx_status_t lr20xx_workarounds_lora_sx1276_compatibility_mode_store_retention_mem( const void* context, + uint8_t slot ) +{ + return lr20xx_system_add_register_to_retention_mem( context, slot, + LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_ADDRESS ); +} + +lr20xx_status_t lr20xx_workarounds_lora_freq_hop_enable_sx1276_compatibility_mode( const void* context ) +{ + return lr20xx_workaround_lora_frequency_hopping_sx1276_compatibility_write_value( context, true ); +} + +lr20xx_status_t lr20xx_workarounds_lora_freq_hop_disable_sx1276_compatibility_mode( const void* context ) +{ + return lr20xx_workaround_lora_frequency_hopping_sx1276_compatibility_write_value( context, false ); +} + +lr20xx_status_t lr20xx_workarounds_lora_freq_hop_sx1276_compatibility_mode_store_retention_mem( const void* context, + uint8_t slot ) +{ + return lr20xx_system_add_register_to_retention_mem( + context, slot, LR20XX_WORKAROUND_LORA_FREQ_HOP_SX1276_COMPATIBILITY_REGISTER_ADDRESS ); +} + +lr20xx_status_t lr20xx_workarounds_ook_set_detection_threshold_level( const void* context, int16_t threshold_level_db ) +{ + const int threshold_db = threshold_level_db + 10 + 64; + return lr20xx_regmem_write_regmem32_mask( + context, LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_ADDRESS, + LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_MASK, + ( ( ( uint32_t ) threshold_db ) << 20u ) & LR20XX_WORKAROUND_OOK_DETECTION_THRESHOLD_REGISTER_MASK ); +} + +int16_t lr20xx_workarounds_ook_get_default_detection_threshold_level( lr20xx_radio_fsk_common_bw_t bw ) +{ + switch( bw ) + { + case LR20XX_RADIO_FSK_COMMON_RX_BW_3_500_HZ: + { + return -135; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_4_200_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_4_300_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_4_500_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_4_800_HZ: + { + return -134; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_5_200_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_5_600_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_5_800_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_6_000_HZ: + { + return -133; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_6_900_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_7_400_HZ: + { + return -132; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_8_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_8_300_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_8_700_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_8_900_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_9_600_HZ: + { + return -131; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_10_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_11_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_12_000_HZ: + { + return -130; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_13_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_14_000_HZ: + { + return -129; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_16_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_17_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_19_000_HZ: + { + return -128; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_20_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_22_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_23_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_24_000_HZ: + { + return -127; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_27_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_29_000_HZ: + { + return -126; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_32_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_33_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_34_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_35_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_38_000_HZ: + { + return -125; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_41_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_44_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_46_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_48_000_HZ: + { + return -124; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_55_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_59_000_HZ: + { + return -123; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_64_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_66_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_69_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_71_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_76_000_HZ: + { + return -122; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_83_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_89_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_92_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_96_000_HZ: + { + return -121; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_111_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_119_000_HZ: + { + return -120; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_128_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_133_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_138_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_142_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_153_000_HZ: + { + return -119; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_166_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_178_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_185_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_192_000_HZ: + { + return -118; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_222_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_238_000_HZ: + { + return -117; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_256_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_266_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_277_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_285_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_307_000_HZ: + { + return -116; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_333_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_357_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_370_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_384_000_HZ: + { + return -115; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_444_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_476_000_HZ: + { + return -114; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_512_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_533_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_555_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_571_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_615_000_HZ: + { + return -113; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_666_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_714_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_740_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_769_000_HZ: + { + return -112; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_888_000_HZ: + { + return -111; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_1_111_000_HZ: + { + return -110; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_1_333_000_HZ: + { + return -109; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_2_222_000_HZ: + { + return -107; + } + case LR20XX_RADIO_FSK_COMMON_RX_BW_2_666_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_2_857_000_HZ: + case LR20XX_RADIO_FSK_COMMON_RX_BW_3_076_000_HZ: + { + return -106; + } + default: + { + return 0; + } + } +} + +lr20xx_status_t lr20xx_workarounds_rttof_truncate_pll_freq_step( const void* context ) +{ + return lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_RTTOF_RF_FREQ_ADDRESS, + LR20XX_WORKAROUND_RTTOF_RF_FREQ_MASK, 0 ); +} + +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 ) +{ + uint16_t max_gain = 0; + int16_t power_offset = 0; + RETURN_STATUS_ON_NOT_OK( + lr20xx_workarounds_rttof_rssi_computation_get_gain_power( context, &max_gain, &power_offset ) ); + ( *rssi1_raw_fixed ) = + lr20xx_workarounds_rttof_rssi_computation_apply_correction( max_gain, power_offset, rssi1_raw_value ); + if( rssi2_raw_fixed != 0 ) + { + ( *rssi2_raw_fixed ) = + 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 ) +{ + 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 ); +} + +lr20xx_status_t lr20xx_workarounds_dcdc_configure( const void* context ) +{ + uint32_t adc_ctrl_raw = 0; + RETURN_STATUS_ON_NOT_OK( + lr20xx_regmem_read_regmem32( context, LR20XX_WORKAROUND_DCDC_ADC_CTRL_REGISTER_ADDRESS, &adc_ctrl_raw, 1 ) ); + const uint32_t ana_dec = ( adc_ctrl_raw >> 8 ) & 0x7; + + uint32_t rx_path_raw = 0; + RETURN_STATUS_ON_NOT_OK( + 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 ); + + if( ( is_rx_hf == false ) && ( ( ana_dec == 1 ) || ( ana_dec == 2 ) ) ) + { + RETURN_STATUS_ON_NOT_OK( + lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_DCDC_SWITCHER_REGISTER_ADDRESS, + LR20XX_WORKAROUND_DCDC_SWITCHER_RISE_REGISTER_MASK, 11 << 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, 13 << 16 ) ); + } + else + { + 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 ) ); + } + + if( ana_dec == 1 ) + { + return lr20xx_workaround_dcdc_set_frequency( context, 4300000 ); + } + else + { + return lr20xx_workaround_dcdc_set_frequency( context, 2800000 ); + } +} + +lr20xx_status_t lr20xx_workarounds_dcdc_store_retention_mem( const void* context, uint8_t slot ) +{ + return lr20xx_system_add_register_to_retention_mem( context, slot, + LR20XX_WORKAROUND_DCDC_SWITCHER_REGISTER_ADDRESS ); +} + +lr20xx_status_t lr20xx_workarounds_rttof_results_deviation( const void* context, bool is_manager ) +{ + RETURN_STATUS_ON_NOT_OK( + lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_ADDRESS, + LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_MASK, + LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_VALUE ) ); + return lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_ADDRESS, + LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_MASK, + is_manager ? LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_MANAGER_VALUE + : LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_SUBORDINATE_VALUE ); +} + +lr20xx_status_t lr20xx_workarounds_rttof_results_deviation_store_retention_mem( const void* context, uint8_t slot_1, + uint8_t slot_2 ) +{ + RETURN_STATUS_ON_NOT_OK( lr20xx_system_add_register_to_retention_mem( + context, slot_1, LR20XX_WORKAROUND_RESULT_DEVIATION_CHANNEL_FILTER_ADDRESS ) ); + return lr20xx_system_add_register_to_retention_mem( context, slot_2, + LR20XX_WORKAROUND_RESULT_DEVIATION_DCC_ADDRESS ); +} + +lr20xx_status_t lr20xx_workarounds_rttof_extended_stuck_second_request_enable( const void* context ) +{ + return lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_ADDRESS, + LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_MASK, + LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_SET_VALUE ); +} + +lr20xx_status_t lr20xx_workarounds_rttof_extended_stuck_second_request_disable( const void* context ) +{ + return lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_ADDRESS, + LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_MASK, + LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_RESET_VALUE ); +} + +lr20xx_status_t lr20xx_workarounds_rttof_extended_stuck_second_request_store_retention_mem( const void* context, + uint8_t slot ) +{ + return lr20xx_system_add_register_to_retention_mem( context, slot, LR20XX_WORKAROUND_RTTOF_EXTENDED_STUCK_ADDRESS ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +lr20xx_status_t lr20xx_workaround_lora_sx1276_compatibility_write_value( const void* context, bool value ) +{ + return lr20xx_regmem_write_regmem32_mask( context, LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_ADDRESS, + LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_MASK, + ( value ? ( 1 << 19 ) : 0 ) ); +} + +lr20xx_status_t lr20xx_workaround_lora_frequency_hopping_sx1276_compatibility_write_value( const void* context, + bool value ) +{ + return lr20xx_regmem_write_regmem32_mask( + context, LR20XX_WORKAROUND_LORA_FREQ_HOP_SX1276_COMPATIBILITY_REGISTER_ADDRESS, + LR20XX_WORKAROUND_LORA_FREQ_HOP_SX1276_COMPATIBILITY_REGISTER_MASK, ( value ? ( 1 << 18 ) : 0 ) ); +} + +lr20xx_status_t lr20xx_workaround_lora_sx1276_compatibility_read_sf_value( const void* context, uint8_t* sf ) +{ + uint32_t raw_register_value = 0; + const lr20xx_status_t read_status = lr20xx_regmem_read_regmem32( + context, LR20XX_WORKAROUND_LORA_SX1276_COMPATIBILITY_REGISTER_ADDRESS, &raw_register_value, 1 ); + if( read_status == LR20XX_STATUS_OK ) + { + *sf = raw_register_value & 0x0f; + } + return read_status; +} + +lr20xx_status_t lr20xx_workarounds_rttof_rssi_computation_get_gain_power( const void* context, uint16_t* max_gain, + int16_t* power_offset ) +{ + 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 ); + + 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 ) ); + 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 ); + return LR20XX_STATUS_OK; +} + +uint8_t lr20xx_workarounds_rttof_rssi_computation_apply_correction( uint16_t max_gain, int16_t power_offset, + uint8_t raw_rssi ) +{ + 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 ) +{ + 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 ) ); + uint32_t rf_frequency = 0; + RETURN_STATUS_ON_NOT_OK( lr20xx_workaround_dcdc_get_rf_frequency( context, &rf_frequency ) ); + return lr20xx_radio_common_set_rf_freq( context, rf_frequency ); +} + +lr20xx_status_t lr20xx_workaround_dcdc_get_rf_frequency( const void* context, uint32_t* frequency ) +{ + uint32_t raw_rf_freq = 0; + RETURN_STATUS_ON_NOT_OK( + lr20xx_regmem_read_regmem32( context, LR20XX_WORKAROUND_DCDC_RF_FREQ_ADDRESS, &raw_rf_freq, 1 ) ); + *frequency = pll_step_to_hz( raw_rf_freq ); + return LR20XX_STATUS_OK; +} + +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 + return ( uint32_t ) ( ( numerator + denominator - 1 ) / denominator ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.h b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.h new file mode 100644 index 0000000..37bb84f --- /dev/null +++ b/zephcore/adapters/radio/lr20xx/lr20xx_workarounds.h @@ -0,0 +1,491 @@ +/*! + * @file lr20xx_workarounds.h + * + * @brief System driver workarounds definition for LR20XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2025. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR20XX_WORKAROUNDS_H +#define LR20XX_WORKAROUNDS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "lr20xx_status.h" +#include "lr20xx_radio_fsk_common_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +#ifndef LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_RESET +#define LR20XX_WORKAROUNDS_CONDITIONAL_APPLY_AUTOMATIC_DCDC_RESET( cont ) lr20xx_workarounds_dcdc_reset( cont ) +#else +#define LR20XX_WORKAROUNDS_CONDITIONAL_APPLY_AUTOMATIC_DCDC_RESET( cont ) LR20XX_STATUS_OK +#endif // LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_RESET + +#ifndef LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE +#define LR20XX_WORKAROUNDS_CONDITIONAL_APPLY_AUTOMATIC_DCDC_CONFIGURE( cont ) lr20xx_workarounds_dcdc_configure( cont ) +#else +#define LR20XX_WORKAROUNDS_CONDITIONAL_APPLY_AUTOMATIC_DCDC_CONFIGURE( cont ) LR20XX_STATUS_OK +#endif // LR20XX_WORKAROUNDS_DISABLE_AUTOMATIC_DCDC_CONFIGURE + +#ifndef LR20XX_WORKAROUND_DISABLE_AUTOMATIC_BLE_2MBPS_PREAMBLE_LENGTH +#define LR20XX_WORKAROUND_CONDITIONAL_APPLY_BLE_2MBPS_PREAMBLE_LENGTH( cont ) \ + lr20xx_workarounds_bluetooth_le_2mbps_preamble_length( cont ) +#else +#define LR20XX_WORKAROUND_CONDITIONAL_APPLY_BLE_2MBPS_PREAMBLE_LENGTH( cont ) LR20XX_STATUS_OK +#endif // LR20XX_WORKAROUND_DISABLE_AUTOMATIC_BLE_2MBPS_PREAMBLE_LENGTH + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- 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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + * + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +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 + */ +lr20xx_status_t lr20xx_workarounds_rttof_extended_stuck_second_request_store_retention_mem( const void* context, + uint8_t slot ); + +#ifdef __cplusplus +} +#endif + +#endif // LR20XX_WORKAROUNDS_H + +/* --- EOF ------------------------------------------------------------------ */ \ No newline at end of file diff --git a/zephcore/boards/nrf52840/promicro_lr2021/Kconfig.promicro_lr2021 b/zephcore/boards/nrf52840/promicro_lr2021/Kconfig.promicro_lr2021 new file mode 100644 index 0000000..b40b557 --- /dev/null +++ b/zephcore/boards/nrf52840/promicro_lr2021/Kconfig.promicro_lr2021 @@ -0,0 +1,6 @@ +# ProMicro LR2021 board configuration +# Copyright (c) 2025 ZephCore +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_PROMICRO_LR2021 + select SOC_NRF52840_QIAA diff --git a/zephcore/boards/nrf52840/promicro_lr2021/board.conf b/zephcore/boards/nrf52840/promicro_lr2021/board.conf new file mode 100644 index 0000000..0efa411 --- /dev/null +++ b/zephcore/boards/nrf52840/promicro_lr2021/board.conf @@ -0,0 +1,10 @@ +# ProMicro LR2021 — nRF52840 SuperMini + LR2021 +# SoftDevice v6 (S140 v6) — NiceNano/SuperMini bootloader uses S140 6.1.1 +# 0x00B6 = S140 v6 firmware ID; app partition starts at 0x26000 + +CONFIG_ZEPHCORE_BOARD_NAME="ProMicro LR2021" +CONFIG_BT_DIS_MODEL_NUMBER_STR="ProMicro LR2021 nRF52840-LR2021" +CONFIG_ZEPHCORE_SD_FWID=0x00B6 + +# LR2021 radio driver +CONFIG_ZEPHCORE_RADIO_LR2021=y diff --git a/zephcore/boards/nrf52840/promicro_lr2021/board.yml b/zephcore/boards/nrf52840/promicro_lr2021/board.yml new file mode 100644 index 0000000..865b53f --- /dev/null +++ b/zephcore/boards/nrf52840/promicro_lr2021/board.yml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 ZephCore +# SPDX-License-Identifier: Apache-2.0 +board: + name: promicro_lr2021 + full_name: ProMicro LR2021 (nRF52840 SuperMini + LR2021) + vendor: zephcore + socs: + - name: nrf52840 diff --git a/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021-pinctrl.dtsi b/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021-pinctrl.dtsi new file mode 100644 index 0000000..8e87cd7 --- /dev/null +++ b/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021-pinctrl.dtsi @@ -0,0 +1,58 @@ +/* + * ProMicro LR2021 pin control definitions + * Copyright (c) 2025 ZephCore + * SPDX-License-Identifier: Apache-2.0 + * + * SPI2 (LR2021): SCK=P1.11, MOSI=P1.15, MISO=P0.02 + * I2C0 (sensors): SDA=P1.04, SCL=P0.11 + * UART0 (GPS): TX=P0.20→GPS RX, RX=P0.22←GPS TX + */ + +&pinctrl { + spi2_default: spi2_default { + group1 { + psels = , + , + ; + }; + }; + spi2_sleep: spi2_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + i2c0_default: i2c0_default { + group1 { + psels = , + ; + }; + }; + i2c0_sleep: i2c0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + uart0_default: uart0_default { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + uart0_sleep: uart0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; diff --git a/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021.dts b/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021.dts new file mode 100644 index 0000000..a33da9b --- /dev/null +++ b/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021.dts @@ -0,0 +1,205 @@ +/* + * ProMicro LR2021 — nRF52840 SuperMini + LR2021 + * Copyright (c) 2025 ZephCore + * + * SPDX-License-Identifier: Apache-2.0 + * + * Hardware: + * - nRF52840 (SuperMini/ProMicro form factor) with BLE 5 + * - Semtech LR2021 LoRa Plus radio (DIO5 as IRQ, DIO3 as TCXO supply) + * - GPS module on UART0 (9600 baud, P0.22 RX, P0.20 TX) + * - Battery ADC on AIN7 (P0.31), 150K+150K divider (2:1) + * - User button on P1.00, LED on P0.15 + * - 3V3 enable on P0.13 (must be HIGH for LR2021 to function) + */ + +/dts-v1/; +#include +#include "promicro_lr2021-pinctrl.dtsi" +#include +#include + +/ { + model = "ProMicro LR2021"; + compatible = "zephcore,promicro-lr2021"; + + chosen { + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,code-partition = &code_partition; + zephyr,console = &cdc_acm_uart; + zephyr,shell-uart = &cdc_acm_uart; + }; + + aliases { + lora0 = &lora; + led0 = &led0; + sw0 = &user_button; + watchdog0 = &wdt0; + gps-enable = &gps_enable_pin; + }; + + leds { + compatible = "gpio-leds"; + led0: led_0 { + gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; + label = "LED"; + }; + }; + + buttons: buttons { + compatible = "gpio-keys"; + user_button: button_0 { + gpios = <&gpio1 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + zephyr,code = ; + label = "User Button"; + }; + }; + + /* 3V3 enable — must be HIGH to power LR2021 module */ + vcc3v3_enable: vcc3v3-enable { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3-enable"; + enable-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; + regulator-boot-on; + startup-delay-us = <5000>; + }; + + /* GPS power control */ + gps_en: gps-enable { + compatible = "gpio-leds"; + gps_enable_pin: gps_enable { + gpios = <&gpio0 24 GPIO_ACTIVE_HIGH>; + label = "GPS Enable"; + }; + }; + + /* Battery ADC: P0.31 (AIN7), 150K+150K voltage divider (2:1) */ + zephyr,user { + io-channels = <&adc 7>; + vbat-mv-multiplier = <7200>; + }; +}; + +®0 { + status = "okay"; +}; + +®1 { + regulator-initial-mode = ; +}; + +&uicr { + nfct-pins-as-gpios; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&adc { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + /* Battery voltage on P0.31 (AIN7) */ + channel@7 { + reg = <7>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; + zephyr,resolution = <12>; + }; +}; + +/* ---- SPI2 for LR2021 ---- */ +&spi2 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi2_default>; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; + cs-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; /* P1.13 LR2021_CS */ + + lora: lora@0 { + compatible = "semtech,lr2021"; + reg = <0>; + spi-max-frequency = <8000000>; + + reset-gpios = <&gpio0 9 GPIO_ACTIVE_LOW>; + busy-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>; + dio1-gpios = <&gpio0 10 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + + tcxo-voltage-mv = <1800>; + tcxo-startup-delay-ms = <5>; + rx-boosted; + + /* RF switch via LR2021 internal DIOs (DIO5=bit0..DIO8=bit3) + * Standard Semtech reference design RF switch config: + * DIO5: ANT_SW (high in all active modes) + * DIO6: TX_SW (high in TX) + * DIO7: unused + * DIO8: RX_SW (high in RX) + * rfswitch-rx=0x09 → DIO5+DIO8 HIGH in RX + * rfswitch-tx=0x03 → DIO5+DIO6 HIGH in LF TX + * rfswitch-tx-hp=0x03 → DIO5+DIO6 HIGH in HF TX */ + rfswitch-enable = <0x0F>; + rfswitch-standby = <0x00>; + rfswitch-rx = <0x09>; + rfswitch-tx = <0x03>; + rfswitch-tx-hp = <0x03>; + + pa-hp-sel = <7>; + pa-duty-cycle = <4>; + }; +}; + +/* ---- I2C0 for optional sensors ---- */ +&i2c0 { + compatible = "nordic,nrf-twim"; + status = "okay"; + clock-frequency = ; + pinctrl-0 = <&i2c0_default>; + pinctrl-1 = <&i2c0_sleep>; + pinctrl-names = "default", "sleep"; + + /* All supported environment & power sensors — auto-detected at runtime */ + #include "../../common/sensors-i2c.dtsi" +}; + +/* ---- GPS on UART0 (9600 baud) ---- */ +&uart0 { + compatible = "nordic,nrf-uarte"; + status = "okay"; + current-speed = <9600>; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; + + gnss: gnss { + compatible = "luatos,air530z"; + on-off-gpios = <&gpio0 24 GPIO_ACTIVE_HIGH>; + }; +}; + +/* ---- USB CDC for console/CLI ---- */ +zephyr_udc0: &usbd { + compatible = "nordic,nrf-usbd"; + status = "okay"; + + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + }; +}; + +/* ---- Flash partitions (SoftDevice v6, app@0x26000) ---- */ +#include "../../common/nrf52_partitions_sdv6.dtsi" diff --git a/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021_defconfig b/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021_defconfig new file mode 100644 index 0000000..1551170 --- /dev/null +++ b/zephcore/boards/nrf52840/promicro_lr2021/promicro_lr2021_defconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 +CONFIG_ARM_MPU=y +CONFIG_HW_STACK_PROTECTION=y +CONFIG_GPIO=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y +CONFIG_PINCTRL=y +CONFIG_SPI=y +CONFIG_ADC=y diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/CMakeLists.txt b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/CMakeLists.txt new file mode 100644 index 0000000..a465468 --- /dev/null +++ b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025 ZephCore +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_compile_definitions(LR20XX_DISABLE_WARNINGS) + +zephyr_library_sources(lr20xx_lora.c) + +set(LR20XX_SDK_DIR ${APPLICATION_SOURCE_DIR}/adapters/radio/lr20xx) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_hal_zephyr.c) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_radio_common.c) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_radio_lora.c) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_radio_fifo.c) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_system.c) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_regmem.c) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_workarounds.c) +zephyr_library_sources(${LR20XX_SDK_DIR}/lr20xx_driver_version.c) + +zephyr_library_include_directories(${LR20XX_SDK_DIR}) +zephyr_library_add_dependencies(offsets_h) diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/Kconfig b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/Kconfig new file mode 100644 index 0000000..12d139d --- /dev/null +++ b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/Kconfig @@ -0,0 +1,13 @@ +# Copyright (c) 2025 ZephCore +# SPDX-License-Identifier: Apache-2.0 + +config LORA_LR20XX + bool "Semtech LR20xx LoRa transceiver driver" + default y + depends on DT_HAS_SEMTECH_LR2021_ENABLED + select SPI + select GPIO + help + Enable driver for Semtech LR2021 (LoRa Plus, 4th-gen) transceivers. + Uses native Semtech lr20xx_driver SDK with Zephyr LoRa driver API. + Supports sub-GHz + 2.4 GHz ISM + NTN/SATCOM. diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c new file mode 100644 index 0000000..ee1cdd4 --- /dev/null +++ b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c @@ -0,0 +1,984 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * LR20xx Zephyr LoRa driver + * + * Implements the standard Zephyr lora_driver_api using the Semtech lr20xx_driver + * SDK. All SPI access, DIO1 IRQ handling, and radio state management is internal. + */ + +#define DT_DRV_COMPAT semtech_lr2021 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lr20xx_lora.h" +#include "lr20xx_hal_zephyr.h" +#include "lr20xx_radio_common.h" +#include "lr20xx_radio_common_types.h" +#include "lr20xx_radio_lora.h" +#include "lr20xx_radio_lora_types.h" +#include "lr20xx_radio_fifo.h" +#include "lr20xx_system.h" +#include "lr20xx_system_types.h" + +LOG_MODULE_REGISTER(lr20xx_lora, CONFIG_LORA_LOG_LEVEL); + +/* Dedicated DIO1 work queue — keeps LoRa interrupt processing off the + * system work queue so USB/BLE/timer work items cannot delay packet RX. */ +#define LR20XX_DIO1_WQ_STACK_SIZE 2560 +K_THREAD_STACK_DEFINE(lr20xx_dio1_wq_stack, LR20XX_DIO1_WQ_STACK_SIZE); + +/* ── Driver data structures ─────────────────────────────────────────── */ + +struct lr20xx_config { + struct spi_dt_spec bus; + struct gpio_dt_spec reset; + struct gpio_dt_spec busy; + struct gpio_dt_spec dio1; + uint16_t tcxo_voltage_mv; + uint32_t tcxo_startup_delay_ms; + bool rx_boosted; + /* RF switch DIO bitmasks (bit 0 = DIO5, bit 1 = DIO6, ...) */ + uint8_t rfswitch_enable; + uint8_t rfswitch_standby; + uint8_t rfswitch_rx; + uint8_t rfswitch_tx; + uint8_t rfswitch_tx_hp; + /* PA config */ + uint8_t pa_hp_sel; /* maps to pa_lf_slices in LR20xx */ + uint8_t pa_duty_cycle; /* maps to pa_lf_duty_cycle in LR20xx */ +}; + +struct lr20xx_data { + const struct device *dev; + struct lr20xx_hal_context hal_ctx; + struct k_mutex spi_mutex; + + /* Cached modem config from lora_config() */ + struct lora_modem_config modem_cfg; + bool configured; + + /* Async RX state */ + lora_recv_cb async_rx_cb; + void *async_rx_user_data; + + /* Async TX state */ + struct k_poll_signal *tx_signal; + + /* DIO1 work — runs on dedicated queue, not system work queue */ + struct k_work dio1_work; + struct k_work_q dio1_wq; + + /* Radio state */ + volatile bool tx_active; + volatile bool in_rx_mode; + + /* Extension features */ + bool rx_duty_cycle_enabled; + bool rx_boost_enabled; + bool rx_boost_applied; + + /* Deferred hardware init */ + bool hw_initialized; + + /* DIO1 stuck-HIGH detection */ + int dio1_stuck_count; + + /* RX data buffer */ + uint8_t rx_buf[256]; +}; + +/* ── Helpers ────────────────────────────────────────────────────────── */ + +static lr20xx_radio_lora_bw_t bw_enum_to_lr20xx(enum lora_signal_bandwidth bw) +{ + switch (bw) { + case BW_31_KHZ: return LR20XX_RADIO_LORA_BW_31; + case BW_41_KHZ: return LR20XX_RADIO_LORA_BW_41; + case BW_62_KHZ: return LR20XX_RADIO_LORA_BW_62; + case BW_125_KHZ: return LR20XX_RADIO_LORA_BW_125; + case BW_250_KHZ: return LR20XX_RADIO_LORA_BW_250; + case BW_500_KHZ: return LR20XX_RADIO_LORA_BW_500; + default: return LR20XX_RADIO_LORA_BW_125; + } +} + +static lr20xx_radio_lora_cr_t cr_enum_to_lr20xx(enum lora_coding_rate cr) +{ + switch (cr) { + case CR_4_5: return LR20XX_RADIO_LORA_CR_4_5; + case CR_4_6: return LR20XX_RADIO_LORA_CR_4_6; + case CR_4_7: return LR20XX_RADIO_LORA_CR_4_7; + case CR_4_8: return LR20XX_RADIO_LORA_CR_4_8; + default: return LR20XX_RADIO_LORA_CR_4_8; + } +} + +static lr20xx_system_tcxo_supply_voltage_t get_tcxo_voltage(uint16_t mv) +{ + if (mv >= 3300) return LR20XX_SYSTEM_TCXO_CTRL_3_3V; + if (mv >= 3000) return LR20XX_SYSTEM_TCXO_CTRL_3_0V; + if (mv >= 2700) return LR20XX_SYSTEM_TCXO_CTRL_2_7V; + if (mv >= 2400) return LR20XX_SYSTEM_TCXO_CTRL_2_4V; + if (mv >= 2200) return LR20XX_SYSTEM_TCXO_CTRL_2_2V; + if (mv >= 1800) return LR20XX_SYSTEM_TCXO_CTRL_1_8V; + return LR20XX_SYSTEM_TCXO_CTRL_1_6V; +} + +/* Get kHz value from Zephyr BW enum — used for LDRO/PPM calculation */ +static float bw_enum_to_khz(enum lora_signal_bandwidth bw) +{ + switch (bw) { + case BW_7_KHZ: return 7.81f; + case BW_10_KHZ: return 10.42f; + case BW_15_KHZ: return 15.63f; + case BW_20_KHZ: return 20.83f; + case BW_31_KHZ: return 31.25f; + case BW_41_KHZ: return 41.67f; + case BW_62_KHZ: return 62.5f; + case BW_125_KHZ: return 125.0f; + case BW_250_KHZ: return 250.0f; + case BW_500_KHZ: return 500.0f; + default: return 125.0f; + } +} + +/* ── Configure RF switch DIOs ───────────────────────────────────────── */ + +static void lr20xx_configure_rfswitch(void *ctx, const struct lr20xx_config *cfg) +{ + /* LR20xx RF switch uses per-DIO configuration. + * DIO5..DIO8 map to enable bitmask bits 0..3. + * For each enabled DIO, compute which operational modes + * should drive it HIGH by looking at the per-mode bitmasks. */ + for (int i = 0; i < 4; i++) { + if (!(cfg->rfswitch_enable & BIT(i))) { + continue; + } + + lr20xx_system_dio_t dio = (lr20xx_system_dio_t)(LR20XX_SYSTEM_DIO_5 + i); + + /* Set this DIO function to RF switch control */ + lr20xx_system_set_dio_function(ctx, dio, + LR20XX_SYSTEM_DIO_FUNC_RF_SWITCH); + + /* Build the per-DIO mode bitmask: + * which operational modes drive this DIO HIGH */ + lr20xx_system_dio_rf_switch_cfg_t sw_cfg = 0; + + if (cfg->rfswitch_standby & BIT(i)) { + sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_STANDBY; + } + if (cfg->rfswitch_rx & BIT(i)) { + sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_LF | + LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_RX_HF; + } + if (cfg->rfswitch_tx & BIT(i)) { + sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_TX_LF; + } + if (cfg->rfswitch_tx_hp & BIT(i)) { + sw_cfg |= LR20XX_SYSTEM_DIO_RF_SWITCH_WHEN_TX_HF; + } + + lr20xx_system_set_dio_rf_switch_cfg(ctx, dio, sw_cfg); + } +} + +/* ── Hardware reset (BUSY stuck recovery) ───────────────────────────── */ + +static void lr20xx_hardware_reset(struct lr20xx_data *data, + const struct lr20xx_config *cfg) +{ + void *ctx = &data->hal_ctx; + + LOG_WRN("LR2021 hardware reset (BUSY stuck recovery)"); + + lr20xx_hal_reset(ctx); + + if (cfg->tcxo_voltage_mv > 0) { + lr20xx_system_set_tcxo_mode(ctx, + get_tcxo_voltage(cfg->tcxo_voltage_mv), + (cfg->tcxo_startup_delay_ms * 1000) / 31); + } + + lr20xx_system_set_reg_mode(ctx, LR20XX_SYSTEM_REG_MODE_DCDC); + + lr20xx_configure_rfswitch(ctx, cfg); + + lr20xx_system_calibrate(ctx, 0x6F); + + lr20xx_radio_common_set_rx_tx_fallback_mode(ctx, + LR20XX_RADIO_FALLBACK_STDBY_RC); + + lr20xx_radio_common_set_pkt_type(ctx, LR20XX_RADIO_COMMON_PKT_TYPE_LORA); + + lr20xx_system_clear_errors(ctx); + lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK); + + data->rx_boost_applied = false; + + lr20xx_hal_enable_dio1_irq(&data->hal_ctx); + + LOG_WRN("LR2021 recovered from hardware reset"); +} + +/* ── Apply modem configuration ──────────────────────────────────────── */ + +static void lr20xx_apply_modem_config(struct lr20xx_data *data, + const struct lr20xx_config *cfg, + bool tx_mode) +{ + void *ctx = &data->hal_ctx; + struct lora_modem_config *mc = &data->modem_cfg; + + lr20xx_radio_common_set_rf_freq(ctx, mc->frequency); + + /* LR20xx uses PPM offset instead of explicit LDRO. + * PPM_1_4 (1 bin every 4) is equivalent to LDRO for high-SF + * wide-time-on-air configurations. Use recommended value. */ + lr20xx_radio_lora_mod_params_t mod = { + .sf = (lr20xx_radio_lora_sf_t)mc->datarate, + .bw = bw_enum_to_lr20xx(mc->bandwidth), + .cr = cr_enum_to_lr20xx(mc->coding_rate), + .ppm = lr20xx_radio_lora_get_recommended_ppm_offset( + (lr20xx_radio_lora_sf_t)mc->datarate, + bw_enum_to_lr20xx(mc->bandwidth)), + }; + lr20xx_radio_lora_set_modulation_params(ctx, &mod); + + lr20xx_radio_lora_pkt_params_t pkt = { + .preamble_len_in_symb = mc->preamble_len, + .pkt_mode = LR20XX_RADIO_LORA_PKT_EXPLICIT, + .pld_len_in_bytes = 255, + .crc = mc->packet_crc_disable ? LR20XX_RADIO_LORA_CRC_DISABLED + : LR20XX_RADIO_LORA_CRC_ENABLED, + .iq = mc->iq_inverted ? LR20XX_RADIO_LORA_IQ_INVERTED + : LR20XX_RADIO_LORA_IQ_STANDARD, + }; + lr20xx_radio_lora_set_packet_params(ctx, &pkt); + + lr20xx_radio_lora_set_syncword(ctx, + mc->public_network ? 0x34 : 0x12); + + if (tx_mode) { + /* LR20xx set_tx_params uses half-dBm (multiply by 2) */ + lr20xx_radio_common_set_tx_params(ctx, + (int8_t)(mc->tx_power * 2), + LR20XX_RADIO_COMMON_RAMP_48_US); + + lr20xx_radio_common_select_pa(ctx, LR20XX_RADIO_COMMON_PA_SEL_LF); + + lr20xx_radio_common_pa_cfg_t pa = { + .pa_sel = LR20XX_RADIO_COMMON_PA_SEL_LF, + .pa_lf_mode = LR20XX_RADIO_COMMON_PA_LF_MODE_FSM, + .pa_lf_duty_cycle = cfg->pa_duty_cycle, + .pa_lf_slices = cfg->pa_hp_sel, + .pa_hf_duty_cycle = 16, /* unused for LF, default */ + }; + lr20xx_radio_common_set_pa_cfg(ctx, &pa); + } + + /* Route IRQ events to DIO5 (physical DIO1 pin on the board) */ + lr20xx_system_set_dio_irq_cfg(ctx, LR20XX_SYSTEM_DIO_5, + LR20XX_SYSTEM_IRQ_RX_DONE | LR20XX_SYSTEM_IRQ_TX_DONE | + LR20XX_SYSTEM_IRQ_TIMEOUT | LR20XX_SYSTEM_IRQ_CRC_ERROR | + LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR); +} + +/* ── Start RX (internal) ────────────────────────────────────────────── */ + +static void lr20xx_start_rx(struct lr20xx_data *data, + const struct lr20xx_config *cfg) +{ + void *ctx = &data->hal_ctx; + + LOG_DBG("start_rx: t=%lld", k_uptime_get()); + + /* Standby first — wake from any sleep state */ + data->hal_ctx.radio_is_sleeping = true; + lr20xx_status_t rc = lr20xx_system_set_standby_mode(ctx, + LR20XX_SYSTEM_STANDBY_MODE_RC); + if (rc != LR20XX_STATUS_OK) { + LOG_ERR("standby failed (rc=%d) — triggering HW reset", rc); + lr20xx_hardware_reset(data, cfg); + } + + lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK); + + lr20xx_apply_modem_config(data, cfg, false); + + /* Apply RX boost if needed */ + if (data->rx_boost_enabled && !data->rx_boost_applied) { + lr20xx_radio_common_set_rx_path( + ctx, LR20XX_RADIO_COMMON_RX_PATH_LF, + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_4); + data->rx_boost_applied = true; + } + + /* Start continuous RX using RTC-step API (0xFFFFFF = continuous) */ + lr20xx_radio_common_set_rx_with_timeout_in_rtc_step(ctx, 0xFFFFFF); + + /* Clear any IRQ flags set during modem configuration */ + lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK); + + data->in_rx_mode = true; + data->tx_active = false; +} + +/* ── Lightweight RX restart (no modem reconfig) ─────────────────────── */ + +static void lr20xx_restart_rx(struct lr20xx_data *data) +{ + void *ctx = &data->hal_ctx; + + lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK); + lr20xx_radio_common_set_rx_with_timeout_in_rtc_step(ctx, 0xFFFFFF); + + data->in_rx_mode = true; +} + +/* ── DIO1 IRQ handler (work queue, thread context) ──────────────────── */ + +static void lr20xx_dio1_callback(void *user_data); + +static void lr20xx_dio1_work_handler(struct k_work *work) +{ + struct lr20xx_data *data = CONTAINER_OF(work, struct lr20xx_data, + dio1_work); + const struct lr20xx_config *cfg = data->dev->config; + void *ctx = &data->hal_ctx; + bool rx_restarted = false; + + k_mutex_lock(&data->spi_mutex, K_FOREVER); + + /* Combined get + clear IRQ status */ + lr20xx_system_irq_mask_t irq = 0; + lr20xx_status_t rc = lr20xx_system_get_and_clear_irq_status(ctx, &irq); + + if (rc != LR20XX_STATUS_OK) { + LOG_ERR("Failed to read IRQ status (rc=%d)", rc); + goto safety_check; + } + + LOG_DBG("DIO1 IRQ: 0x%08x tx=%d t=%lld", irq, data->tx_active, + k_uptime_get()); + + if (irq & LR20XX_SYSTEM_IRQ_ERROR) { + LOG_WRN("IRQ hardware ERROR: 0x%08x", irq); + } + + if (irq != 0) { + data->dio1_stuck_count = 0; + } + + /* ── RX done ── */ + if (irq & LR20XX_SYSTEM_IRQ_RX_DONE) { + uint16_t pkt_len = 0; + lr20xx_radio_common_get_rx_packet_length(ctx, &pkt_len); + + if (pkt_len > 0 && pkt_len <= 255) { + lr20xx_radio_lora_packet_status_t pkt_stat; + lr20xx_radio_lora_get_packet_status(ctx, &pkt_stat); + + lr20xx_radio_fifo_read_rx(ctx, data->rx_buf, + (uint16_t)pkt_len); + + /* Restart RX before firing callback */ + lr20xx_restart_rx(data); + rx_restarted = true; + + /* When SNR < 0, use signal RSSI for a more + * accurate reading on weak links. */ + int16_t rssi = pkt_stat.rssi_pkt_in_dbm; + int8_t snr = pkt_stat.snr_pkt_raw / 4; + + if (snr < 0 && + pkt_stat.rssi_signal_pkt_in_dbm > rssi) { + rssi = pkt_stat.rssi_signal_pkt_in_dbm; + } + + k_mutex_unlock(&data->spi_mutex); + + if (data->async_rx_cb) { + data->async_rx_cb(data->dev, data->rx_buf, + (uint8_t)pkt_len, + rssi, snr, + data->async_rx_user_data); + } + return; + } + + LOG_WRN("RX: invalid len %d", pkt_len); + lr20xx_restart_rx(data); + rx_restarted = true; + } + + /* ── TX done ── */ + if (irq & LR20XX_SYSTEM_IRQ_TX_DONE) { + LOG_DBG("TX done"); + data->tx_active = false; + + lr20xx_start_rx(data, cfg); + rx_restarted = true; + + if (data->tx_signal) { + k_poll_signal_raise(data->tx_signal, 0); + } + } + + /* ── Timeout ── */ + if (irq & LR20XX_SYSTEM_IRQ_TIMEOUT) { + LOG_DBG("Timeout IRQ — restarting RX"); + if (!data->tx_active) { + lr20xx_restart_rx(data); + rx_restarted = true; + } + } + + /* ── CRC / Header error ── */ + if (irq & LR20XX_SYSTEM_IRQ_CRC_ERROR || + ((irq & LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR) && + !(irq & LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID))) { + LOG_WRN("RX error: CRC=%d HDR=%d", + (irq & LR20XX_SYSTEM_IRQ_CRC_ERROR) ? 1 : 0, + (irq & LR20XX_SYSTEM_IRQ_LORA_HEADER_ERROR) ? 1 : 0); + + if (!data->tx_active) { + lr20xx_restart_rx(data); + rx_restarted = true; + } + + k_mutex_unlock(&data->spi_mutex); + + if (data->async_rx_cb) { + data->async_rx_cb(data->dev, NULL, 0, 0, 0, + data->async_rx_user_data); + } + return; + } + +safety_check: + if (!rx_restarted && data->in_rx_mode && !data->tx_active) { + LOG_WRN("DIO1 safety: no IRQ handled (0x%08x rc=%d), " + "restarting RX", irq, rc); + lr20xx_restart_rx(data); + } + + /* Edge-triggered DIO1: if still HIGH, re-submit for pending flags. + * Guard against stuck DIO1: after 5 empty cycles, hardware reset. */ + if (gpio_pin_get_dt(&data->hal_ctx.dio1)) { + data->dio1_stuck_count++; + if (data->dio1_stuck_count >= 5) { + LOG_ERR("DIO1 stuck HIGH for %d cycles — " + "hardware reset", data->dio1_stuck_count); + data->dio1_stuck_count = 0; + lr20xx_hardware_reset(data, cfg); + lr20xx_start_rx(data, cfg); + } else { + k_work_submit_to_queue(&data->dio1_wq, + &data->dio1_work); + } + } else { + data->dio1_stuck_count = 0; + } + + k_mutex_unlock(&data->spi_mutex); +} + +static void lr20xx_dio1_callback(void *user_data) +{ + struct lr20xx_data *data = (struct lr20xx_data *)user_data; + k_work_submit_to_queue(&data->dio1_wq, &data->dio1_work); +} + +/* Forward declaration */ +static int lr20xx_hw_init(struct lr20xx_data *data, + const struct lr20xx_config *cfg); + +/* ── Driver API: config ─────────────────────────────────────────────── */ + +static int lr20xx_lora_config(const struct device *dev, + struct lora_modem_config *config) +{ + struct lr20xx_data *data = dev->data; + + if (!data->hw_initialized) { + int ret = lr20xx_hw_init(data, dev->config); + if (ret != 0) { + LOG_ERR("Hardware init failed: %d", ret); + return ret; + } + } + + memcpy(&data->modem_cfg, config, sizeof(*config)); + data->configured = true; + + /* Image calibration at operating frequency */ + k_mutex_lock(&data->spi_mutex, K_FOREVER); + lr20xx_radio_common_front_end_calibration_value_t cal = { + .rx_path = LR20XX_RADIO_COMMON_RX_PATH_LF, + .frequency_in_hertz = config->frequency, + }; + lr20xx_radio_common_calibrate_front_end_helper(&data->hal_ctx, + &cal, 1); + k_mutex_unlock(&data->spi_mutex); + + LOG_INF("config: %uHz SF%d BW%d CR%d pwr=%d tx=%d", + config->frequency, config->datarate, config->bandwidth, + config->coding_rate, config->tx_power, config->tx); + + return 0; +} + +/* ── Driver API: airtime ────────────────────────────────────────────── */ + +static uint32_t lr20xx_lora_airtime(const struct device *dev, + uint32_t data_len) +{ + struct lr20xx_data *data = dev->data; + struct lora_modem_config *mc = &data->modem_cfg; + + uint8_t sf = (uint8_t)mc->datarate; + float bw = bw_enum_to_khz(mc->bandwidth) * 1000.0f; + uint8_t cr = (uint8_t)mc->coding_rate + 4; + + float ts = (float)(1 << sf) / bw; + int de = (sf >= 11 && bw <= 125000.0f) ? 1 : 0; + float n_payload = 8.0f + fmaxf( + ceilf((8.0f * data_len - 4.0f * sf + 28.0f + 16.0f) / + (4.0f * (sf - 2.0f * de))) * cr, + 0.0f); + float t_preamble = (mc->preamble_len + 4.25f) * ts; + float t_payload = n_payload * ts; + + return (uint32_t)((t_preamble + t_payload) * 1000.0f); +} + +/* ── Driver API: send_async ─────────────────────────────────────────── */ + +static int lr20xx_lora_send_async(const struct device *dev, + uint8_t *buf, uint32_t data_len, + struct k_poll_signal *async) +{ + struct lr20xx_data *data = dev->data; + const struct lr20xx_config *cfg = dev->config; + void *ctx = &data->hal_ctx; + + if (!data->configured) return -EINVAL; + if (data->tx_active) return -EBUSY; + if (data_len > 255 || data_len == 0) return -EINVAL; + + k_mutex_lock(&data->spi_mutex, K_FOREVER); + + data->async_rx_cb = NULL; + data->in_rx_mode = false; + + lr20xx_hal_disable_dio1_irq(&data->hal_ctx); + + /* Standby — wake from sleep if needed */ + data->hal_ctx.radio_is_sleeping = true; + lr20xx_status_t rc = lr20xx_system_set_standby_mode(ctx, + LR20XX_SYSTEM_STANDBY_MODE_RC); + if (rc != LR20XX_STATUS_OK) { + LOG_ERR("TX standby failed — HW reset"); + lr20xx_hardware_reset(data, cfg); + } + + lr20xx_apply_modem_config(data, cfg, true); + + /* Set TX-specific packet length */ + lr20xx_radio_lora_pkt_params_t pkt = { + .preamble_len_in_symb = data->modem_cfg.preamble_len, + .pkt_mode = LR20XX_RADIO_LORA_PKT_EXPLICIT, + .pld_len_in_bytes = (uint8_t)data_len, + .crc = data->modem_cfg.packet_crc_disable + ? LR20XX_RADIO_LORA_CRC_DISABLED + : LR20XX_RADIO_LORA_CRC_ENABLED, + .iq = data->modem_cfg.iq_inverted + ? LR20XX_RADIO_LORA_IQ_INVERTED + : LR20XX_RADIO_LORA_IQ_STANDARD, + }; + lr20xx_radio_lora_set_packet_params(ctx, &pkt); + + /* Write to TX FIFO */ + lr20xx_radio_fifo_write_tx(ctx, buf, (uint16_t)data_len); + + lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK); + lr20xx_hal_enable_dio1_irq(&data->hal_ctx); + + data->tx_signal = async; + data->tx_active = true; + lr20xx_radio_common_set_tx(ctx, 5000); + + k_mutex_unlock(&data->spi_mutex); + + LOG_DBG("TX started: len=%u", data_len); + return 0; +} + +/* ── Driver API: send (sync) ────────────────────────────────────────── */ + +static int lr20xx_lora_send(const struct device *dev, + uint8_t *buf, uint32_t data_len) +{ + struct k_poll_signal done = K_POLL_SIGNAL_INITIALIZER(done); + struct k_poll_event evt = K_POLL_EVENT_INITIALIZER( + K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &done); + + int ret = lr20xx_lora_send_async(dev, buf, data_len, &done); + if (ret < 0) return ret; + + uint32_t air_time = lr20xx_lora_airtime(dev, data_len); + ret = k_poll(&evt, 1, K_MSEC(2 * air_time + 1000)); + if (ret < 0) { + LOG_ERR("TX sync timeout"); + return ret; + } + + return 0; +} + +/* ── Driver API: recv_async ─────────────────────────────────────────── */ + +static int lr20xx_lora_recv_async(const struct device *dev, + lora_recv_cb cb, void *user_data) +{ + struct lr20xx_data *data = dev->data; + const struct lr20xx_config *cfg = dev->config; + + if (cb == NULL) { + k_mutex_lock(&data->spi_mutex, K_FOREVER); + data->async_rx_cb = NULL; + data->async_rx_user_data = NULL; + data->in_rx_mode = false; + k_mutex_unlock(&data->spi_mutex); + return 0; + } + + if (!data->configured) return -EINVAL; + + k_mutex_lock(&data->spi_mutex, K_FOREVER); + + data->async_rx_cb = cb; + data->async_rx_user_data = user_data; + + lr20xx_start_rx(data, cfg); + + k_mutex_unlock(&data->spi_mutex); + + LOG_INF("recv_async started (continuous RX%s)", + data->rx_boost_enabled ? ", boosted" : ""); + + return 0; +} + +/* ── Driver API: recv (sync) ────────────────────────────────────────── */ + +static int lr20xx_lora_recv(const struct device *dev, uint8_t *buf, + uint8_t size, k_timeout_t timeout, + int16_t *rssi, int8_t *snr) +{ + ARG_UNUSED(dev); + ARG_UNUSED(buf); + ARG_UNUSED(size); + ARG_UNUSED(timeout); + ARG_UNUSED(rssi); + ARG_UNUSED(snr); + return -ENOTSUP; +} + +/* ── LR20xx extension API ───────────────────────────────────────────── */ + +int16_t lr20xx_get_rssi_inst(const struct device *dev) +{ + struct lr20xx_data *data = dev->data; + int16_t rssi = 0; + uint8_t half_dbm = 0; + + k_mutex_lock(&data->spi_mutex, K_FOREVER); + lr20xx_radio_common_get_rssi_inst(&data->hal_ctx, &rssi, &half_dbm); + k_mutex_unlock(&data->spi_mutex); + + return rssi; +} + +bool lr20xx_is_receiving(const struct device *dev) +{ + struct lr20xx_data *data = dev->data; + + /* LR20xx has no non-destructive IRQ read (only get_and_clear). + * Consuming bits here races with the DIO1 work handler — RX_DONE + * could be cleared before the handler delivers the packet. + * Use radio state flags as proxy: radio is "receiving" if in + * continuous RX, not in TX, and DIO1 is not asserted (asserted + * means a completion event is pending, not a mid-preamble state). */ + if (!data->in_rx_mode || data->tx_active) { + return false; + } + + return !gpio_pin_get_dt(&data->hal_ctx.dio1); +} + +void lr20xx_set_rx_duty_cycle(const struct device *dev, bool enable) +{ + struct lr20xx_data *data = dev->data; + + data->rx_duty_cycle_enabled = enable; + LOG_INF("RX duty cycle %s", enable ? "enabled" : "disabled"); +} + +void lr20xx_set_rx_boost(const struct device *dev, bool enable) +{ + struct lr20xx_data *data = dev->data; + + if (data->rx_boost_enabled == enable) { + return; + } + + data->rx_boost_enabled = enable; + LOG_INF("RX boost %s", enable ? "enabled" : "disabled"); + + if (data->in_rx_mode && data->configured) { + k_mutex_lock(&data->spi_mutex, K_FOREVER); + lr20xx_radio_common_set_rx_path( + &data->hal_ctx, LR20XX_RADIO_COMMON_RX_PATH_LF, + enable ? LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_4 + : LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_NONE); + data->rx_boost_applied = enable; + k_mutex_unlock(&data->spi_mutex); + } else { + data->rx_boost_applied = false; + } +} + +uint32_t lr20xx_get_random(const struct device *dev) +{ + struct lr20xx_data *data = dev->data; + uint32_t random = 0; + + k_mutex_lock(&data->spi_mutex, K_FOREVER); + lr20xx_system_get_random_number( + &data->hal_ctx, + LR20XX_SYSTEM_RANDOM_ENTROPY_SOURCE_PLL | + LR20XX_SYSTEM_RANDOM_ENTROPY_SOURCE_ADC, + &random); + k_mutex_unlock(&data->spi_mutex); + + return random; +} + +void lr20xx_reset_agc(const struct device *dev) +{ + struct lr20xx_data *data = dev->data; + void *ctx = &data->hal_ctx; + + k_mutex_lock(&data->spi_mutex, K_FOREVER); + + /* Warm sleep — powers down analog frontend (resets AGC gain state). + * is_ram_retention_enabled=true = warm sleep (equivalent to LR11xx warm_start). */ + lr20xx_system_sleep_cfg_t sleep_cfg = { + .is_clk_32k_enabled = false, + .is_ram_retention_enabled = true, + }; + lr20xx_system_set_sleep_mode(ctx, &sleep_cfg, 0); + k_sleep(K_USEC(500)); + + lr20xx_system_set_standby_mode(ctx, LR20XX_SYSTEM_STANDBY_MODE_RC); + + lr20xx_system_calibrate(ctx, 0x6F); + + if (data->configured) { + lr20xx_radio_common_front_end_calibration_value_t cal = { + .rx_path = LR20XX_RADIO_COMMON_RX_PATH_LF, + .frequency_in_hertz = data->modem_cfg.frequency, + }; + lr20xx_radio_common_calibrate_front_end_helper(ctx, &cal, 1); + } + + if (data->rx_boost_enabled) { + lr20xx_radio_common_set_rx_path( + ctx, LR20XX_RADIO_COMMON_RX_PATH_LF, + LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_4); + data->rx_boost_applied = true; + } + + k_mutex_unlock(&data->spi_mutex); +} + +/* ── Deferred hardware init ─────────────────────────────────────────── */ + +static int lr20xx_hw_init(struct lr20xx_data *data, + const struct lr20xx_config *cfg) +{ + void *ctx = &data->hal_ctx; + + LOG_INF("LR20xx hardware init starting"); + + lr20xx_system_version_t ver; + bool found = false; + + for (int attempt = 0; attempt < 3; attempt++) { + lr20xx_hal_status_t hal_rc = lr20xx_hal_reset(ctx); + if (hal_rc != LR20XX_HAL_STATUS_OK) { + LOG_WRN("LR20xx reset failed (attempt %d)", attempt); + k_msleep(10); + continue; + } + + lr20xx_status_t st = lr20xx_system_get_version(ctx, &ver); + if (st == LR20XX_STATUS_OK) { + found = true; + break; + } + + LOG_WRN("LR20xx get_version failed (attempt %d)", attempt); + k_msleep(10); + } + + if (!found) { + LOG_ERR("LR20xx not found after 3 attempts"); + return -EIO; + } + + LOG_INF("LR20xx HW:0x%02X FW:0x%04X", ver.hw, ver.fw); + + if (cfg->tcxo_voltage_mv > 0) { + /* Convert ms to RTC steps (31.25 us per step) */ + uint32_t rtc_steps = (cfg->tcxo_startup_delay_ms * 1000) / 31; + lr20xx_system_set_tcxo_mode(ctx, + get_tcxo_voltage(cfg->tcxo_voltage_mv), + rtc_steps); + LOG_DBG("TCXO: %dmV", cfg->tcxo_voltage_mv); + } + + lr20xx_system_set_reg_mode(ctx, LR20XX_SYSTEM_REG_MODE_DCDC); + + lr20xx_configure_rfswitch(ctx, cfg); + + LOG_INF("RF switch: en=0x%02x rx=0x%02x tx=0x%02x txhp=0x%02x", + cfg->rfswitch_enable, cfg->rfswitch_rx, + cfg->rfswitch_tx, cfg->rfswitch_tx_hp); + + /* Calibrate all 7 blocks (LF_RC=1, HF_RC=2, PLL=4, AAF=8, MU=32, PA_OFF=64) */ + lr20xx_system_calibrate(ctx, 0x6F); + LOG_INF("Calibration OK"); + + lr20xx_radio_common_set_rx_tx_fallback_mode(ctx, + LR20XX_RADIO_FALLBACK_STDBY_RC); + + lr20xx_radio_common_set_pkt_type(ctx, LR20XX_RADIO_COMMON_PKT_TYPE_LORA); + + lr20xx_system_errors_t sys_errors = 0; + lr20xx_system_get_errors(ctx, &sys_errors); + if (sys_errors) { + LOG_WRN("System errors at init: 0x%04x — clearing", sys_errors); + } + lr20xx_system_clear_errors(ctx); + lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK); + + lr20xx_hal_enable_dio1_irq(&data->hal_ctx); + + data->rx_boost_enabled = cfg->rx_boosted; + data->rx_boost_applied = false; + + data->hw_initialized = true; + LOG_INF("LR20xx driver ready"); + return 0; +} + +/* ── Driver init (lightweight — runs at POST_KERNEL) ────────────────── */ + +static int lr20xx_lora_init(const struct device *dev) +{ + struct lr20xx_data *data = dev->data; + const struct lr20xx_config *cfg = dev->config; + int ret; + + data->dev = dev; + data->hw_initialized = false; + + k_mutex_init(&data->spi_mutex); + k_work_init(&data->dio1_work, lr20xx_dio1_work_handler); + + k_work_queue_start(&data->dio1_wq, lr20xx_dio1_wq_stack, + K_THREAD_STACK_SIZEOF(lr20xx_dio1_wq_stack), + K_PRIO_COOP(7), NULL); + k_thread_name_set(&data->dio1_wq.thread, "lr20xx_dio1"); + + if (!spi_is_ready_dt(&cfg->bus)) { + LOG_ERR("SPI bus not ready"); + return -ENODEV; + } + + memset(&data->hal_ctx, 0, sizeof(data->hal_ctx)); + data->hal_ctx.spi_dev = cfg->bus.bus; + data->hal_ctx.spi_cfg = cfg->bus.config; + /* Manual NSS control — disable SPI peripheral CS */ + data->hal_ctx.spi_cfg.cs.cs_is_gpio = false; + data->hal_ctx.spi_cfg.cs.gpio.port = NULL; + data->hal_ctx.nss.port = cfg->bus.config.cs.gpio.port; + data->hal_ctx.nss.pin = cfg->bus.config.cs.gpio.pin; + data->hal_ctx.nss.dt_flags = cfg->bus.config.cs.gpio.dt_flags; + data->hal_ctx.reset = cfg->reset; + data->hal_ctx.busy = cfg->busy; + data->hal_ctx.dio1 = cfg->dio1; + data->hal_ctx.radio_is_sleeping = false; + + ret = lr20xx_hal_init(&data->hal_ctx); + if (ret != 0) { + LOG_ERR("HAL init failed: %d", ret); + return ret; + } + + lr20xx_hal_set_dio1_callback(&data->hal_ctx, lr20xx_dio1_callback, + data); + + LOG_INF("LR20xx driver registered (hw init deferred to first config)"); + return 0; +} + +/* ── Device instantiation ───────────────────────────────────────────── */ + +static DEVICE_API(lora, lr20xx_lora_api) = { + .config = lr20xx_lora_config, + .airtime = lr20xx_lora_airtime, + .send = lr20xx_lora_send, + .send_async = lr20xx_lora_send_async, + .recv = lr20xx_lora_recv, + .recv_async = lr20xx_lora_recv_async, +}; + +#define LR20XX_INIT(n) \ + static const struct lr20xx_config lr20xx_config_##n = { \ + .bus = SPI_DT_SPEC_INST_GET(n, \ + SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | \ + SPI_TRANSFER_MSB), \ + .reset = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \ + .busy = GPIO_DT_SPEC_INST_GET(n, busy_gpios), \ + .dio1 = GPIO_DT_SPEC_INST_GET(n, dio1_gpios), \ + .tcxo_voltage_mv = \ + DT_INST_PROP_OR(n, tcxo_voltage_mv, 0), \ + .tcxo_startup_delay_ms = \ + DT_INST_PROP_OR(n, tcxo_startup_delay_ms, 5), \ + .rx_boosted = DT_INST_PROP(n, rx_boosted), \ + .rfswitch_enable = DT_INST_PROP_OR(n, rfswitch_enable, 0), \ + .rfswitch_standby = DT_INST_PROP_OR(n, rfswitch_standby, 0),\ + .rfswitch_rx = DT_INST_PROP_OR(n, rfswitch_rx, 0), \ + .rfswitch_tx = DT_INST_PROP_OR(n, rfswitch_tx, 0), \ + .rfswitch_tx_hp = DT_INST_PROP_OR(n, rfswitch_tx_hp, 0), \ + .pa_hp_sel = DT_INST_PROP_OR(n, pa_hp_sel, 7), \ + .pa_duty_cycle = DT_INST_PROP_OR(n, pa_duty_cycle, 4), \ + }; \ + static struct lr20xx_data lr20xx_data_##n; \ + DEVICE_DT_INST_DEFINE(n, lr20xx_lora_init, NULL, \ + &lr20xx_data_##n, &lr20xx_config_##n, \ + POST_KERNEL, CONFIG_LORA_INIT_PRIORITY, \ + &lr20xx_lora_api); + +DT_INST_FOREACH_STATUS_OKAY(LR20XX_INIT) diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.h b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.h new file mode 100644 index 0000000..96b08f7 --- /dev/null +++ b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.h @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * LR20xx Zephyr LoRa driver — extension API + * + * Functions extending the standard Zephyr lora_driver_api with + * LR20xx-specific features (RX boost, RSSI readout, preamble detection, + * duty cycle, AGC reset). + */ + +#ifndef LR20XX_LORA_H +#define LR20XX_LORA_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get instantaneous RSSI (for noise floor calibration) + * + * @param dev LoRa device + * @return RSSI in dBm, or -128 on error + */ +int16_t lr20xx_get_rssi_inst(const struct device *dev); + +/** + * @brief Check if radio is actively receiving a packet + * + * Checks IRQ status for preamble/sync word detection. + * Uses non-blocking mutex — returns false if SPI is busy. + * + * @param dev LoRa device + * @return true if preamble or sync word detected + */ +bool lr20xx_is_receiving(const struct device *dev); + +/** + * @brief Enable/disable RX duty cycle mode + * + * When enabled, radio alternates between RX and sleep. + * Takes effect on the next RX start. + * + * @param dev LoRa device + * @param enable true to enable, false for continuous RX + */ +void lr20xx_set_rx_duty_cycle(const struct device *dev, bool enable); + +/** + * @brief Enable/disable RX boosted mode + * + * @param dev LoRa device + * @param enable true to enable boost + */ +void lr20xx_set_rx_boost(const struct device *dev, bool enable); + +/** + * @brief Get a random number from the radio hardware RNG + * + * @param dev LoRa device + * @return Random 32-bit value + */ +uint32_t lr20xx_get_random(const struct device *dev); + +/** + * @brief Reset AGC by performing warm sleep + full recalibration + * + * @param dev LoRa device + */ +void lr20xx_reset_agc(const struct device *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* LR20XX_LORA_H */ diff --git a/zephcore/patches/zephyr-new/dts/bindings/lora/semtech,lr2021.yaml b/zephcore/patches/zephyr-new/dts/bindings/lora/semtech,lr2021.yaml new file mode 100644 index 0000000..393229a --- /dev/null +++ b/zephcore/patches/zephyr-new/dts/bindings/lora/semtech,lr2021.yaml @@ -0,0 +1,92 @@ +# Copyright (c) 2025 ZephCore +# SPDX-License-Identifier: Apache-2.0 + +description: | + Semtech LR2021 LoRa Plus transceiver (4th-gen LoRa IP). + Uses native Semtech lr20xx_driver SDK with Zephyr LoRa driver API. + Supports sub-GHz + 2.4 GHz ISM + NTN/SATCOM. + +compatible: "semtech,lr2021" + +include: spi-device.yaml + +properties: + reset-gpios: + type: phandle-array + required: true + description: GPIO connected to NRESET (active-low). + + busy-gpios: + type: phandle-array + required: true + description: GPIO connected to BUSY (high = busy). + + dio1-gpios: + type: phandle-array + required: true + description: GPIO connected to DIO5/IRQ line (active-high, pull-down). + + tcxo-voltage-mv: + type: int + default: 0 + description: | + TCXO supply voltage in millivolts (provided by LR2021 DIO3). + 0 = no TCXO (use XTAL). Common: 1800 (1.8V), 3300 (3.3V). + + tcxo-startup-delay-ms: + type: int + default: 5 + description: TCXO startup stabilization time in milliseconds. + + rx-boosted: + type: boolean + description: Enable RX boosted mode for better sensitivity (+~3dB, +~2mA). + + rfswitch-enable: + type: int + default: 0 + description: | + RF switch DIO enable bitmask (bit 0 = DIO5, bit 1 = DIO6, ...). + Set a bit to include that DIO in RF switch control. + + rfswitch-standby: + type: int + default: 0 + description: | + Bitmask of RF switch DIOs that are HIGH in standby mode. + Bit mapping matches rfswitch-enable. + + rfswitch-rx: + type: int + default: 0 + description: | + Bitmask of RF switch DIOs that are HIGH in RX mode (both LF and HF). + Bit mapping matches rfswitch-enable. + + rfswitch-tx: + type: int + default: 0 + description: | + Bitmask of RF switch DIOs that are HIGH in sub-GHz TX mode (LF PA). + Bit mapping matches rfswitch-enable. + + rfswitch-tx-hp: + type: int + default: 0 + description: | + Bitmask of RF switch DIOs that are HIGH in 2.4 GHz / high-power TX mode (HF PA). + Bit mapping matches rfswitch-enable. + + pa-hp-sel: + type: int + default: 7 + description: | + LF PA number of slices (0-7). Maps to pa_lf_slices in the SDK. + Higher = more PA output slices active = more power. Default 7 = maximum. + + pa-duty-cycle: + type: int + default: 4 + description: | + LF PA duty cycle (0-7). Maps to pa_lf_duty_cycle in the SDK. + Default 4 is suitable for most sub-GHz applications. diff --git a/zephcore/patches/zephyr/0002-lora-lr20xx-build.patch b/zephcore/patches/zephyr/0002-lora-lr20xx-build.patch new file mode 100644 index 0000000..6fd782d --- /dev/null +++ b/zephcore/patches/zephyr/0002-lora-lr20xx-build.patch @@ -0,0 +1,37 @@ +diff --git a/drivers/lora/CMakeLists.txt b/drivers/lora/CMakeLists.txt +index 9cd035fcf2b..18d04ec099a 100644 +--- a/drivers/lora/CMakeLists.txt ++++ b/drivers/lora/CMakeLists.txt +@@ -1,7 +1,11 @@ + # SPDX-License-Identifier: Apache-2.0 ++# ZephCore patch: adds lr11xx driver subdirectory. + + zephyr_sources_ifdef(CONFIG_LORA_SHELL shell.c) + ++add_subdirectory_ifdef(CONFIG_LORA_LR11XX lr11xx) ++add_subdirectory_ifdef(CONFIG_LORA_LR20XX lr20xx) ++ + # zephyr-keep-sorted-start + add_subdirectory_ifdef(CONFIG_LORA_MODULE_BACKEND_LORAMAC_NODE loramac-node) + add_subdirectory_ifdef(CONFIG_LORA_MODULE_BACKEND_LORA_BASICS_MODEM lora-basics-modem) +diff --git a/drivers/lora/Kconfig b/drivers/lora/Kconfig +index 657bdb9ce28..f8513b37044 100644 +--- a/drivers/lora/Kconfig ++++ b/drivers/lora/Kconfig +@@ -5,6 +5,7 @@ + # + + # Top-level configuration file for LORA drivers. ++# ZephCore patch: adds lr11xx driver Kconfig. + + menuconfig LORA + bool "LoRa drivers" +@@ -60,6 +61,8 @@ config LORA_INIT_PRIORITY + + rsource "Kconfig.sx12xx" + rsource "Kconfig.rylrxxx" ++rsource "lr11xx/Kconfig" ++rsource "lr20xx/Kconfig" + rsource "lora-basics-modem/Kconfig" + rsource "native/Kconfig" +