From 59b7cd179269f810623f317861a947cf2f4a381e Mon Sep 17 00:00:00 2001 From: Matthieu Antoine Date: Thu, 24 Sep 2020 15:22:41 +0200 Subject: [PATCH] Release v1.0.0 --- CHANGELOG.md | 11 + LICENSE | 24 + README.md | 21 + src/CMakeLists.txt | 34 + src/llcc68.c | 1500 +++++++++++++++++++++++++++++++++++++++++++ src/llcc68.h | 1521 ++++++++++++++++++++++++++++++++++++++++++++ src/llcc68_hal.h | 133 ++++ src/llcc68_regs.h | 200 ++++++ 8 files changed, 3444 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/CMakeLists.txt create mode 100644 src/llcc68.c create mode 100644 src/llcc68.h create mode 100644 src/llcc68_hal.h create mode 100644 src/llcc68_regs.h diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6025c7a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2020-09-24 + +### General + +- Initial version diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..02bc1c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Revised BSD License +Copyright Semtech Corporation 2020. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted 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. + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d14f36a --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# LLCC68 driver + +This package proposes an implementation in C of the driver for **LLCC68** radio component. + +## Structure + +The driver is defined as follows: + +- llcc68.c: implementation of the driver functions +- llcc68.h: declarations of the driver functions +- llcc68_regs.h: definitions of all useful registers (address and fields) +- llcc68_hal.h: declarations of the HAL functions (to be implemented by the user - see below) + +## HAL + +The HAL (Hardware Abstraction Layer) is a collection of functions the user shall implement to write platform-dependant calls to the host. The list of functions is the following: + +- llcc68_hal_reset +- llcc68_hal_wakeup +- llcc68_hal_write +- llcc68_hal_read diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..fcbb8fc --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,34 @@ +## +## ______ _ +## / _____) _ | | +## ( (____ _____ ____ _| |_ _____ ____| |__ +## \____ \| ___ | (_ _) ___ |/ ___) _ \ +## _____) ) ____| | | || |_| ____( (___| | | | +## (______/|_____)_|_|_| \__)_____)\____)_| |_| +## (C)2019 Semtech +## +## License: Revised BSD License, see LICENSE.TXT file included in the project +## +## Authors: Semtech WSP Applications Team +## +project(llcc68_driver VERSION 0.9.0 DESCRIPTION "LLCC68 driver") +cmake_minimum_required(VERSION 3.6) + +#--------------------------------------------------------------------------------------- +# Target +#--------------------------------------------------------------------------------------- + +list(APPEND ${PROJECT_NAME}_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/llcc68.c +) + +add_library(${PROJECT_NAME} OBJECT EXCLUDE_FROM_ALL ${${PROJECT_NAME}_SOURCES}) + +# For debug builds set the symbol DEBUG +set(CMAKE_C_FLAGS_DEBUG -DDEBUG) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) diff --git a/src/llcc68.c b/src/llcc68.c new file mode 100644 index 0000000..f6906cc --- /dev/null +++ b/src/llcc68.c @@ -0,0 +1,1500 @@ +/** + * @file llcc68.c + * + * @brief LLCC68 radio driver implementation + * + * Revised BSD License + * Copyright Semtech Corporation 2020. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted 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. + * + * 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 // memcpy +#include "llcc68.h" +#include "llcc68_hal.h" +#include "llcc68_regs.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/** + * @brief Internal frequency of the radio + */ +#define LLCC68_XTAL_FREQ 32000000UL + +/** + * @brief Internal frequency of the radio + */ +#define LLCC68_RTC_FREQ_IN_HZ 64000UL + +/** + * @brief Scaling factor used to perform fixed-point operations + */ +#define LLCC68_PLL_STEP_SHIFT_AMOUNT ( 14 ) + +/** + * @brief PLL step - scaled with LLCC68_PLL_STEP_SHIFT_AMOUNT + */ +#define LLCC68_PLL_STEP_SCALED ( LLCC68_XTAL_FREQ >> ( 25 - LLCC68_PLL_STEP_SHIFT_AMOUNT ) ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/** + * Commands Interface + */ +typedef enum llcc68_commands_e +{ + // Operational Modes Functions + LLCC68_SET_SLEEP = 0x84, + LLCC68_SET_STANDBY = 0x80, + LLCC68_SET_FS = 0xC1, + LLCC68_SET_TX = 0x83, + LLCC68_SET_RX = 0x82, + LLCC68_SET_STOP_TIMER_ON_PREAMBLE = 0x9F, + LLCC68_SET_RX_DUTY_CYCLE = 0x94, + LLCC68_SET_CAD = 0xC5, + LLCC68_SET_TX_CONTINUOUS_WAVE = 0xD1, + LLCC68_SET_TX_INFINITE_PREAMBLE = 0xD2, + LLCC68_SET_REGULATOR_MODE = 0x96, + LLCC68_CALIBRATE = 0x89, + LLCC68_CALIBRATE_IMAGE = 0x98, + LLCC68_SET_PA_CFG = 0x95, + LLCC68_SET_RX_TX_FALLBACK_MODE = 0x93, + // Registers and buffer Access + LLCC68_WRITE_REGISTER = 0x0D, + LLCC68_READ_REGISTER = 0x1D, + LLCC68_WRITE_BUFFER = 0x0E, + LLCC68_READ_BUFFER = 0x1E, + // DIO and IRQ Control Functions + LLCC68_SET_DIO_IRQ_PARAMS = 0x08, + LLCC68_GET_IRQ_STATUS = 0x12, + LLCC68_CLR_IRQ_STATUS = 0x02, + LLCC68_SET_DIO2_AS_RF_SWITCH_CTRL = 0x9D, + LLCC68_SET_DIO3_AS_TCXO_CTRL = 0x97, + // RF Modulation and Packet-Related Functions + LLCC68_SET_RF_FREQUENCY = 0x86, + LLCC68_SET_PKT_TYPE = 0x8A, + LLCC68_GET_PKT_TYPE = 0x11, + LLCC68_SET_TX_PARAMS = 0x8E, + LLCC68_SET_MODULATION_PARAMS = 0x8B, + LLCC68_SET_PKT_PARAMS = 0x8C, + LLCC68_SET_CAD_PARAMS = 0x88, + LLCC68_SET_BUFFER_BASE_ADDRESS = 0x8F, + LLCC68_SET_LORA_SYMB_NUM_TIMEOUT = 0xA0, + // Communication Status Information + LLCC68_GET_STATUS = 0xC0, + LLCC68_GET_RX_BUFFER_STATUS = 0x13, + LLCC68_GET_PKT_STATUS = 0x14, + LLCC68_GET_RSSI_INST = 0x15, + LLCC68_GET_STATS = 0x10, + LLCC68_RESET_STATS = 0x00, + // Miscellaneous + LLCC68_GET_DEVICE_ERRORS = 0x17, + LLCC68_CLR_DEVICE_ERRORS = 0x07, +} llcc68_commands_t; + +/** + * Commands Interface buffer sizes + */ +typedef enum llcc68_commands_size_e +{ + // Operational Modes Functions + LLCC68_SIZE_SET_SLEEP = 2, + LLCC68_SIZE_SET_STANDBY = 2, + LLCC68_SIZE_SET_FS = 1, + LLCC68_SIZE_SET_TX = 4, + LLCC68_SIZE_SET_RX = 4, + LLCC68_SIZE_SET_STOP_TIMER_ON_PREAMBLE = 2, + LLCC68_SIZE_SET_RX_DUTY_CYCLE = 7, + LLCC68_SIZE_SET_CAD = 1, + LLCC68_SIZE_SET_TX_CONTINUOUS_WAVE = 1, + LLCC68_SIZE_SET_TX_INFINITE_PREAMBLE = 1, + LLCC68_SIZE_SET_REGULATOR_MODE = 2, + LLCC68_SIZE_CALIBRATE = 2, + LLCC68_SIZE_CALIBRATE_IMAGE = 3, + LLCC68_SIZE_SET_PA_CFG = 5, + LLCC68_SIZE_SET_RX_TX_FALLBACK_MODE = 2, + // Registers and buffer Access + // Full size: this value plus buffer size + LLCC68_SIZE_WRITE_REGISTER = 3, + // Full size: this value plus buffer size + LLCC68_SIZE_READ_REGISTER = 4, + // Full size: this value plus buffer size + LLCC68_SIZE_WRITE_BUFFER = 2, + // Full size: this value plus buffer size + LLCC68_SIZE_READ_BUFFER = 3, + // DIO and IRQ Control Functions + LLCC68_SIZE_SET_DIO_IRQ_PARAMS = 9, + LLCC68_SIZE_GET_IRQ_STATUS = 2, + LLCC68_SIZE_CLR_IRQ_STATUS = 3, + LLCC68_SIZE_SET_DIO2_AS_RF_SWITCH_CTRL = 2, + LLCC68_SIZE_SET_DIO3_AS_TCXO_CTRL = 5, + // RF Modulation and Packet-Related Functions + LLCC68_SIZE_SET_RF_FREQUENCY = 5, + LLCC68_SIZE_SET_PKT_TYPE = 2, + LLCC68_SIZE_GET_PKT_TYPE = 3, + LLCC68_SIZE_SET_TX_PARAMS = 3, + LLCC68_SIZE_SET_MODULATION_PARAMS_GFSK = 9, + LLCC68_SIZE_SET_MODULATION_PARAMS_LORA = 5, + LLCC68_SIZE_SET_PKT_PARAMS_GFSK = 10, + LLCC68_SIZE_SET_PKT_PARAMS_LORA = 7, + LLCC68_SIZE_SET_CAD_PARAMS = 8, + LLCC68_SIZE_SET_BUFFER_BASE_ADDRESS = 3, + LLCC68_SIZE_SET_LORA_SYMB_NUM_TIMEOUT = 2, + // Communication Status Information + LLCC68_SIZE_GET_STATUS = 1, + LLCC68_SIZE_GET_RX_BUFFER_STATUS = 2, + LLCC68_SIZE_GET_PKT_STATUS = 2, + LLCC68_SIZE_GET_RSSI_INST = 2, + LLCC68_SIZE_GET_STATS = 2, + LLCC68_SIZE_RESET_STATS = 7, + // Miscellaneous + LLCC68_SIZE_GET_DEVICE_ERRORS = 2, + LLCC68_SIZE_CLR_DEVICE_ERRORS = 3, + LLCC68_SIZE_MAX_BUFFER = 255, + LLCC68_SIZE_DUMMY_BYTE = 1, +} llcc68_commands_size_t; + +typedef struct +{ + uint32_t bw; + uint8_t param; +} gfsk_bw_t; + +gfsk_bw_t gfsk_bw[] = { + { 4800, LLCC68_GFSK_BW_4800 }, { 5800, LLCC68_GFSK_BW_5800 }, { 7300, LLCC68_GFSK_BW_7300 }, + { 9700, LLCC68_GFSK_BW_9700 }, { 11700, LLCC68_GFSK_BW_11700 }, { 14600, LLCC68_GFSK_BW_14600 }, + { 19500, LLCC68_GFSK_BW_19500 }, { 23400, LLCC68_GFSK_BW_23400 }, { 29300, LLCC68_GFSK_BW_29300 }, + { 39000, LLCC68_GFSK_BW_39000 }, { 46900, LLCC68_GFSK_BW_46900 }, { 58600, LLCC68_GFSK_BW_58600 }, + { 78200, LLCC68_GFSK_BW_78200 }, { 93800, LLCC68_GFSK_BW_93800 }, { 117300, LLCC68_GFSK_BW_117300 }, + { 156200, LLCC68_GFSK_BW_156200 }, { 187200, LLCC68_GFSK_BW_187200 }, { 234300, LLCC68_GFSK_BW_234300 }, + { 312000, LLCC68_GFSK_BW_312000 }, { 373600, LLCC68_GFSK_BW_373600 }, { 467000, LLCC68_GFSK_BW_467000 }, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/** + * @brief 15.1.2 Workaround + * + * @remark Before any packet transmission, bit #2 of LLCC68_REG_TX_MODULATION shall be set to: + * 0 if the LoRa BW = 500 kHz + * 1 for any other LoRa BW + * 1 for any (G)FSK configuration + * + * @param [in] context Chip implementation context. + * @param [in] pkt_type The modulation type (G)FSK/LoRa + * @param [in] bw In case of LoRa modulation the bandwith must be specified + * + * @returns Operation status + */ +static llcc68_status_t llcc68_tx_modulation_workaround( const void* context, llcc68_pkt_type_t pkt_type, + llcc68_lora_bw_t bw ); + +static inline uint32_t llcc68_get_gfsk_crc_len_in_bytes( llcc68_gfsk_crc_types_t crc_type ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +llcc68_status_t llcc68_set_sleep( const void* context, const llcc68_sleep_cfgs_t cfg ) +{ + uint8_t buf[LLCC68_SIZE_SET_SLEEP] = { 0 }; + + buf[0] = LLCC68_SET_SLEEP; + + buf[1] = ( uint8_t ) cfg; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_SLEEP, 0, 0 ); +} + +llcc68_status_t llcc68_set_standby( const void* context, const llcc68_standby_cfg_t cfg ) +{ + uint8_t buf[LLCC68_SIZE_SET_STANDBY] = { 0 }; + + buf[0] = LLCC68_SET_STANDBY; + + buf[1] = ( uint8_t ) cfg; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_STANDBY, 0, 0 ); +} + +llcc68_status_t llcc68_set_fs( const void* context ) +{ + uint8_t buf[LLCC68_SIZE_SET_FS] = { 0 }; + + buf[0] = LLCC68_SET_FS; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_FS, 0, 0 ); +} + +llcc68_status_t llcc68_set_tx( const void* context, const uint32_t timeout_in_ms ) +{ + if( timeout_in_ms > LLCC68_MAX_TIMEOUT_IN_MS ) + { + return LLCC68_STATUS_UNKNOWN_VALUE; + } + + const uint32_t timeout_in_rtc_step = llcc68_convert_timeout_in_ms_to_rtc_step( timeout_in_ms ); + + return llcc68_set_tx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +llcc68_status_t llcc68_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ) +{ + uint8_t buf[LLCC68_SIZE_SET_TX] = { 0 }; + + buf[0] = LLCC68_SET_TX; + + buf[1] = ( uint8_t )( timeout_in_rtc_step >> 16 ); + buf[2] = ( uint8_t )( timeout_in_rtc_step >> 8 ); + buf[3] = ( uint8_t )( timeout_in_rtc_step >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_TX, 0, 0 ); +} + +llcc68_status_t llcc68_set_rx( const void* context, const uint32_t timeout_in_ms ) +{ + if( timeout_in_ms > LLCC68_MAX_TIMEOUT_IN_MS ) + { + return LLCC68_STATUS_UNKNOWN_VALUE; + } + + const uint32_t timeout_in_rtc_step = llcc68_convert_timeout_in_ms_to_rtc_step( timeout_in_ms ); + + return llcc68_set_rx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +llcc68_status_t llcc68_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ) +{ + uint8_t buf[LLCC68_SIZE_SET_RX] = { 0 }; + + buf[0] = LLCC68_SET_RX; + + buf[1] = ( uint8_t )( timeout_in_rtc_step >> 16 ); + buf[2] = ( uint8_t )( timeout_in_rtc_step >> 8 ); + buf[3] = ( uint8_t )( timeout_in_rtc_step >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_RX, 0, 0 ); +} + +llcc68_status_t llcc68_stop_timer_on_preamble( const void* context, const bool enable ) +{ + uint8_t buf[LLCC68_SIZE_SET_STOP_TIMER_ON_PREAMBLE] = { 0 }; + + buf[0] = LLCC68_SET_STOP_TIMER_ON_PREAMBLE; + + buf[1] = ( enable == true ) ? 1 : 0; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_STOP_TIMER_ON_PREAMBLE, 0, 0 ); +} + +llcc68_status_t llcc68_set_rx_duty_cycle( const void* context, const uint32_t rx_time_in_ms, + const uint32_t sleep_time_in_ms ) +{ + const uint32_t rx_time_in_rtc_step = llcc68_convert_timeout_in_ms_to_rtc_step( rx_time_in_ms ); + const uint32_t sleep_time_in_rtc_step = llcc68_convert_timeout_in_ms_to_rtc_step( sleep_time_in_ms ); + + return llcc68_set_rx_duty_cycle_with_timings_in_rtc_step( context, rx_time_in_rtc_step, sleep_time_in_rtc_step ); +} + +llcc68_status_t llcc68_set_rx_duty_cycle_with_timings_in_rtc_step( const void* context, + const uint32_t rx_time_in_rtc_step, + const uint32_t sleep_time_in_rtc_step ) +{ + uint8_t buf[LLCC68_SIZE_SET_RX_DUTY_CYCLE] = { 0 }; + + buf[0] = LLCC68_SET_RX_DUTY_CYCLE; + + buf[1] = ( uint8_t )( rx_time_in_rtc_step >> 16 ); + buf[2] = ( uint8_t )( rx_time_in_rtc_step >> 8 ); + buf[3] = ( uint8_t )( rx_time_in_rtc_step >> 0 ); + + buf[4] = ( uint8_t )( sleep_time_in_rtc_step >> 16 ); + buf[5] = ( uint8_t )( sleep_time_in_rtc_step >> 8 ); + buf[6] = ( uint8_t )( sleep_time_in_rtc_step >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_RX_DUTY_CYCLE, 0, 0 ); +} + +llcc68_status_t llcc68_set_cad( const void* context ) +{ + uint8_t buf[LLCC68_SIZE_SET_CAD] = { 0 }; + + buf[0] = LLCC68_SET_CAD; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_CAD, 0, 0 ); +} + +llcc68_status_t llcc68_set_tx_cw( const void* context ) +{ + uint8_t buf[LLCC68_SIZE_SET_TX_CONTINUOUS_WAVE] = { 0 }; + + buf[0] = LLCC68_SET_TX_CONTINUOUS_WAVE; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_TX_CONTINUOUS_WAVE, 0, 0 ); +} + +llcc68_status_t llcc68_set_tx_infinite_preamble( const void* context ) +{ + uint8_t buf[LLCC68_SIZE_SET_TX_INFINITE_PREAMBLE] = { 0 }; + + buf[0] = LLCC68_SET_TX_INFINITE_PREAMBLE; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_TX_INFINITE_PREAMBLE, 0, 0 ); +} + +llcc68_status_t llcc68_set_reg_mode( const void* context, const llcc68_reg_mod_t mode ) +{ + uint8_t buf[LLCC68_SIZE_SET_REGULATOR_MODE] = { 0 }; + + buf[0] = LLCC68_SET_REGULATOR_MODE; + + buf[1] = ( uint8_t ) mode; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_REGULATOR_MODE, 0, 0 ); +} + +llcc68_status_t llcc68_cal( const void* context, const llcc68_cal_mask_t param ) +{ + uint8_t buf[LLCC68_SIZE_CALIBRATE] = { 0 }; + + buf[0] = LLCC68_CALIBRATE; + + buf[1] = param; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_CALIBRATE, 0, 0 ); +} + +llcc68_status_t llcc68_cal_img( const void* context, const uint32_t freq_in_hz ) +{ + uint8_t buf[LLCC68_SIZE_CALIBRATE_IMAGE] = { 0 }; + + buf[0] = LLCC68_CALIBRATE_IMAGE; + + if( freq_in_hz > 900000000 ) + { + buf[1] = 0xE1; + buf[2] = 0xE9; + } + else if( freq_in_hz > 850000000 ) + { + buf[1] = 0xD7; + buf[2] = 0xDB; + } + else if( freq_in_hz > 770000000 ) + { + buf[1] = 0xC1; + buf[2] = 0xC5; + } + else if( freq_in_hz > 460000000 ) + { + buf[1] = 0x75; + buf[2] = 0x81; + } + else + { + buf[1] = 0x6B; + buf[2] = 0x6F; + } + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_CALIBRATE_IMAGE, 0, 0 ); +} + +llcc68_status_t llcc68_set_pa_cfg( const void* context, const llcc68_pa_cfg_params_t* params ) +{ + uint8_t buf[LLCC68_SIZE_SET_PA_CFG] = { 0 }; + + buf[0] = LLCC68_SET_PA_CFG; + + buf[1] = params->pa_duty_cycle; + buf[2] = params->hp_max; + buf[3] = params->device_sel; + buf[4] = params->pa_lut; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_PA_CFG, 0, 0 ); +} + +llcc68_status_t llcc68_set_rx_tx_fallback_mode( const void* context, const llcc68_fallback_modes_t fallback_mode ) +{ + uint8_t buf[LLCC68_SIZE_SET_RX_TX_FALLBACK_MODE] = { 0 }; + + buf[0] = LLCC68_SET_RX_TX_FALLBACK_MODE; + + buf[1] = ( uint8_t ) fallback_mode; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_RX_TX_FALLBACK_MODE, 0, 0 ); +} + +// +// Registers and buffer Access +// + +llcc68_status_t llcc68_write_register( const void* context, const uint16_t address, const uint8_t* buffer, + const uint8_t size ) +{ + uint8_t buf[LLCC68_SIZE_WRITE_REGISTER] = { 0 }; + + buf[0] = LLCC68_WRITE_REGISTER; + + buf[1] = ( uint8_t )( address >> 8 ); + buf[2] = ( uint8_t )( address >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_WRITE_REGISTER, buffer, size ); +} + +llcc68_status_t llcc68_read_register( const void* context, const uint16_t address, uint8_t* buffer, const uint8_t size ) +{ + uint8_t buf[LLCC68_SIZE_READ_REGISTER] = { 0 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_READ_REGISTER; + + buf[1] = ( uint8_t )( address >> 8 ); + buf[2] = ( uint8_t )( address >> 0 ); + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_READ_REGISTER, buffer, size ); + + return status; +} + +llcc68_status_t llcc68_write_buffer( const void* context, const uint8_t offset, const uint8_t* buffer, + const uint8_t size ) +{ + uint8_t buf[LLCC68_SIZE_WRITE_BUFFER] = { 0 }; + + buf[0] = LLCC68_WRITE_BUFFER; + + buf[1] = offset; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_WRITE_BUFFER, buffer, size ); +} + +llcc68_status_t llcc68_read_buffer( const void* context, const uint8_t offset, uint8_t* buffer, const uint8_t size ) +{ + uint8_t buf[LLCC68_SIZE_READ_BUFFER] = { 0 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_READ_BUFFER; + + buf[1] = offset; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_READ_BUFFER, buffer, size ); + + return status; +} + +// +// DIO and IRQ Control Functions +// +llcc68_status_t llcc68_set_dio_irq_params( const void* context, const uint16_t irq_mask, const uint16_t dio1_mask, + const uint16_t dio2_mask, const uint16_t dio3_mask ) +{ + uint8_t buf[LLCC68_SIZE_SET_DIO_IRQ_PARAMS] = { 0 }; + + buf[0] = LLCC68_SET_DIO_IRQ_PARAMS; + + buf[1] = ( uint8_t )( irq_mask >> 8 ); + buf[2] = ( uint8_t )( irq_mask >> 0 ); + + buf[3] = ( uint8_t )( dio1_mask >> 8 ); + buf[4] = ( uint8_t )( dio1_mask >> 0 ); + + buf[5] = ( uint8_t )( dio2_mask >> 8 ); + buf[6] = ( uint8_t )( dio2_mask >> 0 ); + + buf[7] = ( uint8_t )( dio3_mask >> 8 ); + buf[8] = ( uint8_t )( dio3_mask >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_DIO_IRQ_PARAMS, 0, 0 ); +} + +llcc68_status_t llcc68_get_irq_status( const void* context, llcc68_irq_mask_t* irq ) +{ + uint8_t buf[LLCC68_SIZE_GET_IRQ_STATUS] = { 0 }; + uint8_t irq_local[sizeof( llcc68_irq_mask_t )] = { 0x00 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_IRQ_STATUS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_IRQ_STATUS, irq_local, + sizeof( llcc68_irq_mask_t ) ); + + if( status == LLCC68_STATUS_OK ) + { + *irq = ( ( llcc68_irq_mask_t ) irq_local[0] << 8 ) + ( ( llcc68_irq_mask_t ) irq_local[1] << 0 ); + } + + return status; +} + +llcc68_status_t llcc68_clear_irq_status( const void* context, const llcc68_irq_mask_t irq_mask ) +{ + uint8_t buf[LLCC68_SIZE_CLR_IRQ_STATUS] = { 0 }; + + buf[0] = LLCC68_CLR_IRQ_STATUS; + + buf[1] = ( uint8_t )( irq_mask >> 8 ); + buf[2] = ( uint8_t )( irq_mask >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_CLR_IRQ_STATUS, 0, 0 ); +} + +llcc68_status_t llcc68_get_and_clear_irq_status( const void* context, llcc68_irq_mask_t* irq ) +{ + llcc68_irq_mask_t llcc68_irq_mask = LLCC68_IRQ_NONE; + + llcc68_status_t status = llcc68_get_irq_status( context, &llcc68_irq_mask ); + + if( ( status == LLCC68_STATUS_OK ) && ( llcc68_irq_mask != 0 ) ) + { + status = llcc68_clear_irq_status( context, llcc68_irq_mask ); + } + if( ( status == LLCC68_STATUS_OK ) && ( irq != NULL ) ) + { + *irq = llcc68_irq_mask; + } + return status; +} + +llcc68_status_t llcc68_set_dio2_as_rf_sw_ctrl( const void* context, const bool enable ) +{ + uint8_t buf[LLCC68_SIZE_SET_DIO2_AS_RF_SWITCH_CTRL] = { 0 }; + + buf[0] = LLCC68_SET_DIO2_AS_RF_SWITCH_CTRL; + + buf[1] = ( enable == true ) ? 1 : 0; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_DIO2_AS_RF_SWITCH_CTRL, 0, 0 ); +} + +llcc68_status_t llcc68_set_dio3_as_tcxo_ctrl( const void* context, const llcc68_tcxo_ctrl_voltages_t tcxo_voltage, + const uint32_t timeout ) +{ + uint8_t buf[LLCC68_SIZE_SET_DIO3_AS_TCXO_CTRL] = { 0 }; + + buf[0] = LLCC68_SET_DIO3_AS_TCXO_CTRL; + + buf[1] = ( uint8_t ) tcxo_voltage; + + buf[2] = ( uint8_t )( timeout >> 16 ); + buf[3] = ( uint8_t )( timeout >> 8 ); + buf[4] = ( uint8_t )( timeout >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_DIO3_AS_TCXO_CTRL, 0, 0 ); +} + +// +// RF Modulation and Packet-Related Functions +// + +llcc68_status_t llcc68_set_rf_freq( const void* context, const uint32_t freq_in_hz ) +{ + const uint32_t freq = llcc68_convert_freq_in_hz_to_pll_step( freq_in_hz ); + + return llcc68_set_rf_freq_in_pll_steps( context, freq ); +} + +llcc68_status_t llcc68_set_rf_freq_in_pll_steps( const void* context, const uint32_t freq ) +{ + uint8_t buf[LLCC68_SIZE_SET_RF_FREQUENCY] = { 0 }; + + buf[0] = LLCC68_SET_RF_FREQUENCY; + + buf[1] = ( uint8_t )( freq >> 24 ); + buf[2] = ( uint8_t )( freq >> 16 ); + buf[3] = ( uint8_t )( freq >> 8 ); + buf[4] = ( uint8_t )( freq >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_RF_FREQUENCY, 0, 0 ); +} + +llcc68_status_t llcc68_set_pkt_type( const void* context, const llcc68_pkt_type_t pkt_type ) +{ + uint8_t buf[LLCC68_SIZE_SET_PKT_TYPE] = { 0 }; + + buf[0] = LLCC68_SET_PKT_TYPE; + + buf[1] = ( uint8_t ) pkt_type; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_PKT_TYPE, 0, 0 ); +} + +llcc68_status_t llcc68_get_pkt_type( const void* context, llcc68_pkt_type_t* pkt_type ) +{ + uint8_t buf[LLCC68_SIZE_GET_PKT_TYPE] = { 0 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t pkt_type_raw; + + buf[0] = LLCC68_GET_PKT_TYPE; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_PKT_TYPE, &pkt_type_raw, 1 ); + *pkt_type = ( llcc68_pkt_type_t ) pkt_type_raw; + + return status; +} + +llcc68_status_t llcc68_set_tx_params( const void* context, const int8_t pwr_in_dbm, const llcc68_ramp_time_t ramp_time ) +{ + uint8_t buf[LLCC68_SIZE_SET_TX_PARAMS] = { 0 }; + + buf[0] = LLCC68_SET_TX_PARAMS; + + buf[1] = ( uint8_t ) pwr_in_dbm; + buf[2] = ( uint8_t ) ramp_time; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_TX_PARAMS, 0, 0 ); +} + +llcc68_status_t llcc68_set_gfsk_mod_params( const void* context, const llcc68_mod_params_gfsk_t* params ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t buf[LLCC68_SIZE_SET_MODULATION_PARAMS_GFSK] = { 0 }; + uint32_t bitrate = ( uint32_t )( 32 * LLCC68_XTAL_FREQ / params->br_in_bps ); + const uint32_t fdev = llcc68_convert_freq_in_hz_to_pll_step( params->fdev_in_hz ); + + buf[0] = LLCC68_SET_MODULATION_PARAMS; + + buf[1] = ( uint8_t )( bitrate >> 16 ); + buf[2] = ( uint8_t )( bitrate >> 8 ); + buf[3] = ( uint8_t )( bitrate >> 0 ); + + buf[4] = ( uint8_t )( params->pulse_shape ); + + buf[5] = params->bw_dsb_param; + + buf[6] = ( uint8_t )( fdev >> 16 ); + buf[7] = ( uint8_t )( fdev >> 8 ); + buf[8] = ( uint8_t )( fdev >> 0 ); + + status = ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_MODULATION_PARAMS_GFSK, 0, 0 ); + + if( status == LLCC68_STATUS_OK ) + { + // WORKAROUND - Modulation Quality with 500 kHz LoRa Bandwidth, see DS_SX1261-2_V1.2 datasheet chapter 15.1 + status = llcc68_tx_modulation_workaround( context, LLCC68_PKT_TYPE_GFSK, ( llcc68_lora_bw_t ) 0 ); + // WORKAROUND END + } + return status; +} + +llcc68_status_t llcc68_set_lora_mod_params( const void* context, const llcc68_mod_params_lora_t* params ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + + uint8_t buf[LLCC68_SIZE_SET_MODULATION_PARAMS_LORA] = { 0 }; + + if( ( ( params->bw == LLCC68_LORA_BW_250 ) && ( params->sf == LLCC68_LORA_SF11 ) ) || + ( ( params->bw == LLCC68_LORA_BW_125 ) && + ( ( params->sf == LLCC68_LORA_SF11 ) || ( params->sf == LLCC68_LORA_SF10 ) ) ) ) + { + status = LLCC68_STATUS_UNSUPPORTED_FEATURE; + } + else + { + buf[0] = LLCC68_SET_MODULATION_PARAMS; + + buf[1] = ( uint8_t )( params->sf ); + buf[2] = ( uint8_t )( params->bw ); + buf[3] = ( uint8_t )( params->cr ); + buf[4] = params->ldro & 0x01; + + status = llcc68_hal_write( context, buf, LLCC68_SIZE_SET_MODULATION_PARAMS_LORA, 0, 0 ); + + if( status == LLCC68_STATUS_OK ) + { + // WORKAROUND - Modulation Quality with 500 kHz LoRa Bandwidth, see datasheet DS_SX1261-2_V1.2 §15.1 + status = llcc68_tx_modulation_workaround( context, LLCC68_PKT_TYPE_LORA, params->bw ); + // WORKAROUND END + } + } + + return status; +} + +llcc68_status_t llcc68_set_gfsk_pkt_params( const void* context, const llcc68_pkt_params_gfsk_t* params ) +{ + uint8_t buf[LLCC68_SIZE_SET_PKT_PARAMS_GFSK] = { 0 }; + + buf[0] = LLCC68_SET_PKT_PARAMS; + + buf[1] = ( uint8_t )( params->preamble_len_in_bits >> 8 ); + buf[2] = ( uint8_t )( params->preamble_len_in_bits >> 0 ); + buf[3] = ( uint8_t )( params->preamble_detector ); + buf[4] = params->sync_word_len_in_bits; + buf[5] = ( uint8_t )( params->address_filtering ); + buf[6] = ( uint8_t )( params->header_type ); + buf[7] = params->pld_len_in_bytes; + buf[8] = ( uint8_t )( params->crc_type ); + buf[9] = ( uint8_t )( params->dc_free ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_PKT_PARAMS_GFSK, 0, 0 ); +} + +llcc68_status_t llcc68_set_lora_pkt_params( const void* context, const llcc68_pkt_params_lora_t* params ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + + uint8_t buf[LLCC68_SIZE_SET_PKT_PARAMS_LORA] = { 0 }; + + buf[0] = LLCC68_SET_PKT_PARAMS; + + buf[1] = ( uint8_t )( params->preamble_len_in_symb >> 8 ); + buf[2] = ( uint8_t )( params->preamble_len_in_symb >> 0 ); + buf[3] = ( uint8_t )( params->header_type ); + buf[4] = params->pld_len_in_bytes; + buf[5] = ( uint8_t )( params->crc_is_on ? 1 : 0 ); + buf[6] = ( uint8_t )( params->invert_iq_is_on ? 1 : 0 ); + + status = ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_PKT_PARAMS_LORA, 0, 0 ); + + // WORKAROUND - Optimizing the Inverted IQ Operation, see datasheet DS_SX1261-2_V1.2 §15.4 + if( status == LLCC68_STATUS_OK ) + { + uint8_t reg_value = 0; + + status = llcc68_read_register( context, LLCC68_REG_IRQ_POLARITY, ®_value, 1 ); + if( status == LLCC68_STATUS_OK ) + { + if( params->invert_iq_is_on == true ) + { + reg_value &= ~( 1 << 2 ); // Bit 2 set to 0 when using inverted IQ polarity + } + else + { + reg_value |= ( 1 << 2 ); // Bit 2 set to 1 when using standard IQ polarity + } + status = llcc68_write_register( context, LLCC68_REG_IRQ_POLARITY, ®_value, 1 ); + } + } + // WORKAROUND END + + return status; +} + +llcc68_status_t llcc68_set_cad_params( const void* context, const llcc68_cad_params_t* params ) +{ + uint8_t buf[LLCC68_SIZE_SET_CAD_PARAMS] = { 0 }; + + buf[0] = LLCC68_SET_CAD_PARAMS; + + buf[1] = ( uint8_t ) params->cad_symb_nb; + buf[2] = params->cad_detect_peak; + buf[3] = params->cad_detect_min; + buf[4] = ( uint8_t ) params->cad_exit_mode; + buf[5] = ( uint8_t )( params->cad_timeout >> 16 ); + buf[6] = ( uint8_t )( params->cad_timeout >> 8 ); + buf[7] = ( uint8_t )( params->cad_timeout >> 0 ); + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_CAD_PARAMS, 0, 0 ); +} + +llcc68_status_t llcc68_set_buffer_base_address( const void* context, const uint8_t tx_base_address, + const uint8_t rx_base_address ) +{ + uint8_t buf[LLCC68_SIZE_SET_BUFFER_BASE_ADDRESS] = { 0 }; + + buf[0] = LLCC68_SET_BUFFER_BASE_ADDRESS; + + buf[1] = tx_base_address; + buf[2] = rx_base_address; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_BUFFER_BASE_ADDRESS, 0, 0 ); +} + +llcc68_status_t llcc68_set_lora_symb_nb_timeout( const void* context, const uint8_t nb_of_symbs ) +{ + uint8_t buf[LLCC68_SIZE_SET_LORA_SYMB_NUM_TIMEOUT] = { 0 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_SET_LORA_SYMB_NUM_TIMEOUT; + + buf[1] = nb_of_symbs; + + status = ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_SET_LORA_SYMB_NUM_TIMEOUT, 0, 0 ); + + if( ( status == LLCC68_STATUS_OK ) && ( nb_of_symbs >= 64 ) ) + { + uint8_t mant = nb_of_symbs >> 1; + uint8_t exp = 0; + uint8_t reg = 0; + + while( mant > 31 ) + { + mant >>= 2; + exp++; + } + + reg = exp + ( mant << 3 ); + status = llcc68_write_register( context, LLCC68_REG_LR_SYNCH_TIMEOUT, ®, 1 ); + } + + return status; +} + +// +// Communication Status Information +// + +llcc68_status_t llcc68_get_status( const void* context, llcc68_chip_status_t* radio_status ) +{ + uint8_t buf[LLCC68_SIZE_GET_STATUS] = { 0 }; + uint8_t status_local = 0; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_STATUS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_STATUS, &status_local, 1 ); + + if( status == LLCC68_STATUS_OK ) + { + radio_status->cmd_status = + ( llcc68_cmd_status_t )( ( status_local & LLCC68_CMD_STATUS_MASK ) >> LLCC68_CMD_STATUS_POS ); + radio_status->chip_mode = + ( llcc68_chip_modes_t )( ( status_local & LLCC68_CHIP_MODES_MASK ) >> LLCC68_CHIP_MODES_POS ); + } + + return status; +} + +llcc68_status_t llcc68_get_rx_buffer_status( const void* context, llcc68_rx_buffer_status_t* rx_buffer_status ) +{ + uint8_t buf[LLCC68_SIZE_GET_RX_BUFFER_STATUS] = { 0 }; + uint8_t status_local[sizeof( llcc68_rx_buffer_status_t )] = { 0x00 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_RX_BUFFER_STATUS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_RX_BUFFER_STATUS, status_local, + sizeof( llcc68_rx_buffer_status_t ) ); + + if( status == LLCC68_STATUS_OK ) + { + rx_buffer_status->pld_len_in_bytes = status_local[0]; + rx_buffer_status->buffer_start_pointer = status_local[1]; + } + + return status; +} + +llcc68_status_t llcc68_get_gfsk_pkt_status( const void* context, llcc68_pkt_status_gfsk_t* pkt_status ) +{ + uint8_t buf[LLCC68_SIZE_GET_PKT_STATUS] = { 0 }; + uint8_t pkt_status_local[3] = { 0x00 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_PKT_STATUS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_PKT_STATUS, pkt_status_local, 3 ); + + if( status == LLCC68_STATUS_OK ) + { + pkt_status->rx_status.pkt_sent = + ( ( pkt_status_local[0] & LLCC68_GFSK_RX_STATUS_PKT_SENT_MASK ) != 0 ) ? true : false; + pkt_status->rx_status.pkt_received = + ( ( pkt_status_local[0] & LLCC68_GFSK_RX_STATUS_PKT_RECEIVED_MASK ) != 0 ) ? true : false; + pkt_status->rx_status.abort_error = + ( ( pkt_status_local[0] & LLCC68_GFSK_RX_STATUS_ABORT_ERROR_MASK ) != 0 ) ? true : false; + pkt_status->rx_status.length_error = + ( ( pkt_status_local[0] & LLCC68_GFSK_RX_STATUS_LENGTH_ERROR_MASK ) != 0 ) ? true : false; + pkt_status->rx_status.crc_error = + ( ( pkt_status_local[0] & LLCC68_GFSK_RX_STATUS_CRC_ERROR_MASK ) != 0 ) ? true : false; + pkt_status->rx_status.adrs_error = + ( ( pkt_status_local[0] & LLCC68_GFSK_RX_STATUS_ADRS_ERROR_MASK ) != 0 ) ? true : false; + + pkt_status->rssi_sync = ( int8_t )( -pkt_status_local[1] >> 1 ); + pkt_status->rssi_avg = ( int8_t )( -pkt_status_local[2] >> 1 ); + } + + return status; +} + +llcc68_status_t llcc68_get_lora_pkt_status( const void* context, llcc68_pkt_status_lora_t* pkt_status ) +{ + uint8_t buf[LLCC68_SIZE_GET_PKT_STATUS] = { 0 }; + uint8_t pkt_status_local[sizeof( llcc68_pkt_status_lora_t )] = { 0x00 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_PKT_STATUS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_PKT_STATUS, pkt_status_local, + sizeof( llcc68_pkt_status_lora_t ) ); + + if( status == LLCC68_STATUS_OK ) + { + pkt_status->rssi_pkt_in_dbm = ( int8_t )( -pkt_status_local[0] >> 1 ); + pkt_status->snr_pkt_in_db = ( ( ( int8_t ) pkt_status_local[1] ) + 2 ) >> 2; + pkt_status->signal_rssi_pkt_in_dbm = ( int8_t )( -pkt_status_local[2] >> 1 ); + } + + return status; +} + +llcc68_status_t llcc68_get_rssi_inst( const void* context, int16_t* rssi_in_dbm ) +{ + uint8_t buf[LLCC68_SIZE_GET_RSSI_INST] = { 0 }; + uint8_t rssi_local = 0x00; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_RSSI_INST; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_RSSI_INST, &rssi_local, 1 ); + + if( status == LLCC68_STATUS_OK ) + { + *rssi_in_dbm = ( int8_t )( -rssi_local >> 1 ); + } + + return status; +} + +llcc68_status_t llcc68_get_gfsk_stats( const void* context, llcc68_stats_gfsk_t* stats ) +{ + uint8_t buf[LLCC68_SIZE_GET_STATS] = { 0 }; + uint8_t stats_local[sizeof( llcc68_stats_gfsk_t )] = { 0 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_STATS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_STATS, stats_local, + sizeof( llcc68_stats_gfsk_t ) ); + + if( status == LLCC68_STATUS_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) stats_local[0] << 8 ) + ( uint16_t ) stats_local[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) stats_local[2] << 8 ) + ( uint16_t ) stats_local[3]; + stats->nb_pkt_len_error = ( ( uint16_t ) stats_local[4] << 8 ) + ( uint16_t ) stats_local[5]; + } + + return status; +} + +llcc68_status_t llcc68_get_lora_stats( const void* context, llcc68_stats_lora_t* stats ) +{ + uint8_t buf[LLCC68_SIZE_GET_STATS] = { 0 }; + uint8_t stats_local[sizeof( llcc68_stats_lora_t )] = { 0 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_STATS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_STATS, stats_local, + sizeof( llcc68_stats_lora_t ) ); + + if( status == LLCC68_STATUS_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) stats_local[0] << 8 ) + ( uint16_t ) stats_local[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) stats_local[2] << 8 ) + ( uint16_t ) stats_local[3]; + stats->nb_pkt_header_error = ( ( uint16_t ) stats_local[4] << 8 ) + ( uint16_t ) stats_local[5]; + } + return status; +} + +llcc68_status_t llcc68_reset_stats( const void* context ) +{ + uint8_t buf[LLCC68_SIZE_RESET_STATS] = { 0 }; + + buf[0] = LLCC68_RESET_STATS; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_RESET_STATS, 0, 0 ); +} + +// +// Miscellaneous +// + +llcc68_status_t llcc68_reset( const void* context ) +{ + return ( llcc68_status_t ) llcc68_hal_reset( context ); +} + +llcc68_status_t llcc68_wakeup( const void* context ) +{ + return ( llcc68_status_t ) llcc68_hal_wakeup( context ); +} + +llcc68_status_t llcc68_get_device_errors( const void* context, llcc68_errors_mask_t* errors ) +{ + uint8_t buf[LLCC68_SIZE_GET_DEVICE_ERRORS] = { 0 }; + uint8_t errors_local[sizeof( llcc68_errors_mask_t )] = { 0x00 }; + llcc68_status_t status = LLCC68_STATUS_ERROR; + + buf[0] = LLCC68_GET_DEVICE_ERRORS; + + status = ( llcc68_status_t ) llcc68_hal_read( context, buf, LLCC68_SIZE_GET_DEVICE_ERRORS, errors_local, + sizeof( llcc68_errors_mask_t ) ); + + if( status == LLCC68_STATUS_OK ) + { + *errors = ( ( llcc68_errors_mask_t ) errors_local[0] << 8 ) + ( ( llcc68_errors_mask_t ) errors_local[1] << 0 ); + } + + return status; +} + +llcc68_status_t llcc68_clear_device_errors( const void* context ) +{ + uint8_t buf[LLCC68_SIZE_CLR_DEVICE_ERRORS] = { 0 }; + + buf[0] = LLCC68_CLR_DEVICE_ERRORS; + + return ( llcc68_status_t ) llcc68_hal_write( context, buf, LLCC68_SIZE_CLR_DEVICE_ERRORS, 0, 0 ); +} + +llcc68_status_t llcc68_get_gfsk_bw_param( const uint32_t bw, uint8_t* param ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + + if( bw != 0 ) + { + status = LLCC68_STATUS_UNKNOWN_VALUE; + for( uint8_t i = 0; i < ( sizeof( gfsk_bw ) / sizeof( gfsk_bw_t ) ); i++ ) + { + if( bw <= gfsk_bw[i].bw ) + { + *param = gfsk_bw[i].param; + status = LLCC68_STATUS_OK; + break; + } + } + } + + return status; +} + +uint32_t llcc68_get_lora_bw_in_hz( llcc68_lora_bw_t bw ) +{ + uint32_t bw_in_hz = 0; + + switch( bw ) + { + case LLCC68_LORA_BW_007: + bw_in_hz = 7812UL; + break; + case LLCC68_LORA_BW_010: + bw_in_hz = 10417UL; + break; + case LLCC68_LORA_BW_015: + bw_in_hz = 15625UL; + break; + case LLCC68_LORA_BW_020: + bw_in_hz = 20833UL; + break; + case LLCC68_LORA_BW_031: + bw_in_hz = 31250UL; + break; + case LLCC68_LORA_BW_041: + bw_in_hz = 41667UL; + break; + case LLCC68_LORA_BW_062: + bw_in_hz = 62500UL; + break; + case LLCC68_LORA_BW_125: + bw_in_hz = 125000UL; + break; + case LLCC68_LORA_BW_250: + bw_in_hz = 250000UL; + break; + case LLCC68_LORA_BW_500: + bw_in_hz = 500000UL; + break; + } + + return bw_in_hz; +} + +uint32_t llcc68_get_lora_time_on_air_numerator( const llcc68_pkt_params_lora_t* pkt_p, + const llcc68_mod_params_lora_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->header_type == LLCC68_LORA_PKT_IMPLICIT; + const int32_t cr_denom = mod_p->cr + 4; + + int32_t ceil_denominator; + int32_t ceil_numerator = + ( pld_len_in_bytes << 3 ) + ( pkt_p->crc_is_on ? 16 : 0 ) - ( 4 * sf ) + ( pld_is_fix ? 0 : 20 ); + + if( sf <= 6 ) + { + ceil_denominator = 4 * sf; + } + else + { + ceil_numerator += 8; + + if( mod_p->ldro ) + { + ceil_denominator = 4 * ( sf - 2 ); + } + else + { + ceil_denominator = 4 * sf; + } + } + + if( ceil_numerator < 0 ) + { + ceil_numerator = 0; + } + + // Perform integral ceil() + int32_t intermed = + ( ( ceil_numerator + ceil_denominator - 1 ) / ceil_denominator ) * cr_denom + pkt_p->preamble_len_in_symb + 12; + + if( sf <= 6 ) + { + intermed += 2; + } + + return ( uint32_t )( ( 4 * intermed + 1 ) * ( 1 << ( sf - 2 ) ) ); +} + +uint32_t llcc68_get_lora_time_on_air_in_ms( const llcc68_pkt_params_lora_t* pkt_p, + const llcc68_mod_params_lora_t* mod_p ) +{ + uint32_t numerator = 1000U * llcc68_get_lora_time_on_air_numerator( pkt_p, mod_p ); + uint32_t denominator = llcc68_get_lora_bw_in_hz( mod_p->bw ); + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +uint32_t llcc68_get_gfsk_time_on_air_numerator( const llcc68_pkt_params_gfsk_t* pkt_p ) +{ + return pkt_p->preamble_len_in_bits + ( pkt_p->header_type == LLCC68_GFSK_PKT_VAR_LEN ? 8 : 0 ) + + pkt_p->sync_word_len_in_bits + + ( ( pkt_p->pld_len_in_bytes + ( pkt_p->address_filtering == LLCC68_GFSK_ADDRESS_FILTERING_DISABLE ? 0 : 1 ) + + llcc68_get_gfsk_crc_len_in_bytes( pkt_p->crc_type ) ) + << 3 ); +} + +uint32_t llcc68_get_gfsk_time_on_air_in_ms( const llcc68_pkt_params_gfsk_t* pkt_p, + const llcc68_mod_params_gfsk_t* mod_p ) +{ + uint32_t numerator = 1000U * llcc68_get_gfsk_time_on_air_numerator( pkt_p ); + uint32_t denominator = mod_p->br_in_bps; + + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +llcc68_status_t llcc68_get_random_numbers( const void* context, uint32_t* numbers, unsigned int n ) +{ + llcc68_status_t status; + + uint8_t tmp_ana_lna = 0x00; + uint8_t tmp_ana_mixer = 0x00; + uint8_t tmp = 0x00; + + // Configure for random number generation + status = llcc68_read_register( context, LLCC68_REG_ANA_LNA, &tmp_ana_lna, 1 ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + tmp = tmp_ana_lna & ~( 1 << 0 ); + status = llcc68_write_register( context, LLCC68_REG_ANA_LNA, &tmp, 1 ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + + status = llcc68_read_register( context, LLCC68_REG_ANA_MIXER, &tmp_ana_mixer, 1 ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + tmp = tmp_ana_mixer & ~( 1 << 7 ); + status = llcc68_write_register( context, LLCC68_REG_ANA_MIXER, &tmp, 1 ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + + // Start RX continuous + status = llcc68_set_rx_with_timeout_in_rtc_step( context, 0xFFFFFF ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + + // Store values + for( unsigned int i = 0; i < n; i++ ) + { + status = llcc68_read_register( context, LLCC68_REG_RNGBASEADDRESS, ( uint8_t* ) &numbers[i], 4 ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + } + + status = llcc68_set_standby( context, LLCC68_STANDBY_CFG_RC ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + + // Restore registers + status = llcc68_write_register( context, LLCC68_REG_ANA_LNA, &tmp_ana_lna, 1 ); + if( status != LLCC68_STATUS_OK ) + { + return status; + } + status = llcc68_write_register( context, LLCC68_REG_ANA_MIXER, &tmp_ana_mixer, 1 ); + + return status; +} + +uint32_t llcc68_convert_freq_in_hz_to_pll_step( uint32_t freq_in_hz ) +{ + uint32_t steps_int; + uint32_t steps_frac; + + // Get integer and fractional parts of the frequency computed with a PLL step scaled value + steps_int = freq_in_hz / LLCC68_PLL_STEP_SCALED; + steps_frac = freq_in_hz - ( steps_int * LLCC68_PLL_STEP_SCALED ); + + // Apply the scaling factor to retrieve a frequency in Hz (+ ceiling) + return ( steps_int << LLCC68_PLL_STEP_SHIFT_AMOUNT ) + + ( ( ( steps_frac << LLCC68_PLL_STEP_SHIFT_AMOUNT ) + ( LLCC68_PLL_STEP_SCALED >> 1 ) ) / + LLCC68_PLL_STEP_SCALED ); +} + +uint32_t llcc68_convert_timeout_in_ms_to_rtc_step( uint32_t timeout_in_ms ) +{ + return ( uint32_t )( timeout_in_ms * ( LLCC68_RTC_FREQ_IN_HZ / 1000 ) ); +} + +// +// Registers access +// + +llcc68_status_t llcc68_cfg_rx_boosted( const void* context, const bool state ) +{ + if( state == true ) + { + return llcc68_write_register( context, LLCC68_REG_RXGAIN, ( const uint8_t[] ){ 0x96 }, 1 ); + } + else + { + return llcc68_write_register( context, LLCC68_REG_RXGAIN, ( const uint8_t[] ){ 0x94 }, 1 ); + } +} + +llcc68_status_t llcc68_set_gfsk_sync_word( const void* context, const uint8_t* sync_word, const uint8_t sync_word_len ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t buf[8] = { 0 }; + + if( sync_word_len <= 8 ) + { + memcpy( buf, sync_word, sync_word_len ); + status = llcc68_write_register( context, LLCC68_REG_SYNCWORDBASEADDRESS, buf, 8 ); + } + + return status; +} + +llcc68_status_t llcc68_set_lora_sync_word( const void* context, const uint8_t sync_word ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t buffer[2] = { 0x00 }; + + status = llcc68_read_register( context, LLCC68_REG_LR_SYNCWORD, buffer, 2 ); + + if( status == LLCC68_STATUS_OK ) + { + buffer[0] = ( buffer[0] & ~0xF0 ) + ( sync_word & 0xF0 ); + buffer[1] = ( buffer[1] & ~0xF0 ) + ( ( sync_word & 0x0F ) << 4 ); + + status = llcc68_write_register( context, LLCC68_REG_LR_SYNCWORD, buffer, 2 ); + } + + return status; +} + +llcc68_status_t llcc68_set_gfsk_crc_seed( const void* context, uint16_t seed ) +{ + uint8_t s[] = { ( uint8_t )( seed >> 8 ), ( uint8_t ) seed }; + + return llcc68_write_register( context, LLCC68_REG_CRCSEEDBASEADDRESS, s, sizeof( s ) ); +} + +llcc68_status_t llcc68_set_gfsk_crc_polynomial( const void* context, const uint16_t polynomial ) +{ + uint8_t poly[] = { ( uint8_t )( polynomial >> 8 ), ( uint8_t ) polynomial }; + + return llcc68_write_register( context, LLCC68_REG_CRCPOLYBASEADDRESS, poly, sizeof( poly ) ); +} + +llcc68_status_t llcc68_set_gfsk_whitening_seed( const void* context, const uint16_t seed ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t reg_value = 0; + + // The LLCC68_REG_WHITSEEDBASEADDRESS @ref LSBit is used for the seed value. The 7 MSBits must not be modified. + // Thus, we first need to read the current value and then change the LSB according to the provided seed @ref value. + status = llcc68_read_register( context, LLCC68_REG_WHITSEEDBASEADDRESS, ®_value, 1 ); + if( status == LLCC68_STATUS_OK ) + { + reg_value = ( reg_value & 0xFE ) | ( ( uint8_t )( seed >> 8 ) & 0x01 ); + status = llcc68_write_register( context, LLCC68_REG_WHITSEEDBASEADDRESS, ®_value, 1 ); + if( status == LLCC68_STATUS_OK ) + { + reg_value = ( uint8_t ) seed; + status = llcc68_write_register( context, LLCC68_REG_WHITSEEDBASEADDRESS + 1, ®_value, 1 ); + } + } + + return status; +} + +llcc68_status_t llcc68_cfg_tx_clamp( const void* context ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t reg_value = 0x00; + + status = llcc68_read_register( context, LLCC68_REG_TX_CLAMP_CFG, ®_value, 1 ); + + if( status == LLCC68_STATUS_OK ) + { + reg_value |= LLCC68_REG_TX_CLAMP_CFG_MASK; + status = llcc68_write_register( context, LLCC68_REG_TX_CLAMP_CFG, ®_value, 1 ); + } + + return status; +} + +llcc68_status_t llcc68_stop_rtc( const void* context ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t reg_value = 0; + + reg_value = 0; + status = llcc68_write_register( context, LLCC68_REG_RTC_CTRL, ®_value, 1 ); + + if( status == LLCC68_STATUS_OK ) + { + status = llcc68_read_register( context, LLCC68_REG_EVT_CLR, ®_value, 1 ); + + if( status == LLCC68_STATUS_OK ) + { + reg_value |= LLCC68_REG_EVT_CLR_TIMEOUT_MASK; + status = llcc68_write_register( context, LLCC68_REG_EVT_CLR, ®_value, 1 ); + } + } + + return status; +} + +llcc68_status_t llcc68_set_ocp_value( const void* context, const uint8_t ocp_in_step_of_2_5_ma ) +{ + return ( llcc68_status_t ) llcc68_write_register( context, LLCC68_REG_OCP, &ocp_in_step_of_2_5_ma, 1 ); +} + +llcc68_status_t llcc68_set_trimming_capacitor_values( const void* context, const uint8_t trimming_cap_xta, + const uint8_t trimming_cap_xtb ) +{ + uint8_t trimming_capacitor_values[2] = { trimming_cap_xta, trimming_cap_xtb }; + + return ( llcc68_status_t ) llcc68_write_register( context, LLCC68_REG_XTATRIM, trimming_capacitor_values, 2 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static llcc68_status_t llcc68_tx_modulation_workaround( const void* context, llcc68_pkt_type_t pkt_type, + llcc68_lora_bw_t bw ) +{ + llcc68_status_t status = LLCC68_STATUS_ERROR; + uint8_t reg_value = 0; + + status = llcc68_read_register( context, LLCC68_REG_TX_MODULATION, ®_value, 1 ); + + if( status == LLCC68_STATUS_OK ) + { + if( pkt_type == LLCC68_PKT_TYPE_LORA ) + { + if( bw == LLCC68_LORA_BW_500 ) + { + reg_value &= ~( 1 << 2 ); // Bit 2 set to 0 if the LoRa BW = 500 kHz + } + else + { + reg_value |= ( 1 << 2 ); // Bit 2 set to 1 for any other LoRa BW + } + } + else + { + reg_value |= ( 1 << 2 ); // Bit 2 set to 1 for any (G)FSK configuration + } + + status = llcc68_write_register( context, LLCC68_REG_TX_MODULATION, ®_value, 1 ); + } + return status; +} + +static inline uint32_t llcc68_get_gfsk_crc_len_in_bytes( llcc68_gfsk_crc_types_t crc_type ) +{ + switch( crc_type ) + { + case LLCC68_GFSK_CRC_OFF: + return 0; + case LLCC68_GFSK_CRC_1_BYTE: + return 1; + case LLCC68_GFSK_CRC_2_BYTES: + return 2; + case LLCC68_GFSK_CRC_1_BYTE_INV: + return 1; + case LLCC68_GFSK_CRC_2_BYTES_INV: + return 2; + } + + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/llcc68.h b/src/llcc68.h new file mode 100644 index 0000000..058de31 --- /dev/null +++ b/src/llcc68.h @@ -0,0 +1,1521 @@ +/** + * @file llcc68.h + * + * @brief LLCC68 radio driver definition + * + * Revised BSD License + * Copyright Semtech Corporation 2020. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted 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. + * + * 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 LLCC68_H +#define LLCC68_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Maximum value for parameter timeout_in_rtc_step in both functions \ref llcc68_set_rx_with_timeout_in_rtc_step + * and \ref llcc68_set_tx_with_timeout_in_rtc_step + */ +#define LLCC68_MAX_TIMEOUT_IN_RTC_STEP 0x00FFFFFE + +/** + * @brief Maximum value for parameter timeout_in_ms in both functions \ref llcc68_set_rx and \ref llcc68_set_tx + */ +#define LLCC68_MAX_TIMEOUT_IN_MS ( LLCC68_MAX_TIMEOUT_IN_RTC_STEP / 64 ) + +/** + * @brief Timeout parameter in \ref llcc68_set_rx_with_timeout_in_rtc_step to set the chip in reception until a + * reception occurs + */ +#define LLCC68_RX_SINGLE_MODE 0x00000000 + +/** + * @brief Timeout parameter in \ref llcc68_set_rx_with_timeout_in_rtc_step to launch a continuous reception + */ +#define LLCC68_RX_CONTINUOUS 0x00FFFFFF + +#define LLCC68_CHIP_MODES_POS ( 4U ) +#define LLCC68_CHIP_MODES_MASK ( 0x07UL << LLCC68_CHIP_MODES_POS ) + +#define LLCC68_CMD_STATUS_POS ( 1U ) +#define LLCC68_CMD_STATUS_MASK ( 0x07UL << LLCC68_CMD_STATUS_POS ) + +#define LLCC68_GFSK_RX_STATUS_PKT_SENT_POS ( 0U ) +#define LLCC68_GFSK_RX_STATUS_PKT_SENT_MASK ( 0x01UL << LLCC68_GFSK_RX_STATUS_PKT_SENT_POS ) + +#define LLCC68_GFSK_RX_STATUS_PKT_RECEIVED_POS ( 1U ) +#define LLCC68_GFSK_RX_STATUS_PKT_RECEIVED_MASK ( 0x01UL << LLCC68_GFSK_RX_STATUS_PKT_RECEIVED_POS ) + +#define LLCC68_GFSK_RX_STATUS_ABORT_ERROR_POS ( 2U ) +#define LLCC68_GFSK_RX_STATUS_ABORT_ERROR_MASK ( 0x01UL << LLCC68_GFSK_RX_STATUS_ABORT_ERROR_POS ) + +#define LLCC68_GFSK_RX_STATUS_LENGTH_ERROR_POS ( 3U ) +#define LLCC68_GFSK_RX_STATUS_LENGTH_ERROR_MASK ( 0x01UL << LLCC68_GFSK_RX_STATUS_LENGTH_ERROR_POS ) + +#define LLCC68_GFSK_RX_STATUS_CRC_ERROR_POS ( 4U ) +#define LLCC68_GFSK_RX_STATUS_CRC_ERROR_MASK ( 0x01UL << LLCC68_GFSK_RX_STATUS_CRC_ERROR_POS ) + +#define LLCC68_GFSK_RX_STATUS_ADRS_ERROR_POS ( 5U ) +#define LLCC68_GFSK_RX_STATUS_ADRS_ERROR_MASK ( 0x01UL << LLCC68_GFSK_RX_STATUS_ADRS_ERROR_POS ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LLCC68 APIs return status enumeration definition + */ +typedef enum llcc68_status_e +{ + LLCC68_STATUS_OK = 0, + LLCC68_STATUS_UNSUPPORTED_FEATURE, + LLCC68_STATUS_UNKNOWN_VALUE, + LLCC68_STATUS_ERROR, +} llcc68_status_t; + +/** + * @brief LLCC68 sleep mode configurations definition + */ +typedef enum llcc68_sleep_cfgs_e +{ + LLCC68_SLEEP_CFG_COLD_START = ( 0 << 2 ), + LLCC68_SLEEP_CFG_WARM_START = ( 1 << 2 ), +} llcc68_sleep_cfgs_t; + +/** + * @brief LLCC68 standby modes enumeration definition + */ +typedef enum llcc68_standby_cfgs_e +{ + LLCC68_STANDBY_CFG_RC = 0x00, + LLCC68_STANDBY_CFG_XOSC = 0x01, +} llcc68_standby_cfgs_t; + +typedef uint8_t llcc68_standby_cfg_t; + +/** + * @brief LLCC68 power regulator modes enumeration definition + */ +typedef enum llcc68_reg_mods_e +{ + LLCC68_REG_MODE_LDO = 0x00, // default + LLCC68_REG_MODE_DCDC = 0x01, +} llcc68_reg_mod_t; + +/** + * @brief LLCC68 power amplifier configuration parameters structure definition + */ +typedef struct llcc68_pa_cfg_params_s +{ + uint8_t pa_duty_cycle; + uint8_t hp_max; + uint8_t device_sel; + uint8_t pa_lut; +} llcc68_pa_cfg_params_t; + +/** + * @brief LLCC68 fallback modes enumeration definition + */ +typedef enum llcc68_fallback_modes_e +{ + LLCC68_FALLBACK_STDBY_RC = 0x20, + LLCC68_FALLBACK_STDBY_XOSC = 0x30, + LLCC68_FALLBACK_FS = 0x40, +} llcc68_fallback_modes_t; + +/** + * @brief LLCC68 interrupt masks enumeration definition + */ +enum llcc68_irq_masks_e +{ + LLCC68_IRQ_NONE = ( 0 << 0 ), + LLCC68_IRQ_TX_DONE = ( 1 << 0 ), + LLCC68_IRQ_RX_DONE = ( 1 << 1 ), + LLCC68_IRQ_PREAMBLE_DETECTED = ( 1 << 2 ), + LLCC68_IRQ_SYNC_WORD_VALID = ( 1 << 3 ), + LLCC68_IRQ_HEADER_VALID = ( 1 << 4 ), + LLCC68_IRQ_HEADER_ERROR = ( 1 << 5 ), + LLCC68_IRQ_CRC_ERROR = ( 1 << 6 ), + LLCC68_IRQ_CAD_DONE = ( 1 << 7 ), + LLCC68_IRQ_CAD_DETECTED = ( 1 << 8 ), + LLCC68_IRQ_TIMEOUT = ( 1 << 9 ), + LLCC68_IRQ_ALL = LLCC68_IRQ_TX_DONE | LLCC68_IRQ_RX_DONE | LLCC68_IRQ_PREAMBLE_DETECTED | + LLCC68_IRQ_SYNC_WORD_VALID | LLCC68_IRQ_HEADER_VALID | LLCC68_IRQ_HEADER_ERROR | + LLCC68_IRQ_CRC_ERROR | LLCC68_IRQ_CAD_DONE | LLCC68_IRQ_CAD_DETECTED | LLCC68_IRQ_TIMEOUT, +}; + +typedef uint16_t llcc68_irq_mask_t; + +/** + * @brief Calibration settings + */ +enum llcc68_cal_mask_e +{ + LLCC68_CAL_RC64K = ( 1 << 0 ), + LLCC68_CAL_RC13M = ( 1 << 1 ), + LLCC68_CAL_PLL = ( 1 << 2 ), + LLCC68_CAL_ADC_PULSE = ( 1 << 3 ), + LLCC68_CAL_ADC_BULK_N = ( 1 << 4 ), + LLCC68_CAL_ADC_BULK_P = ( 1 << 5 ), + LLCC68_CAL_IMAGE = ( 1 << 6 ), + LLCC68_CAL_ALL = LLCC68_CAL_RC64K | LLCC68_CAL_RC13M | LLCC68_CAL_PLL | LLCC68_CAL_ADC_PULSE | + LLCC68_CAL_ADC_BULK_N | LLCC68_CAL_ADC_BULK_P | LLCC68_CAL_IMAGE, +}; + +typedef uint8_t llcc68_cal_mask_t; + +/** + * @brief LLCC68 TCXO control voltages enumeration definition + */ +typedef enum llcc68_tcxo_ctrl_voltages_e +{ + LLCC68_TCXO_CTRL_1_6V = 0x00, + LLCC68_TCXO_CTRL_1_7V = 0x01, + LLCC68_TCXO_CTRL_1_8V = 0x02, + LLCC68_TCXO_CTRL_2_2V = 0x03, + LLCC68_TCXO_CTRL_2_4V = 0x04, + LLCC68_TCXO_CTRL_2_7V = 0x05, + LLCC68_TCXO_CTRL_3_0V = 0x06, + LLCC68_TCXO_CTRL_3_3V = 0x07, +} llcc68_tcxo_ctrl_voltages_t; + +/** + * @brief LLCC68 packet types enumeration definition + */ +typedef enum llcc68_pkt_types_e +{ + LLCC68_PKT_TYPE_GFSK = 0x00, + LLCC68_PKT_TYPE_LORA = 0x01, +} llcc68_pkt_type_t; + +/** + * @brief LLCC68 power amplifier ramp-up timings enumeration definition + */ +typedef enum llcc68_ramp_time_e +{ + LLCC68_RAMP_10_US = 0x00, + LLCC68_RAMP_20_US = 0x01, + LLCC68_RAMP_40_US = 0x02, + LLCC68_RAMP_80_US = 0x03, + LLCC68_RAMP_200_US = 0x04, + LLCC68_RAMP_800_US = 0x05, + LLCC68_RAMP_1700_US = 0x06, + LLCC68_RAMP_3400_US = 0x07, +} llcc68_ramp_time_t; + +/** + * @brief LLCC68 GFSK modulation shaping enumeration definition + */ +typedef enum llcc68_gfsk_pulse_shape_e +{ + LLCC68_GFSK_PULSE_SHAPE_OFF = 0x00, + LLCC68_GFSK_PULSE_SHAPE_BT_03 = 0x08, + LLCC68_GFSK_PULSE_SHAPE_BT_05 = 0x09, + LLCC68_GFSK_PULSE_SHAPE_BT_07 = 0x0A, + LLCC68_GFSK_PULSE_SHAPE_BT_1 = 0x0B, +} llcc68_gfsk_pulse_shape_t; + +/** + * @brief LLCC68 GFSK Rx bandwidth enumeration definition + */ +typedef enum llcc68_gfsk_bw_e +{ + LLCC68_GFSK_BW_4800 = 0x1F, + LLCC68_GFSK_BW_5800 = 0x17, + LLCC68_GFSK_BW_7300 = 0x0F, + LLCC68_GFSK_BW_9700 = 0x1E, + LLCC68_GFSK_BW_11700 = 0x16, + LLCC68_GFSK_BW_14600 = 0x0E, + LLCC68_GFSK_BW_19500 = 0x1D, + LLCC68_GFSK_BW_23400 = 0x15, + LLCC68_GFSK_BW_29300 = 0x0D, + LLCC68_GFSK_BW_39000 = 0x1C, + LLCC68_GFSK_BW_46900 = 0x14, + LLCC68_GFSK_BW_58600 = 0x0C, + LLCC68_GFSK_BW_78200 = 0x1B, + LLCC68_GFSK_BW_93800 = 0x13, + LLCC68_GFSK_BW_117300 = 0x0B, + LLCC68_GFSK_BW_156200 = 0x1A, + LLCC68_GFSK_BW_187200 = 0x12, + LLCC68_GFSK_BW_234300 = 0x0A, + LLCC68_GFSK_BW_312000 = 0x19, + LLCC68_GFSK_BW_373600 = 0x11, + LLCC68_GFSK_BW_467000 = 0x09, +} llcc68_gfsk_bw_t; + +/** + * @brief LLCC68 GFSK modulation parameters structure definition + */ +typedef struct llcc68_mod_params_gfsk_s +{ + uint32_t br_in_bps; + uint32_t fdev_in_hz; + llcc68_gfsk_pulse_shape_t pulse_shape; + llcc68_gfsk_bw_t bw_dsb_param; +} llcc68_mod_params_gfsk_t; + +/** + * @brief LLCC68 LoRa spreading factor enumeration definition + */ +typedef enum llcc68_lora_sf_e +{ + LLCC68_LORA_SF5 = 0x05, + LLCC68_LORA_SF6 = 0x06, + LLCC68_LORA_SF7 = 0x07, + LLCC68_LORA_SF8 = 0x08, + LLCC68_LORA_SF9 = 0x09, + LLCC68_LORA_SF10 = 0x0A, + LLCC68_LORA_SF11 = 0x0B, +} llcc68_lora_sf_t; + +/** + * @brief LLCC68 LoRa bandwidth enumeration definition + */ +typedef enum llcc68_lora_bw_e +{ + LLCC68_LORA_BW_500 = 6, + LLCC68_LORA_BW_250 = 5, + LLCC68_LORA_BW_125 = 4, + LLCC68_LORA_BW_062 = 3, + LLCC68_LORA_BW_041 = 10, + LLCC68_LORA_BW_031 = 2, + LLCC68_LORA_BW_020 = 9, + LLCC68_LORA_BW_015 = 1, + LLCC68_LORA_BW_010 = 8, + LLCC68_LORA_BW_007 = 0, +} llcc68_lora_bw_t; + +/** + * @brief LLCC68 LoRa coding rate enumeration definition + */ +typedef enum llcc68_lora_cr_e +{ + LLCC68_LORA_CR_4_5 = 0x01, + LLCC68_LORA_CR_4_6 = 0x02, + LLCC68_LORA_CR_4_7 = 0x03, + LLCC68_LORA_CR_4_8 = 0x04, +} llcc68_lora_cr_t; + +/** + * @brief LLCC68 LoRa modulation parameters structure definition + */ +typedef struct llcc68_mod_params_lora_s +{ + llcc68_lora_sf_t sf; //!< LoRa Spreading Factor + llcc68_lora_bw_t bw; //!< LoRa Bandwidth + llcc68_lora_cr_t cr; //!< LoRa Coding Rate + uint8_t ldro; //!< Low DataRate Optimization configuration +} llcc68_mod_params_lora_t; + +/** + * @brief LLCC68 GFSK preamble length Rx detection size enumeration definition + */ +typedef enum llcc68_gfsk_preamble_detector_e +{ + LLCC68_GFSK_PREAMBLE_DETECTOR_OFF = 0x00, + LLCC68_GFSK_PREAMBLE_DETECTOR_MIN_8BITS = 0x04, + LLCC68_GFSK_PREAMBLE_DETECTOR_MIN_16BITS = 0x05, + LLCC68_GFSK_PREAMBLE_DETECTOR_MIN_24BITS = 0x06, + LLCC68_GFSK_PREAMBLE_DETECTOR_MIN_32BITS = 0x07, +} llcc68_gfsk_preamble_detector_t; + +/** + * @brief LLCC68 GFSK address filtering configuration enumeration definition + */ +typedef enum llcc68_gfsk_address_filtering_e +{ + LLCC68_GFSK_ADDRESS_FILTERING_DISABLE = 0x00, + LLCC68_GFSK_ADDRESS_FILTERING_NODE_ADDRESS = 0x01, + LLCC68_GFSK_ADDRESS_FILTERING_NODE_AND_BROADCAST_ADDRESSES = 0x02, +} llcc68_gfsk_address_filtering_t; + +/** + * @brief LLCC68 GFSK packet length enumeration definition + */ +typedef enum llcc68_gfsk_pkt_len_modes_e +{ + LLCC68_GFSK_PKT_FIX_LEN = 0x00, //!< The packet length is known on both sides, no header included + LLCC68_GFSK_PKT_VAR_LEN = 0x01, //!< The packet length is variable, header included +} llcc68_gfsk_pkt_len_modes_t; + +/** + * @brief LLCC68 GFSK CRC type enumeration definition + */ +typedef enum sx16x_gfsk_crc_types_e +{ + LLCC68_GFSK_CRC_OFF = 0x01, + LLCC68_GFSK_CRC_1_BYTE = 0x00, + LLCC68_GFSK_CRC_2_BYTES = 0x02, + LLCC68_GFSK_CRC_1_BYTE_INV = 0x04, + LLCC68_GFSK_CRC_2_BYTES_INV = 0x06, +} llcc68_gfsk_crc_types_t; + +/** + * @brief LLCC68 GFSK whitening control enumeration definition + */ +typedef enum llcc68_gfsk_dc_free_e +{ + LLCC68_GFSK_DC_FREE_OFF = 0x00, + LLCC68_GFSK_DC_FREE_WHITENING = 0x01, +} llcc68_gfsk_dc_free_t; + +/** + * @brief LLCC68 LoRa packet length enumeration definition + */ +typedef enum llcc68_lora_pkt_len_modes_e +{ + LLCC68_LORA_PKT_EXPLICIT = 0x00, //!< Header included in the packet + LLCC68_LORA_PKT_IMPLICIT = 0x01, //!< Header not included in the packet +} llcc68_lora_pkt_len_modes_t; + +/** + * @brief LLCC68 LoRa packet parameters structure definition + */ +typedef struct llcc68_pkt_params_lora_s +{ + uint16_t preamble_len_in_symb; //!< Preamble length in symbols + llcc68_lora_pkt_len_modes_t header_type; //!< Header type + uint8_t pld_len_in_bytes; //!< Payload length in bytes + bool crc_is_on; //!< CRC activation + bool invert_iq_is_on; //!< IQ polarity setup +} llcc68_pkt_params_lora_t; + +/** + * @brief LLCC68 GFSK packet parameters structure definition + */ +typedef struct llcc68_pkt_params_gfsk_s +{ + uint16_t preamble_len_in_bits; //!< Preamble length in bits + llcc68_gfsk_preamble_detector_t preamble_detector; //!< Preamble detection length + uint8_t sync_word_len_in_bits; //!< Sync word length in bits + llcc68_gfsk_address_filtering_t address_filtering; //!< Address filtering configuration + llcc68_gfsk_pkt_len_modes_t header_type; //!< Header type + uint8_t pld_len_in_bytes; //!< Payload length in bytes + llcc68_gfsk_crc_types_t crc_type; //!< CRC type configuration + llcc68_gfsk_dc_free_t dc_free; //!< Whitening configuration +} llcc68_pkt_params_gfsk_t; + +/** + * @brief LLCC68 LoRa CAD number of symbols enumeration definition + * + * @note Represents the number of symbols to be used for a CAD operation + */ +typedef enum llcc68_cad_symbs_e +{ + LLCC68_CAD_01_SYMB = 0x00, + LLCC68_CAD_02_SYMB = 0x01, + LLCC68_CAD_04_SYMB = 0x02, + LLCC68_CAD_08_SYMB = 0x03, + LLCC68_CAD_16_SYMB = 0x04, +} llcc68_cad_symbs_t; + +/** + * @brief LLCC68 LoRa CAD exit modes enumeration definition + * + * @note Represents the action to be performed after a CAD is done + */ +typedef enum llcc68_cad_exit_modes_e +{ + LLCC68_CAD_ONLY = 0x00, + LLCC68_CAD_RX = 0x01, + LLCC68_CAD_LBT = 0x10, +} llcc68_cad_exit_modes_t; + +/** + * @brief LLCC68 CAD parameters structure definition + */ +typedef struct llcc68_cad_param_s +{ + llcc68_cad_symbs_t cad_symb_nb; //!< CAD number of symbols + uint8_t cad_detect_peak; //!< CAD peak detection + uint8_t cad_detect_min; //!< CAD minimum detection + llcc68_cad_exit_modes_t cad_exit_mode; //!< CAD exit mode + uint32_t cad_timeout; //!< CAD timeout value +} llcc68_cad_params_t; + +/** + * @brief LLCC68 chip mode enumeration definition + */ +typedef enum llcc68_chip_modes_e +{ + LLCC68_CHIP_MODE_UNUSED = 0, + LLCC68_CHIP_MODE_RFU = 1, + LLCC68_CHIP_MODE_STBY_RC = 2, + LLCC68_CHIP_MODE_STBY_XOSC = 3, + LLCC68_CHIP_MODE_FS = 4, + LLCC68_CHIP_MODE_RX = 5, + LLCC68_CHIP_MODE_TX = 6, +} llcc68_chip_modes_t; + +/** + * @brief LLCC68 command status enumeration definition + */ +typedef enum llcc68_cmd_status_e +{ + LLCC68_CMD_STATUS_RESERVED = 0, + LLCC68_CMD_STATUS_RFU = 1, + LLCC68_CMD_STATUS_DATA_AVAILABLE = 2, + LLCC68_CMD_STATUS_CMD_TIMEOUT = 3, + LLCC68_CMD_STATUS_CMD_PROCESS_ERROR = 4, + LLCC68_CMD_STATUS_CMD_EXEC_FAILURE = 5, + LLCC68_CMD_STATUS_CMD_TX_DONE = 6, +} llcc68_cmd_status_t; + +/** + * @brief LLCC68 chip status structure definition + */ +typedef struct llcc68_chip_status_s +{ + llcc68_cmd_status_t cmd_status; //!< Previous command status + llcc68_chip_modes_t chip_mode; //!< Current chip mode +} llcc68_chip_status_t; + +/** + * @brief LLCC68 RX buffer status structure definition + */ +typedef struct llcc68_rx_buffer_status_s +{ + uint8_t pld_len_in_bytes; //!< Number of bytes available in the buffer + uint8_t buffer_start_pointer; //!< Position of the first byte in the buffer +} llcc68_rx_buffer_status_t; + +typedef struct llcc68_rx_status_gfsk_s +{ + bool pkt_sent; + bool pkt_received; + bool abort_error; + bool length_error; + bool crc_error; + bool adrs_error; +} llcc68_rx_status_gfsk_t; + +/** + * @brief LLCC68 GFSK packet status structure definition + */ +typedef struct llcc68_pkt_status_gfsk_s +{ + llcc68_rx_status_gfsk_t rx_status; + int8_t rssi_sync; //!< The RSSI measured on last packet + int8_t rssi_avg; //!< The averaged RSSI +} llcc68_pkt_status_gfsk_t; + +/** + * @brief LLCC68 LoRa packet status structure definition + */ +typedef struct llcc68_pkt_status_lora_s +{ + int8_t rssi_pkt_in_dbm; //!< RSSI of the last packet + int8_t snr_pkt_in_db; //!< SNR of the last packet + int8_t signal_rssi_pkt_in_dbm; //!< Estimation of RSSI (after despreading) +} llcc68_pkt_status_lora_t; + +/** + * @brief LLCC68 GFSK reception statistics structure definition + */ +typedef struct llcc68_stats_gfsk_s +{ + uint16_t nb_pkt_received; + uint16_t nb_pkt_crc_error; + uint16_t nb_pkt_len_error; +} llcc68_stats_gfsk_t; + +/** + * @brief LLCC68 LoRa reception statistics structure definition + */ +typedef struct llcc68_stats_lora_s +{ + uint16_t nb_pkt_received; + uint16_t nb_pkt_crc_error; + uint16_t nb_pkt_header_error; +} llcc68_stats_lora_t; + +/** + * @brief LLCC68 errors enumeration definition + */ +enum llcc68_errors_e +{ + LLCC68_ERRORS_RC64K_CALIBRATION = ( 1 << 0 ), + LLCC68_ERRORS_RC13M_CALIBRATION = ( 1 << 1 ), + LLCC68_ERRORS_PLL_CALIBRATION = ( 1 << 2 ), + LLCC68_ERRORS_ADC_CALIBRATION = ( 1 << 3 ), + LLCC68_ERRORS_IMG_CALIBRATION = ( 1 << 4 ), + LLCC68_ERRORS_XOSC_START = ( 1 << 5 ), + LLCC68_ERRORS_PLL_LOCK = ( 1 << 6 ), + LLCC68_ERRORS_PA_RAMP = ( 1 << 8 ), +}; + +typedef uint16_t llcc68_errors_mask_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +// +// Operational Modes Functions +// + +/** + * @brief Set the chip in sleep mode + * + * @param [in] context Chip implementation context. + * @param [in] cfg Sleep mode configuration + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_sleep( const void* context, const llcc68_sleep_cfgs_t cfg ); + +/** + * @brief Set the chip in stand-by mode + * + * @param [in] context Chip implementation context. + * @param [in] cfg Stand-by mode configuration + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_standby( const void* context, const llcc68_standby_cfg_t cfg ); + +/** + * @brief Set the chip in frequency synthesis mode + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_fs( const void* context ); + +/** + * @brief Set the chip in transmission mode + * + * @remark The packet type shall be configured with @ref llcc68_set_pkt_type before using this command. + * + * @remark By default, the chip returns automatically to standby RC mode as soon as the packet is sent or if the packet + * has not been completely transmitted before the timeout. This behavior can be altered by @ref + * llcc68_set_rx_tx_fallback_mode. + * + * @remark If the timeout argument is 0, then no timeout is used. + * + * @param [in] context Chip implementation context. + * @param [in] timeout_in_ms The timeout configuration in millisecond for Tx operation + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_tx( const void* context, const uint32_t timeout_in_ms ); + +/** + * @brief Set the chip in transmission mode + * + * @remark The packet type shall be configured with @ref llcc68_set_pkt_type before using this command. + * + * @remark By default, the chip returns automatically to standby RC mode as soon as the packet is sent or if the packet + * has not been completely transmitted before the timeout. This behavior can be altered by @ref + * llcc68_set_rx_tx_fallback_mode. + * + * @remark The timeout duration can be computed with the formula: + * \f$ timeout\_duration\_ms = timeout_in_rtc_step \times * \frac{1}{64} \f$ + * + * @remark Maximal value is LLCC68_MAX_TIMEOUT_IN_RTC_STEP (i.e. 262 143 ms) + * + * @remark If the timeout argument is 0, then no timeout is used. + * + * @param [in] context Chip implementation context. + * @param [in] timeout_in_rtc_step The timeout configuration for Tx operation + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); + +/** + * @brief Set the chip in reception mode + * + * @remark The packet type shall be configured with @ref llcc68_set_pkt_type before using this command. + * + * @remark By default, the chip returns automatically to standby RC mode as soon as a packet is received + * or if no packet has been received before the timeout. This behavior can be altered by @ref + * llcc68_set_rx_tx_fallback_mode. + * + * @remark The timeout argument can have the following special values: + * + * | Special values | Meaning | + * | ----------------------| --------------------------------------------------------------------------------------| + * | LLCC68_RX_SINGLE_MODE | Single: the chip stays in RX mode until a reception occurs, then switch to standby RC | + * + * @param [in] context Chip implementation context. + * @param [in] timeout_in_ms The timeout configuration in millisecond for Rx operation + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_rx( const void* context, const uint32_t timeout_in_ms ); + +/** + * @brief Set the chip in reception mode + * + * @remark The packet type shall be configured with @ref llcc68_set_pkt_type before using this command. + * + * @remark By default, the chip returns automatically to standby RC mode as soon as a packet is received + * or if no packet has been received before the timeout. This behavior can be altered by @ref + * llcc68_set_rx_tx_fallback_mode. + * + * @remark The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout_in_rtc_step \times \frac{1}{64} \f$ + * + * @remark Maximal timeout value is LLCC68_MAX_TIMEOUT_IN_RTC_STEP (i.e. 262 143 ms). + * + * @remark The timeout argument can have the following special values: + * + * | Special values | Meaning | + * | ----------------------| --------------------------------------------------------------------------------------| + * | LLCC68_RX_SINGLE_MODE | Single: the chip stays in RX mode until a reception occurs, then switch to standby RC | + * | LLCC68_RX_CONTINUOUS | Continuous: the chip stays in RX mode even after reception of a packet | + * + * @param [in] context Chip implementation context. + * @param [in] timeout_in_rtc_step The timeout configuration for Rx operation + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); + +/** + * @brief Configure the event on which the Rx timeout is stopped + * + * @remark The two options are: + * - Syncword / Header detection (default) + * - Preamble detection + * + * @param [in] context Chip implementation context. + * @param [in] enable If true, the timer stops on Syncword / Header detection. + * + * @returns Operation status + */ +llcc68_status_t llcc68_stop_timer_on_preamble( const void* context, const bool enable ); + +/** + * @brief Set the chip in reception mode with duty cycling + * + * @param [in] context Chip implementation context. + * @param [in] rx_time_in_ms The timeout of Rx period - in millisecond + * @param [in] sleep_time_in_ms The length of sleep period - in millisecond + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_rx_duty_cycle( const void* context, const uint32_t rx_time_in_ms, + const uint32_t sleep_time_in_ms ); + +/** + * @brief Set the chip in reception mode with duty cycling + * + * @remark The Rx mode duration is defined by: + * \f$ rx\_duration\_ms = rx_time \times \frac{1}{64} \f$ + * + * @remark The sleep mode duration is defined by: + * \f$ sleep\_duration\_ms = sleep_time \times \frac{1}{64} \f$ + * + * @remark Maximal timeout value is 0xFFFFFF (i.e. 511 seconds). + * + * @param [in] context Chip implementation context. + * @param [in] rx_time The timeout of Rx period + * @param [in] sleep_time The length of sleep period + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_rx_duty_cycle_with_timings_in_rtc_step( const void* context, + const uint32_t rx_time_in_rtc_step, + const uint32_t sleep_time_in_rtc_step ); + +/** + * @brief Set the chip in CAD (Channel Activity Detection) mode + * + * @remark The LoRa packet type shall be selected with @ref llcc68_set_pkt_type before this function is called. + * + * @remark The fallback mode is configured with @ref llcc68_set_cad_params. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_cad( const void* context ); + +/** + * @brief Set the chip in Tx continuous wave (RF tone). + * + * @remark The packet type shall be configured with @ref llcc68_set_pkt_type before using this command. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_tx_cw( const void* context ); + +/** + * @brief Set the chip in Tx infinite preamble (modulated signal). + * + * @remark The packet type shall be configured with @ref llcc68_set_pkt_type before using this command. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_tx_infinite_preamble( const void* context ); + +/** + * @brief Configure the regulator mode to be used + * + * @remark This function shall be called to set the correct regulator mode, depending on the usage of LDO or DC/DC on + * the PCB implementation. + * + * @param [in] context Chip implementation context. + * @param [in] mode Regulator mode configuration. + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_reg_mode( const void* context, const llcc68_reg_mod_t mode ); + +/** + * @brief Perform the calibration of the requested blocks + * + * @remark This function shall only be called in stand-by RC mode + * + * @remark The chip will return to stand-by RC mode on exit. Potential calibration issues can be read out with @ref + * llcc68_get_device_errors command. + * + * @param [in] context Chip implementation context. + * @param [in] param Mask holding the blocks to be calibrated + * + * @returns Operation status + */ +llcc68_status_t llcc68_cal( const void* context, const llcc68_cal_mask_t param ); + +/** + * @brief Perform device operating frequency band image rejection calibration + * + * @param [in] context Chip implementation context. + * @param [in] freq_in_hz Frequency in Hz used for the image calibration + * + * @returns Operation status + */ +llcc68_status_t llcc68_cal_img( const void* context, const uint32_t freq_in_hz ); + +/** + * @brief Configure the PA (Power Amplifier) + * + * @details This command is used to differentiate the SX1261 from the SX1262 / SX1268. When using this command, the user + * selects the PA to be used by the device as well as its configuration. + * + * @param [in] context Chip implementation context. + * @param [in] params Power amplifier configuration parameters + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_pa_cfg( const void* context, const llcc68_pa_cfg_params_t* params ); + +/** + * @brief Set chip mode to be used after successful transmission or reception. + * + * @remark This setting is not taken into account during Rx Duty Cycle mode or Auto TxRx. + * + * @param [in] context Chip implementation context. + * @param [in] fallback_mode Selected fallback mode + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_rx_tx_fallback_mode( const void* context, const llcc68_fallback_modes_t fallback_mode ); + +// +// Registers and Buffer Access +// + +/** + * @brief Write data into register memory space. + * + * @param [in] context Chip implementation context. + * @param [in] address The register memory address to start writing operation + * @param [in] buffer The buffer of bytes to write into memory. + * @param [in] size Number of bytes to write into memory, starting from address + * + * @see llcc68_read_register + */ +llcc68_status_t llcc68_write_register( const void* context, const uint16_t address, const uint8_t* buffer, + const uint8_t size ); + +/** + * @brief Read data from register memory space. + * + * @param [in] context Chip implementation context. + * @param [in] address The register memory address to start reading operation + * @param [in] buffer The buffer of bytes to be filled with data from registers + * @param [in] size Number of bytes to read from memory, starting from address + * + * @see llcc68_write_register + */ +llcc68_status_t llcc68_read_register( const void* context, const uint16_t address, uint8_t* buffer, + const uint8_t size ); + +/** + * @brief Write data into radio Tx buffer memory space. + * + * @param [in] context Chip implementation context. + * @param [in] offset Start address in the Tx buffer of the chip + * @param [in] buffer The buffer of bytes to write into radio buffer + * @param [in] size The number of bytes to write into Tx radio buffer + * + * @returns Operation status + * + * @see llcc68_read_buffer + */ +llcc68_status_t llcc68_write_buffer( const void* context, const uint8_t offset, const uint8_t* buffer, + const uint8_t size ); + +/** + * @brief Read data from radio Rx buffer memory space. + * + * @param [in] context Chip implementation context. + * @param [in] offset Start address in the Rx buffer of the chip + * @param [in] buffer The buffer of bytes to be filled with content from Rx radio buffer + * @param [in] size The number of bytes to read from the Rx radio buffer + * + * @returns Operation status + * + * @see llcc68_write_buffer + */ +llcc68_status_t llcc68_read_buffer( const void* context, const uint8_t offset, uint8_t* buffer, const uint8_t size ); + +// +// DIO and IRQ Control Functions +// + +/** + * @brief Set which interrupt signals are redirected to the dedicated DIO pin + * + * @remark By default, no interrupt signal is redirected. + * + * @remark An interrupt will not occur until it is enabled system-wide, even if it is redirected to a specific DIO. + * + * @remark The DIO pin will remain asserted until all redirected interrupt signals are cleared with a call to @ref + * llcc68_clear_irq_status. + * + * @remark DIO2 and DIO3 are shared with other features. See @ref llcc68_set_dio2_as_rf_sw_ctrl and @ref + * llcc68_set_dio3_as_tcxo_ctrl + * + * @param [in] context Chip implementation context. + * @param [in] irq_mask Variable that holds the system interrupt mask + * @param [in] dio1_mask Variable that holds the interrupt mask for dio1 + * @param [in] dio2_mask Variable that holds the interrupt mask for dio2 + * @param [in] dio3_mask Variable that holds the interrupt mask for dio3 + * + * @returns Operation status + * + * @see llcc68_clear_irq_status, llcc68_get_irq_status, llcc68_set_dio2_as_rf_sw_ctrl, llcc68_set_dio3_as_tcxo_ctrl + */ +llcc68_status_t llcc68_set_dio_irq_params( const void* context, const uint16_t irq_mask, const uint16_t dio1_mask, + const uint16_t dio2_mask, const uint16_t dio3_mask ); + +/** + * @brief Get system interrupt status + * + * @param [in] context Chip implementation context. + * @param [out] irq Pointer to a variable for holding the system interrupt status + * + * @returns Operation status + * + * @see llcc68_clear_irq_status + */ +llcc68_status_t llcc68_get_irq_status( const void* context, llcc68_irq_mask_t* irq ); + +/** + * @brief Clear selected system interrupts + * + * @param [in] context Chip implementation context. + * @param [in] irq_mask Variable that holds the system interrupt to be cleared + * + * @returns Operation status + * + * @see llcc68_get_irq_status + */ +llcc68_status_t llcc68_clear_irq_status( const void* context, const llcc68_irq_mask_t irq_mask ); + +/** + * @brief 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. Can be NULL. + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_and_clear_irq_status( const void* context, llcc68_irq_mask_t* irq ); + +/** + * @brief Configure the embedded RF switch control + * + * @param [in] context Chip implementation context. + * @param [in] enable Enable this feature if set to true + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_dio2_as_rf_sw_ctrl( const void* context, const bool enable ); + +/** + * @brief Configure the embedded TCXO switch control + * + * @remark This function shall only be called in standby RC mode. + * + * @remark The chip will wait for the timeout to happen before starting any operation that requires the TCXO. + * + * @param [in] context Chip implementation context. + * @param [in] tcxo_voltage Voltage used to power the TCXO. + * @param [in] timeout Time needed for the TCXO to be stable. + * + * @returns Operation status + * + */ +llcc68_status_t llcc68_set_dio3_as_tcxo_ctrl( const void* context, const llcc68_tcxo_ctrl_voltages_t tcxo_voltage, + const uint32_t timeout ); + +// +// RF Modulation and Packet-Related Functions +// + +/** + * @brief Set the RF frequency for future radio operations. + * + * @remark This commands shall be called only after a packet type is selected. + * + * @param [in] context Chip implementation context. + * @param [in] freq_in_hz The frequency in Hz to set for radio operations + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_rf_freq( const void* context, const uint32_t freq_in_hz ); + +/** + * @brief Set the RF frequency for future radio operations - parameter in PLL steps + * + * @remark This commands shall be called only after a packet type is selected. + * + * @param [in] context Chip implementation context. + * @param [in] freq The frequency in PLL steps to set for radio operations + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_rf_freq_in_pll_steps( const void* context, const uint32_t freq ); + +/** + * @brief Set the packet type + * + * @param [in] context Chip implementation context. + * + * @param [in] pkt_type Packet type to set + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_pkt_type( const void* context, const llcc68_pkt_type_t pkt_type ); + +/** + * @brief Get the current packet type + * + * @param [in] context Chip implementation context. + * @param [out] pkt_type Pointer to a variable holding the packet type + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_pkt_type( const void* context, llcc68_pkt_type_t* pkt_type ); + +/** + * @brief Set the parameters for TX power and power amplifier ramp time + * + * @param [in] context Chip implementation context. + * @param [in] pwr_in_dbm The Tx output power in dBm + * @param [in] ramp_time The ramping time configuration for the PA + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_tx_params( const void* context, const int8_t pwr_in_dbm, + const llcc68_ramp_time_t ramp_time ); + +/** + * @brief Set the modulation parameters for GFSK packets + * + * @remark The command @ref llcc68_set_pkt_type must be called prior to this + * one. + * + * @param [in] context Chip implementation context. + * @param [in] params The structure of GFSK modulation configuration + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_gfsk_mod_params( const void* context, const llcc68_mod_params_gfsk_t* params ); + +/** + * @brief Set the modulation parameters for LoRa packets + * + * @remark The command @ref llcc68_set_pkt_type must be called prior to this one. + * + * @param [in] context Chip implementation context. + * @param [in] params The structure of LoRa modulation configuration + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_lora_mod_params( const void* context, const llcc68_mod_params_lora_t* params ); + +/** + * @brief Set the packet parameters for GFSK packets + * + * @remark The command @ref llcc68_set_pkt_type must be called prior to this + * one. + * + * @param [in] context Chip implementation context. + * @param [in] params The structure of GFSK packet configuration + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_gfsk_pkt_params( const void* context, const llcc68_pkt_params_gfsk_t* params ); + +/** + * @brief Set the packet parameters for LoRa packets + * + * @remark The command @ref llcc68_set_pkt_type must be called prior to this one. + * + * @param [in] context Chip implementation context. + * @param [in] params The structure of LoRa packet configuration + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_lora_pkt_params( const void* context, const llcc68_pkt_params_lora_t* params ); + +/** + * @brief Set the parameters for CAD operation + * + * @remark The command @ref llcc68_set_pkt_type must be called prior to this one. + * + * @param [in] context Chip implementation context. + * @param [in] params The structure of CAD configuration + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_cad_params( const void* context, const llcc68_cad_params_t* params ); + +/** + * @brief Set buffer start addresses for both Tx and Rx operations + * + * @param [in] context Chip implementation context. + * @param [in] tx_base_address The start address used for Tx operations + * @param [in] rx_base_address The start address used for Rx operations + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_buffer_base_address( const void* context, const uint8_t tx_base_address, + const uint8_t rx_base_address ); + +llcc68_status_t llcc68_set_lora_symb_nb_timeout( const void* context, const uint8_t nb_of_symbs ); + +// +// Communication Status Information +// + +/** + * @brief Get the chip status + * + * @param [in] context Chip implementation context. + * @param [out] radio_status Pointer to a structure holding the radio status + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_status( const void* context, llcc68_chip_status_t* radio_status ); + +/** + * @brief Get the current Rx buffer status for both LoRa and GFSK Rx operations + * + * @details This function is used to get the length of the received payload and the start address to be used when + * reading data from the Rx buffer. + * + * @param [in] context Chip implementation context. + * @param [out] rx_buffer_status Pointer to a structure to store the current status + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_rx_buffer_status( const void* context, llcc68_rx_buffer_status_t* rx_buffer_status ); + +/** + * @brief Get the status of the last GFSK packet received + * + * @param [in] context Chip implementation context. + * @param [out] pkt_status Pointer to a structure to store the packet status + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_gfsk_pkt_status( const void* context, llcc68_pkt_status_gfsk_t* pkt_status ); + +/** + * @brief Get the status of the last LoRa packet received + * + * @param [in] context Chip implementation context. + * @param [out] pkt_status Pointer to a structure to store the packet status + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_lora_pkt_status( const void* context, llcc68_pkt_status_lora_t* pkt_status ); + +/** + * @brief Get the instantaneous RSSI value. + * + * @remark This function shall be called when in Rx mode. + * + * @param [in] context Chip implementation context. + * @param [out] rssi_in_dbm Pointer to a variable to store the RSSI value in dBm + * + * @returns Operation status + * + * @see llcc68_set_rx + */ +llcc68_status_t llcc68_get_rssi_inst( const void* context, int16_t* rssi_in_dbm ); + +/** + * @brief Get the statistics about GFSK communication + * + * @param [in] context Chip implementation context. + * @param [out] stats Pointer to a structure to store GFSK-related statistics + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_gfsk_stats( const void* context, llcc68_stats_gfsk_t* stats ); + +/** + * @brief Get the statistics about LoRa communication + * + * @param [in] context Chip implementation context. + * @param [out] stats Pointer to a structure to store LoRa-related statistics + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_lora_stats( const void* context, llcc68_stats_lora_t* stats ); + +/** + * @brief Reset all the statistics for both Lora and GFSK communications + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_reset_stats( const void* context ); + +// +// Miscellaneous +// + +/** + * @brief Perform a hard reset of the chip + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_reset( const void* context ); + +/** + * @brief Wake the radio up from sleep mode. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_wakeup( const void* context ); + +/** + * @brief Get the list of all active errors + * + * @param [in] context Chip implementation context. + * @param [out] errors Pointer to a variable to store the error list + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_device_errors( const void* context, llcc68_errors_mask_t* errors ); + +/** + * @brief Clear all active errors + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_clear_device_errors( const void* context ); + +/** + * @brief Get the parameter corresponding to a GFSK Rx bandwith immediately above the minimum requested one. + * + * @param [in] bw Minimum required bandwith in Hz + * @param [out] param Pointer to a value to store the parameter + * + * @returns Operation status + */ +llcc68_status_t llcc68_get_gfsk_bw_param( const uint32_t bw, uint8_t* param ); + +/** + * @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 llcc68_get_lora_bw_in_hz( llcc68_lora_bw_t bw ); + +/** + * @brief Compute the numerator for LoRa time-on-air computation. + * + * @remark To get the actual time-on-air in second, 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 llcc68_get_lora_time_on_air_numerator( const llcc68_pkt_params_lora_t* pkt_p, + const llcc68_mod_params_lora_t* mod_p ); + +/** + * @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 llcc68_get_lora_time_on_air_in_ms( const llcc68_pkt_params_lora_t* pkt_p, + const llcc68_mod_params_lora_t* mod_p ); + +/** + * @brief Compute the numerator for GFSK time-on-air computation. + * + * @remark To get the actual time-on-air in second, this value has to be divided by the GFSK bitrate in bits per + * second. + * + * @param [in] pkt_p Pointer to the structure holding the GFSK packet parameters + * + * @returns GFSK time-on-air numerator + */ +uint32_t llcc68_get_gfsk_time_on_air_numerator( const llcc68_pkt_params_gfsk_t* pkt_p ); + +/** + * @brief Get the time on air in ms for GFSK transmission + * + * @param [in] pkt_p Pointer to a structure holding the GFSK packet parameters + * @param [in] mod_p Pointer to a structure holding the GFSK modulation parameters + * + * @returns Time-on-air value in ms for GFSK transmission + */ +uint32_t llcc68_get_gfsk_time_on_air_in_ms( const llcc68_pkt_params_gfsk_t* pkt_p, + const llcc68_mod_params_gfsk_t* mod_p ); + +/** + * @brief Generate one or more 32-bit random numbers. + * + * @remark A valid packet type must have been configured with @ref llcc68_set_pkt_type + * before using this command. + * + * @param [in] context Chip implementation context. + * @param [out] numbers Array where numbers will be stored. + * @param [in] n Number of desired random numbers. + * + * @returns Operation status + * + * This code can potentially result in interrupt generation. It is the responsibility of + * the calling code to disable radio interrupts before calling this function, + * and re-enable them afterwards if necessary, or be certain that any interrupts + * generated during this process will not cause undesired side-effects in the software. + * + * Please note that the random numbers produced by the generator do not have a uniform or Gaussian distribution. If + * uniformity is needed, perform appropriate software post-processing. + */ +llcc68_status_t llcc68_get_random_numbers( const void* context, uint32_t* numbers, unsigned int n ); + +/** + * @brief Get the number of PLL steps for a given frequency in Hertz + * + * @param [in] freq_in_hz Frequency in Hertz + * + * @returns Number of PLL steps + */ +uint32_t llcc68_convert_freq_in_hz_to_pll_step( uint32_t freq_in_hz ); + +/** + * @brief Get the number of RTC steps for a given timeout in millisecond + * + * @param [in] timeout_in_ms Timeout in millisecond + * + * @returns Number of RTC steps + */ +uint32_t llcc68_convert_timeout_in_ms_to_rtc_step( uint32_t timeout_in_ms ); + +// +// Registers access +// + +/** + * @brief Configure the boost mode in reception + * + * @remark This configuration is not kept in the retention memory. Rx boosted mode shall be enabled each time the chip + * leaves sleep mode. + * + * @param [in] context Chip implementation context. + * @param [in] state Boost mode activation + * + * @returns Operation status + */ +llcc68_status_t llcc68_cfg_rx_boosted( const void* context, const bool state ); + +/** + * @brief Configure the sync word used in GFSK packet + * + * @param [in] context Chip implementation context. + * @param [in] sync_word Buffer holding the sync word to be configured + * @param [in] sync_word_len Sync word length in byte + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_gfsk_sync_word( const void* context, const uint8_t* sync_word, const uint8_t sync_word_len ); + +/** + * @brief Configure the sync word used in LoRa packet + * + * @remark In the case of a LoRaWAN use case, the two following values are specified: + * - 0x12 for a private LoRaWAN network (default) + * - 0x34 for a public LoRaWAN network + * + * @param [in] context Chip implementation context. + * @param [in] sync_word Sync word to be configured + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_lora_sync_word( const void* context, const uint8_t sync_word ); + +/** + * @brief Configure the seed used to compute CRC in GFSK packet + * + * @param [in] context Chip implementation context. + * @param [in] seed Seed value used to compute the CRC value + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_gfsk_crc_seed( const void* context, uint16_t seed ); + +/** + * @brief Configure the polynomial used to compute CRC in GFSK packet + * + * @param [in] context Chip implementation context. + * @param [in] polynomial Polynomial value used to compute the CRC value + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_gfsk_crc_polynomial( const void* context, const uint16_t polynomial ); + +/** + * @brief Configure the whitening seed used in GFSK packet + * + * @param [in] context Chip implementation context. + * @param [in] seed Seed value used in data whitening + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_gfsk_whitening_seed( const void* context, const uint16_t seed ); + +/** + * @brief Configure the Tx PA clamp + * + * @remark Workaround - On the SX1262, during the chip initialization, calling this function optimize the PA clamping + * threshold. The call must be done after a Power On Reset, or a wake-up from cold Start.(see DS_SX1261-2_V1.2 datasheet + * chapter 15.2) + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_cfg_tx_clamp( const void* context ); + +/** + * @brief Stop the RTC and clear the related event + * + * @remark Workaround - It is advised to call this function after ANY reception with timeout active sequence, which + * stop the RTC and clear the timeout event, if any (see DS_SX1261-2_V1.2 datasheet chapter 15.4) + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +llcc68_status_t llcc68_stop_rtc( const void* context ); + +/** + * @brief Configure the Over Current Protection (OCP) value + * + * @remark The maximum value that can be configured is 63 (i.e. 157.5 mA) + * + * @param [in] context Chip implementation context. + * @param [in] ocp_in_step_of_2_5_ma OCP value given in steps of 2.5 mA + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_ocp_value( const void* context, const uint8_t ocp_in_step_of_2_5_ma ); + +/** + * @brief Configure the internal trimming capacitor values + * + * @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 11.3pF. + * + * @param [in] context Chip implementation context. + * @param [in] trimming_cap_xta Value for the trimming capacitor connected to XTA pin + * @param [in] trimming_cap_xtb Value for the trimming capacitor connected to XTB pin + * + * @returns Operation status + */ +llcc68_status_t llcc68_set_trimming_capacitor_values( const void* context, const uint8_t trimming_cap_xta, + const uint8_t trimming_cap_xtb ); + +#ifdef __cplusplus +} +#endif + +#endif // LLCC68_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/llcc68_hal.h b/src/llcc68_hal.h new file mode 100644 index 0000000..debf400 --- /dev/null +++ b/src/llcc68_hal.h @@ -0,0 +1,133 @@ +/** + * @file llcc68_hal.h + * + * @brief Hardware Abstraction Layer for LLCC68 + * + * Revised BSD License + * Copyright Semtech Corporation 2020. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted 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. + * + * 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 LLCC68_HAL_H +#define LLCC68_HAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +typedef enum llcc68_hal_status_e +{ + LLCC68_HAL_STATUS_OK = 0, + LLCC68_HAL_STATUS_ERROR = 3, +} llcc68_hal_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * Radio data transfer - write + * + * @remark Shall be implemented by the user + * + * @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 + */ +llcc68_hal_status_t llcc68_hal_write( const void* context, const uint8_t* command, const uint16_t command_length, + const uint8_t* data, const uint16_t data_length ); + +/** + * Radio data transfer - read + * + * @remark Shall be implemented by the user + * + * @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 received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + */ +llcc68_hal_status_t llcc68_hal_read( const void* context, const uint8_t* command, const uint16_t command_length, + uint8_t* data, const uint16_t data_length ); + +/** + * Reset the radio + * + * @remark Shall be implemented by the user + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +llcc68_hal_status_t llcc68_hal_reset( const void* context ); + +/** + * Wake the radio up. + * + * @remark Shall be implemented by the user + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +llcc68_hal_status_t llcc68_hal_wakeup( const void* context ); + +#ifdef __cplusplus +} +#endif + +#endif // LLCC68_HAL_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/src/llcc68_regs.h b/src/llcc68_regs.h new file mode 100644 index 0000000..f7573b3 --- /dev/null +++ b/src/llcc68_regs.h @@ -0,0 +1,200 @@ +/** + * @file llcc68_regs.h + * + * @brief LLCC68 register definitions + * + * Revised BSD License + * Copyright Semtech Corporation 2020. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted 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. + * + * 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 LLCC68_REGS_H +#define LLCC68_REGS_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief The address of the register holding the first byte defining the CRC seed + */ +#define LLCC68_REG_CRCSEEDBASEADDRESS 0x06BC + +/** + * @brief The address of the register holding the first byte defining the CRC polynomial + */ +#define LLCC68_REG_CRCPOLYBASEADDRESS 0x06BE + +/** + * @brief The address of the register holding the first byte defining the whitening seed + */ +#define LLCC68_REG_WHITSEEDBASEADDRESS 0x06B8 + +/** + * @brief The address of the register holding the packet configuration + */ +#define LLCC68_REG_PKTPARAMS 0x0704 + +/** + * @brief The addresses of the registers holding SyncWords values + */ +#define LLCC68_REG_SYNCWORDBASEADDRESS 0x06C0 + +/** + * @brief The addresses of the register holding LoRa Modem SyncWord value + * 0x1424: LoRaWAN private network, + * 0x3444: LoRaWAN public network + */ +#define LLCC68_REG_LR_SYNCWORD 0x0740 + +/*! + * The address of the register giving a 32-bit random number + */ +#define LLCC68_REG_RNGBASEADDRESS 0x0819 + +/*! + * The address of the register used to disable the LNA + */ +#define LLCC68_REG_ANA_LNA 0x08E2 + +/*! + * The address of the register used to disable the mixer + */ +#define LLCC68_REG_ANA_MIXER 0x08E5 + +/*! + * The address of the register holding RX Gain value + * 0x94: power saving, + * 0x96: rx boosted + */ +#define LLCC68_REG_RXGAIN 0x08AC + +/** + * @brief Change the value on the device internal trimming capacitor + */ +#define LLCC68_REG_XTATRIM 0x0911 + +/** + * @brief Set the current max value in the over current protection + */ +#define LLCC68_REG_OCP 0x08E7 + +/** + * @brief WORKAROUND - Optimizing the Inverted IQ Operation, see DS_SX1261-2_V1.2 datasheet chapter 15.4 + */ +#define LLCC68_REG_IRQ_POLARITY 0x0736 + +/** + * @brief WORKAROUND - Modulation Quality with 500 kHz LoRa Bandwidth, see DS_SX1261-2_V1.2 datasheet chapter 15.1 + */ +#define LLCC68_REG_TX_MODULATION 0x0889 + +/** + * @brief WORKAROUND - Better resistance to antenna mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2 + */ +#define LLCC68_REG_TX_CLAMP_CFG 0x08D8 +#define LLCC68_REG_TX_CLAMP_CFG_POS ( 1U ) +#define LLCC68_REG_TX_CLAMP_CFG_MASK ( 0x0FUL << LLCC68_REG_TX_CLAMP_CFG_POS ) + +/** + * @brief RTC control + */ +#define LLCC68_REG_RTC_CTRL 0x0902 + +/** + * @brief Event clear + */ +#define LLCC68_REG_EVT_CLR 0x0944 +#define LLCC68_REG_EVT_CLR_TIMEOUT_POS ( 1U ) +#define LLCC68_REG_EVT_CLR_TIMEOUT_MASK ( 0x01UL << LLCC68_REG_EVT_CLR_TIMEOUT_POS ) + +/** + * @brief RX address pointer + */ +#define LLCC68_REG_RX_ADDRESS_POINTER 0x0803 + +/** + * @brief RX/TX payload length + */ +#define LLCC68_REG_RXTX_PAYLOAD_LEN 0x06BB + +/** + * @brief Output disable + */ +#define LLCC68_REG_OUT_DIS_REG 0x0580 +#define LLCC68_REG_OUT_DIS_REG_DIO3_POS ( 3U ) +#define LLCC68_REG_OUT_DIS_REG_DIO3_MASK ( 0x01UL << LLCC68_REG_OUT_DIS_REG_DIO3_POS ) + +/** + * @brief Input enable + */ +#define LLCC68_REG_IN_EN_REG 0x0583 +#define LLCC68_REG_IN_EN_REG_DIO3_POS ( 3U ) +#define LLCC68_REG_IN_EN_REG_DIO3_MASK ( 0x01UL << LLCC68_REG_IN_EN_REG_DIO3_POS ) + +/** + * @brief TX bitbang A + */ +#define LLCC68_REG_BITBANG_A_REG 0x0680 +#define LLCC68_REG_BITBANG_A_REG_ENABLE_POS ( 4U ) +#define LLCC68_REG_BITBANG_A_REG_ENABLE_MASK ( 0x07UL << LLCC68_REG_BITBANG_A_REG_ENABLE_POS ) +#define LLCC68_REG_BITBANG_A_REG_ENABLE_VAL ( 0x01UL << LLCC68_REG_BITBANG_A_REG_ENABLE_POS ) + +/** + * @brief TX bitbang B + */ +#define LLCC68_REG_BITBANG_B_REG 0x0587 +#define LLCC68_REG_BITBANG_B_REG_ENABLE_POS ( 0U ) +#define LLCC68_REG_BITBANG_B_REG_ENABLE_MASK ( 0x0FUL << LLCC68_REG_BITBANG_B_REG_ENABLE_POS ) +#define LLCC68_REG_BITBANG_B_REG_ENABLE_VAL ( 0x0CUL << LLCC68_REG_BITBANG_B_REG_ENABLE_POS ) + +/** + * @brief Number of symbols given as LLCC68_REG_LR_SYNCH_TIMEOUT[7:3] * 2 ^ (2*LLCC68_REG_LR_SYNCH_TIMEOUT[2:0] + 1) + */ +#define LLCC68_REG_LR_SYNCH_TIMEOUT 0x0706 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LLCC68_REGS_H + +/* --- EOF ------------------------------------------------------------------ */