neptunium-firmware/Drivers/sx126x_driver/src/sx126x.c

1544 lines
53 KiB
C
Raw Normal View History

/**
* @file sx126x.c
*
* @brief SX126x radio driver implementation
*
* The Clear BSD License
* Copyright Semtech Corporation 2021. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the disclaimer
* below) provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Semtech corporation nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
* THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* -----------------------------------------------------------------------------
* --- DEPENDENCIES ------------------------------------------------------------
*/
#include <stddef.h>
#include "sx126x.h"
#include "sx126x_hal.h"
#include "sx126x_regs.h"
/*
* -----------------------------------------------------------------------------
* --- PRIVATE MACROS-----------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PRIVATE CONSTANTS -------------------------------------------------------
*/
/**
* @brief Internal frequency of the radio
*/
#define SX126X_XTAL_FREQ 32000000UL
/**
* @brief Internal frequency of the radio
*/
#define SX126X_RTC_FREQ_IN_HZ 64000UL
/**
* @brief Scaling factor used to perform fixed-point operations
*/
#define SX126X_PLL_STEP_SHIFT_AMOUNT ( 14 )
/**
* @brief PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
*/
#define SX126X_PLL_STEP_SCALED ( SX126X_XTAL_FREQ >> ( 25 - SX126X_PLL_STEP_SHIFT_AMOUNT ) )
/*
* -----------------------------------------------------------------------------
* --- PRIVATE TYPES -----------------------------------------------------------
*/
/**
* Commands Interface
*/
typedef enum sx126x_commands_e
{
// Operational Modes Functions
SX126X_SET_SLEEP = 0x84,
SX126X_SET_STANDBY = 0x80,
SX126X_SET_FS = 0xC1,
SX126X_SET_TX = 0x83,
SX126X_SET_RX = 0x82,
SX126X_SET_STOP_TIMER_ON_PREAMBLE = 0x9F,
SX126X_SET_RX_DUTY_CYCLE = 0x94,
SX126X_SET_CAD = 0xC5,
SX126X_SET_TX_CONTINUOUS_WAVE = 0xD1,
SX126X_SET_TX_INFINITE_PREAMBLE = 0xD2,
SX126X_SET_REGULATOR_MODE = 0x96,
SX126X_CALIBRATE = 0x89,
SX126X_CALIBRATE_IMAGE = 0x98,
SX126X_SET_PA_CFG = 0x95,
SX126X_SET_RX_TX_FALLBACK_MODE = 0x93,
// Registers and buffer Access
SX126X_WRITE_REGISTER = 0x0D,
SX126X_READ_REGISTER = 0x1D,
SX126X_WRITE_BUFFER = 0x0E,
SX126X_READ_BUFFER = 0x1E,
// DIO and IRQ Control Functions
SX126X_SET_DIO_IRQ_PARAMS = 0x08,
SX126X_GET_IRQ_STATUS = 0x12,
SX126X_CLR_IRQ_STATUS = 0x02,
SX126X_SET_DIO2_AS_RF_SWITCH_CTRL = 0x9D,
SX126X_SET_DIO3_AS_TCXO_CTRL = 0x97,
// RF Modulation and Packet-Related Functions
SX126X_SET_RF_FREQUENCY = 0x86,
SX126X_SET_PKT_TYPE = 0x8A,
SX126X_GET_PKT_TYPE = 0x11,
SX126X_SET_TX_PARAMS = 0x8E,
SX126X_SET_MODULATION_PARAMS = 0x8B,
SX126X_SET_PKT_PARAMS = 0x8C,
SX126X_SET_CAD_PARAMS = 0x88,
SX126X_SET_BUFFER_BASE_ADDRESS = 0x8F,
SX126X_SET_LORA_SYMB_NUM_TIMEOUT = 0xA0,
// Communication Status Information
SX126X_GET_STATUS = 0xC0,
SX126X_GET_RX_BUFFER_STATUS = 0x13,
SX126X_GET_PKT_STATUS = 0x14,
SX126X_GET_RSSI_INST = 0x15,
SX126X_GET_STATS = 0x10,
SX126X_RESET_STATS = 0x00,
// Miscellaneous
SX126X_GET_DEVICE_ERRORS = 0x17,
SX126X_CLR_DEVICE_ERRORS = 0x07,
} sx126x_commands_t;
/**
* Commands Interface buffer sizes
*/
typedef enum sx126x_commands_size_e
{
// Operational Modes Functions
SX126X_SIZE_SET_SLEEP = 2,
SX126X_SIZE_SET_STANDBY = 2,
SX126X_SIZE_SET_FS = 1,
SX126X_SIZE_SET_TX = 4,
SX126X_SIZE_SET_RX = 4,
SX126X_SIZE_SET_STOP_TIMER_ON_PREAMBLE = 2,
SX126X_SIZE_SET_RX_DUTY_CYCLE = 7,
SX126X_SIZE_SET_CAD = 1,
SX126X_SIZE_SET_TX_CONTINUOUS_WAVE = 1,
SX126X_SIZE_SET_TX_INFINITE_PREAMBLE = 1,
SX126X_SIZE_SET_REGULATOR_MODE = 2,
SX126X_SIZE_CALIBRATE = 2,
SX126X_SIZE_CALIBRATE_IMAGE = 3,
SX126X_SIZE_SET_PA_CFG = 5,
SX126X_SIZE_SET_RX_TX_FALLBACK_MODE = 2,
// Registers and buffer Access
// Full size: this value plus buffer size
SX126X_SIZE_WRITE_REGISTER = 3,
// Full size: this value plus buffer size
SX126X_SIZE_READ_REGISTER = 4,
// Full size: this value plus buffer size
SX126X_SIZE_WRITE_BUFFER = 2,
// Full size: this value plus buffer size
SX126X_SIZE_READ_BUFFER = 3,
// DIO and IRQ Control Functions
SX126X_SIZE_SET_DIO_IRQ_PARAMS = 9,
SX126X_SIZE_GET_IRQ_STATUS = 2,
SX126X_SIZE_CLR_IRQ_STATUS = 3,
SX126X_SIZE_SET_DIO2_AS_RF_SWITCH_CTRL = 2,
SX126X_SIZE_SET_DIO3_AS_TCXO_CTRL = 5,
// RF Modulation and Packet-Related Functions
SX126X_SIZE_SET_RF_FREQUENCY = 5,
SX126X_SIZE_SET_PKT_TYPE = 2,
SX126X_SIZE_GET_PKT_TYPE = 2,
SX126X_SIZE_SET_TX_PARAMS = 3,
SX126X_SIZE_SET_MODULATION_PARAMS_GFSK = 9,
SX126X_SIZE_SET_MODULATION_PARAMS_BPSK = 5,
SX126X_SIZE_SET_MODULATION_PARAMS_LORA = 5,
SX126X_SIZE_SET_PKT_PARAMS_GFSK = 10,
SX126X_SIZE_SET_PKT_PARAMS_BPSK = 2,
SX126X_SIZE_SET_PKT_PARAMS_LORA = 7,
SX126X_SIZE_SET_CAD_PARAMS = 8,
SX126X_SIZE_SET_BUFFER_BASE_ADDRESS = 3,
SX126X_SIZE_SET_LORA_SYMB_NUM_TIMEOUT = 2,
// Communication Status Information
SX126X_SIZE_GET_STATUS = 1,
SX126X_SIZE_GET_RX_BUFFER_STATUS = 2,
SX126X_SIZE_GET_PKT_STATUS = 2,
SX126X_SIZE_GET_RSSI_INST = 2,
SX126X_SIZE_GET_STATS = 2,
SX126X_SIZE_RESET_STATS = 7,
// Miscellaneous
SX126X_SIZE_GET_DEVICE_ERRORS = 2,
SX126X_SIZE_CLR_DEVICE_ERRORS = 3,
SX126X_SIZE_MAX_BUFFER = 255,
SX126X_SIZE_DUMMY_BYTE = 1,
} sx126x_commands_size_t;
typedef struct
{
uint32_t bw;
uint8_t param;
} gfsk_bw_t;
gfsk_bw_t gfsk_bw[] = {
{ 4800, SX126X_GFSK_BW_4800 }, { 5800, SX126X_GFSK_BW_5800 }, { 7300, SX126X_GFSK_BW_7300 },
{ 9700, SX126X_GFSK_BW_9700 }, { 11700, SX126X_GFSK_BW_11700 }, { 14600, SX126X_GFSK_BW_14600 },
{ 19500, SX126X_GFSK_BW_19500 }, { 23400, SX126X_GFSK_BW_23400 }, { 29300, SX126X_GFSK_BW_29300 },
{ 39000, SX126X_GFSK_BW_39000 }, { 46900, SX126X_GFSK_BW_46900 }, { 58600, SX126X_GFSK_BW_58600 },
{ 78200, SX126X_GFSK_BW_78200 }, { 93800, SX126X_GFSK_BW_93800 }, { 117300, SX126X_GFSK_BW_117300 },
{ 156200, SX126X_GFSK_BW_156200 }, { 187200, SX126X_GFSK_BW_187200 }, { 234300, SX126X_GFSK_BW_234300 },
{ 312000, SX126X_GFSK_BW_312000 }, { 373600, SX126X_GFSK_BW_373600 }, { 467000, SX126X_GFSK_BW_467000 },
};
/*
* -----------------------------------------------------------------------------
* --- PRIVATE VARIABLES -------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PRIVATE FUNCTIONS DECLARATION -------------------------------------------
*/
/**
* @brief 15.1.2 Workaround
*
* @remark Before any packet transmission, bit #2 of SX126X_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 sx126x_status_t sx126x_tx_modulation_workaround( const void* context, sx126x_pkt_type_t pkt_type,
sx126x_lora_bw_t bw );
static inline uint32_t sx126x_get_gfsk_crc_len_in_bytes( sx126x_gfsk_crc_types_t crc_type );
/*
* -----------------------------------------------------------------------------
* --- PUBLIC FUNCTIONS DEFINITION ---------------------------------------------
*/
sx126x_status_t sx126x_set_sleep( const void* context, const sx126x_sleep_cfgs_t cfg )
{
const uint8_t buf[SX126X_SIZE_SET_SLEEP] = {
SX126X_SET_SLEEP,
( uint8_t ) cfg,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_SLEEP, 0, 0 );
}
sx126x_status_t sx126x_set_standby( const void* context, const sx126x_standby_cfg_t cfg )
{
const uint8_t buf[SX126X_SIZE_SET_STANDBY] = {
SX126X_SET_STANDBY,
( uint8_t ) cfg,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_STANDBY, 0, 0 );
}
sx126x_status_t sx126x_set_fs( const void* context )
{
const uint8_t buf[SX126X_SIZE_SET_FS] = {
SX126X_SET_FS,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_FS, 0, 0 );
}
sx126x_status_t sx126x_set_tx( const void* context, const uint32_t timeout_in_ms )
{
if( timeout_in_ms > SX126X_MAX_TIMEOUT_IN_MS )
{
return SX126X_STATUS_UNKNOWN_VALUE;
}
const uint32_t timeout_in_rtc_step = sx126x_convert_timeout_in_ms_to_rtc_step( timeout_in_ms );
return sx126x_set_tx_with_timeout_in_rtc_step( context, timeout_in_rtc_step );
}
sx126x_status_t sx126x_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step )
{
const uint8_t buf[SX126X_SIZE_SET_TX] = {
SX126X_SET_TX,
( uint8_t )( timeout_in_rtc_step >> 16 ),
( uint8_t )( timeout_in_rtc_step >> 8 ),
( uint8_t )( timeout_in_rtc_step >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_TX, 0, 0 );
}
sx126x_status_t sx126x_set_rx( const void* context, const uint32_t timeout_in_ms )
{
if( timeout_in_ms > SX126X_MAX_TIMEOUT_IN_MS )
{
return SX126X_STATUS_UNKNOWN_VALUE;
}
const uint32_t timeout_in_rtc_step = sx126x_convert_timeout_in_ms_to_rtc_step( timeout_in_ms );
return sx126x_set_rx_with_timeout_in_rtc_step( context, timeout_in_rtc_step );
}
sx126x_status_t sx126x_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step )
{
const uint8_t buf[SX126X_SIZE_SET_RX] = {
SX126X_SET_RX,
( uint8_t )( timeout_in_rtc_step >> 16 ),
( uint8_t )( timeout_in_rtc_step >> 8 ),
( uint8_t )( timeout_in_rtc_step >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_RX, 0, 0 );
}
sx126x_status_t sx126x_stop_timer_on_preamble( const void* context, const bool enable )
{
const uint8_t buf[SX126X_SIZE_SET_STOP_TIMER_ON_PREAMBLE] = {
SX126X_SET_STOP_TIMER_ON_PREAMBLE,
( enable == true ) ? 1 : 0,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_STOP_TIMER_ON_PREAMBLE, 0, 0 );
}
sx126x_status_t sx126x_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 = sx126x_convert_timeout_in_ms_to_rtc_step( rx_time_in_ms );
const uint32_t sleep_time_in_rtc_step = sx126x_convert_timeout_in_ms_to_rtc_step( sleep_time_in_ms );
return sx126x_set_rx_duty_cycle_with_timings_in_rtc_step( context, rx_time_in_rtc_step, sleep_time_in_rtc_step );
}
sx126x_status_t sx126x_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 )
{
const uint8_t buf[SX126X_SIZE_SET_RX_DUTY_CYCLE] = {
SX126X_SET_RX_DUTY_CYCLE,
( uint8_t )( rx_time_in_rtc_step >> 16 ),
( uint8_t )( rx_time_in_rtc_step >> 8 ),
( uint8_t )( rx_time_in_rtc_step >> 0 ),
( uint8_t )( sleep_time_in_rtc_step >> 16 ),
( uint8_t )( sleep_time_in_rtc_step >> 8 ),
( uint8_t )( sleep_time_in_rtc_step >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_RX_DUTY_CYCLE, 0, 0 );
}
sx126x_status_t sx126x_set_cad( const void* context )
{
const uint8_t buf[SX126X_SIZE_SET_CAD] = {
SX126X_SET_CAD,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_CAD, 0, 0 );
}
sx126x_status_t sx126x_set_tx_cw( const void* context )
{
const uint8_t buf[SX126X_SIZE_SET_TX_CONTINUOUS_WAVE] = {
SX126X_SET_TX_CONTINUOUS_WAVE,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_TX_CONTINUOUS_WAVE, 0, 0 );
}
sx126x_status_t sx126x_set_tx_infinite_preamble( const void* context )
{
const uint8_t buf[SX126X_SIZE_SET_TX_INFINITE_PREAMBLE] = {
SX126X_SET_TX_INFINITE_PREAMBLE,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_TX_INFINITE_PREAMBLE, 0, 0 );
}
sx126x_status_t sx126x_set_reg_mode( const void* context, const sx126x_reg_mod_t mode )
{
const uint8_t buf[SX126X_SIZE_SET_REGULATOR_MODE] = {
SX126X_SET_REGULATOR_MODE,
( uint8_t ) mode,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_REGULATOR_MODE, 0, 0 );
}
sx126x_status_t sx126x_cal( const void* context, const sx126x_cal_mask_t param )
{
const uint8_t buf[SX126X_SIZE_CALIBRATE] = {
SX126X_CALIBRATE,
( uint8_t ) param,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_CALIBRATE, 0, 0 );
}
sx126x_status_t sx126x_cal_img( const void* context, const uint8_t freq1, const uint8_t freq2 )
{
const uint8_t buf[SX126X_SIZE_CALIBRATE_IMAGE] = {
SX126X_CALIBRATE_IMAGE,
freq1,
freq2,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_CALIBRATE_IMAGE, 0, 0 );
}
sx126x_status_t sx126x_cal_img_in_mhz( const void* context, const uint16_t freq1_in_mhz, const uint16_t freq2_in_mhz )
{
// Perform a floor() to get a value for freq1 corresponding to a frequency lower than or equal to freq1_in_mhz
const uint8_t freq1 = freq1_in_mhz / SX126X_IMAGE_CALIBRATION_STEP_IN_MHZ;
// Perform a ceil() to get a value for freq2 corresponding to a frequency higher than or equal to freq2_in_mhz
const uint8_t freq2 =
( freq2_in_mhz + SX126X_IMAGE_CALIBRATION_STEP_IN_MHZ - 1 ) / SX126X_IMAGE_CALIBRATION_STEP_IN_MHZ;
return sx126x_cal_img( context, freq1, freq2 );
}
sx126x_status_t sx126x_set_pa_cfg( const void* context, const sx126x_pa_cfg_params_t* params )
{
const uint8_t buf[SX126X_SIZE_SET_PA_CFG] = {
SX126X_SET_PA_CFG, params->pa_duty_cycle, params->hp_max, params->device_sel, params->pa_lut,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_PA_CFG, 0, 0 );
}
sx126x_status_t sx126x_set_rx_tx_fallback_mode( const void* context, const sx126x_fallback_modes_t fallback_mode )
{
const uint8_t buf[SX126X_SIZE_SET_RX_TX_FALLBACK_MODE] = {
SX126X_SET_RX_TX_FALLBACK_MODE,
( uint8_t ) fallback_mode,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_RX_TX_FALLBACK_MODE, 0, 0 );
}
//
// Registers and buffer Access
//
sx126x_status_t sx126x_write_register( const void* context, const uint16_t address, const uint8_t* buffer,
const uint8_t size )
{
const uint8_t buf[SX126X_SIZE_WRITE_REGISTER] = {
SX126X_WRITE_REGISTER,
( uint8_t )( address >> 8 ),
( uint8_t )( address >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_WRITE_REGISTER, buffer, size );
}
sx126x_status_t sx126x_read_register( const void* context, const uint16_t address, uint8_t* buffer, const uint8_t size )
{
const uint8_t buf[SX126X_SIZE_READ_REGISTER] = {
SX126X_READ_REGISTER,
( uint8_t )( address >> 8 ),
( uint8_t )( address >> 0 ),
SX126X_NOP,
};
return ( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_READ_REGISTER, buffer, size );
}
sx126x_status_t sx126x_write_buffer( const void* context, const uint8_t offset, const uint8_t* buffer,
const uint8_t size )
{
const uint8_t buf[SX126X_SIZE_WRITE_BUFFER] = {
SX126X_WRITE_BUFFER,
offset,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_WRITE_BUFFER, buffer, size );
}
sx126x_status_t sx126x_read_buffer( const void* context, const uint8_t offset, uint8_t* buffer, const uint8_t size )
{
const uint8_t buf[SX126X_SIZE_READ_BUFFER] = {
SX126X_READ_BUFFER,
offset,
SX126X_NOP,
};
return ( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_READ_BUFFER, buffer, size );
}
//
// DIO and IRQ Control Functions
//
sx126x_status_t sx126x_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 )
{
const uint8_t buf[SX126X_SIZE_SET_DIO_IRQ_PARAMS] = {
SX126X_SET_DIO_IRQ_PARAMS, ( uint8_t )( irq_mask >> 8 ), ( uint8_t )( irq_mask >> 0 ),
( uint8_t )( dio1_mask >> 8 ), ( uint8_t )( dio1_mask >> 0 ), ( uint8_t )( dio2_mask >> 8 ),
( uint8_t )( dio2_mask >> 0 ), ( uint8_t )( dio3_mask >> 8 ), ( uint8_t )( dio3_mask >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_DIO_IRQ_PARAMS, 0, 0 );
}
sx126x_status_t sx126x_get_irq_status( const void* context, sx126x_irq_mask_t* irq )
{
const uint8_t buf[SX126X_SIZE_GET_IRQ_STATUS] = {
SX126X_GET_IRQ_STATUS,
SX126X_NOP,
};
uint8_t irq_local[sizeof( sx126x_irq_mask_t )] = { 0x00 };
const sx126x_status_t status = ( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_IRQ_STATUS,
irq_local, sizeof( sx126x_irq_mask_t ) );
if( status == SX126X_STATUS_OK )
{
*irq = ( ( sx126x_irq_mask_t ) irq_local[0] << 8 ) + ( ( sx126x_irq_mask_t ) irq_local[1] << 0 );
}
return status;
}
sx126x_status_t sx126x_clear_irq_status( const void* context, const sx126x_irq_mask_t irq_mask )
{
const uint8_t buf[SX126X_SIZE_CLR_IRQ_STATUS] = {
SX126X_CLR_IRQ_STATUS,
( uint8_t )( irq_mask >> 8 ),
( uint8_t )( irq_mask >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_CLR_IRQ_STATUS, 0, 0 );
}
sx126x_status_t sx126x_get_and_clear_irq_status( const void* context, sx126x_irq_mask_t* irq )
{
sx126x_irq_mask_t sx126x_irq_mask = SX126X_IRQ_NONE;
sx126x_status_t status = sx126x_get_irq_status( context, &sx126x_irq_mask );
if( ( status == SX126X_STATUS_OK ) && ( sx126x_irq_mask != 0 ) )
{
status = sx126x_clear_irq_status( context, sx126x_irq_mask );
}
if( ( status == SX126X_STATUS_OK ) && ( irq != NULL ) )
{
*irq = sx126x_irq_mask;
}
return status;
}
sx126x_status_t sx126x_set_dio2_as_rf_sw_ctrl( const void* context, const bool enable )
{
const uint8_t buf[SX126X_SIZE_SET_DIO2_AS_RF_SWITCH_CTRL] = {
SX126X_SET_DIO2_AS_RF_SWITCH_CTRL,
( enable == true ) ? 1 : 0,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_DIO2_AS_RF_SWITCH_CTRL, 0, 0 );
}
sx126x_status_t sx126x_set_dio3_as_tcxo_ctrl( const void* context, const sx126x_tcxo_ctrl_voltages_t tcxo_voltage,
const uint32_t timeout )
{
const uint8_t buf[SX126X_SIZE_SET_DIO3_AS_TCXO_CTRL] = {
SX126X_SET_DIO3_AS_TCXO_CTRL, ( uint8_t ) tcxo_voltage, ( uint8_t )( timeout >> 16 ),
( uint8_t )( timeout >> 8 ), ( uint8_t )( timeout >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_DIO3_AS_TCXO_CTRL, 0, 0 );
}
//
// RF Modulation and Packet-Related Functions
//
sx126x_status_t sx126x_set_rf_freq( const void* context, const uint32_t freq_in_hz )
{
const uint32_t freq = sx126x_convert_freq_in_hz_to_pll_step( freq_in_hz );
return sx126x_set_rf_freq_in_pll_steps( context, freq );
}
sx126x_status_t sx126x_set_rf_freq_in_pll_steps( const void* context, const uint32_t freq )
{
const uint8_t buf[SX126X_SIZE_SET_RF_FREQUENCY] = {
SX126X_SET_RF_FREQUENCY, ( uint8_t )( freq >> 24 ), ( uint8_t )( freq >> 16 ),
( uint8_t )( freq >> 8 ), ( uint8_t )( freq >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_RF_FREQUENCY, 0, 0 );
}
sx126x_status_t sx126x_set_pkt_type( const void* context, const sx126x_pkt_type_t pkt_type )
{
const uint8_t buf[SX126X_SIZE_SET_PKT_TYPE] = {
SX126X_SET_PKT_TYPE,
( uint8_t ) pkt_type,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_PKT_TYPE, 0, 0 );
}
sx126x_status_t sx126x_get_pkt_type( const void* context, sx126x_pkt_type_t* pkt_type )
{
const uint8_t buf[SX126X_SIZE_GET_PKT_TYPE] = {
SX126X_GET_PKT_TYPE,
SX126X_NOP,
};
return ( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_PKT_TYPE, ( uint8_t* ) pkt_type, 1 );
}
sx126x_status_t sx126x_set_tx_params( const void* context, const int8_t pwr_in_dbm, const sx126x_ramp_time_t ramp_time )
{
const uint8_t buf[SX126X_SIZE_SET_TX_PARAMS] = {
SX126X_SET_TX_PARAMS,
( uint8_t ) pwr_in_dbm,
( uint8_t ) ramp_time,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_TX_PARAMS, 0, 0 );
}
sx126x_status_t sx126x_set_gfsk_mod_params( const void* context, const sx126x_mod_params_gfsk_t* params )
{
const uint32_t bitrate = ( uint32_t )( 32 * SX126X_XTAL_FREQ / params->br_in_bps );
const uint32_t fdev = sx126x_convert_freq_in_hz_to_pll_step( params->fdev_in_hz );
const uint8_t buf[SX126X_SIZE_SET_MODULATION_PARAMS_GFSK] = {
SX126X_SET_MODULATION_PARAMS, ( uint8_t )( bitrate >> 16 ), ( uint8_t )( bitrate >> 8 ),
( uint8_t )( bitrate >> 0 ), ( uint8_t )( params->pulse_shape ), params->bw_dsb_param,
( uint8_t )( fdev >> 16 ), ( uint8_t )( fdev >> 8 ), ( uint8_t )( fdev >> 0 ),
};
sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_MODULATION_PARAMS_GFSK, 0, 0 );
if( status == SX126X_STATUS_OK )
{
// WORKAROUND - Modulation Quality with 500 kHz LoRa Bandwidth, see DS_SX1261-2_V1.2 datasheet chapter 15.1
status = sx126x_tx_modulation_workaround( context, SX126X_PKT_TYPE_GFSK, ( sx126x_lora_bw_t ) 0 );
// WORKAROUND END
}
return status;
}
sx126x_status_t sx126x_set_bpsk_mod_params( const void* context, const sx126x_mod_params_bpsk_t* params )
{
const uint32_t bitrate = ( uint32_t )( 32 * SX126X_XTAL_FREQ / params->br_in_bps );
const uint8_t buf[SX126X_SIZE_SET_MODULATION_PARAMS_BPSK] = {
SX126X_SET_MODULATION_PARAMS, ( uint8_t )( bitrate >> 16 ), ( uint8_t )( bitrate >> 8 ),
( uint8_t )( bitrate >> 0 ), ( uint8_t )( params->pulse_shape ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_MODULATION_PARAMS_BPSK, 0, 0 );
}
sx126x_status_t sx126x_set_lora_mod_params( const void* context, const sx126x_mod_params_lora_t* params )
{
const uint8_t buf[SX126X_SIZE_SET_MODULATION_PARAMS_LORA] = {
SX126X_SET_MODULATION_PARAMS, ( uint8_t )( params->sf ), ( uint8_t )( params->bw ),
( uint8_t )( params->cr ), params->ldro & 0x01,
};
sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_MODULATION_PARAMS_LORA, 0, 0 );
if( status == SX126X_STATUS_OK )
{
// WORKAROUND - Modulation Quality with 500 kHz LoRa Bandwidth, see datasheet DS_SX1261-2_V1.2 §15.1
status = sx126x_tx_modulation_workaround( context, SX126X_PKT_TYPE_LORA, params->bw );
// WORKAROUND END
}
return status;
}
sx126x_status_t sx126x_set_gfsk_pkt_params( const void* context, const sx126x_pkt_params_gfsk_t* params )
{
const uint8_t buf[SX126X_SIZE_SET_PKT_PARAMS_GFSK] = {
SX126X_SET_PKT_PARAMS,
( uint8_t )( params->preamble_len_in_bits >> 8 ),
( uint8_t )( params->preamble_len_in_bits >> 0 ),
( uint8_t )( params->preamble_detector ),
params->sync_word_len_in_bits,
( uint8_t )( params->address_filtering ),
( uint8_t )( params->header_type ),
params->pld_len_in_bytes,
( uint8_t )( params->crc_type ),
( uint8_t )( params->dc_free ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_PKT_PARAMS_GFSK, 0, 0 );
}
sx126x_status_t sx126x_set_bpsk_pkt_params( const void* context, const sx126x_pkt_params_bpsk_t* params )
{
const uint8_t buf[SX126X_SIZE_SET_PKT_PARAMS_BPSK] = {
SX126X_SET_PKT_PARAMS,
params->pld_len_in_bytes,
};
sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_PKT_PARAMS_BPSK, 0, 0 );
if( status != SX126X_STATUS_OK )
{
return status;
}
const uint8_t buf2[] = {
( uint8_t )( params->ramp_up_delay >> 8 ), ( uint8_t )( params->ramp_up_delay >> 0 ),
( uint8_t )( params->ramp_down_delay >> 8 ), ( uint8_t )( params->ramp_down_delay >> 0 ),
( uint8_t )( params->pld_len_in_bits >> 8 ), ( uint8_t )( params->pld_len_in_bits >> 0 ),
};
return sx126x_write_register( context, 0x00F0, buf2, sizeof( buf2 ) );
}
sx126x_status_t sx126x_set_lora_pkt_params( const void* context, const sx126x_pkt_params_lora_t* params )
{
const uint8_t buf[SX126X_SIZE_SET_PKT_PARAMS_LORA] = {
SX126X_SET_PKT_PARAMS,
( uint8_t )( params->preamble_len_in_symb >> 8 ),
( uint8_t )( params->preamble_len_in_symb >> 0 ),
( uint8_t )( params->header_type ),
params->pld_len_in_bytes,
( uint8_t )( params->crc_is_on ? 1 : 0 ),
( uint8_t )( params->invert_iq_is_on ? 1 : 0 ),
};
sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_PKT_PARAMS_LORA, 0, 0 );
// WORKAROUND - Optimizing the Inverted IQ Operation, see datasheet DS_SX1261-2_V1.2 §15.4
if( status == SX126X_STATUS_OK )
{
uint8_t reg_value = 0;
status = sx126x_read_register( context, SX126X_REG_IQ_POLARITY, &reg_value, 1 );
if( status == SX126X_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 = sx126x_write_register( context, SX126X_REG_IQ_POLARITY, &reg_value, 1 );
}
}
// WORKAROUND END
return status;
}
sx126x_status_t sx126x_set_gfsk_pkt_address( const void* context, const uint8_t node_address,
const uint8_t broadcast_address )
{
const uint8_t addresses[2] = { node_address, broadcast_address };
return sx126x_write_register( context, SX126X_REG_GFSK_NODE_ADDRESS, addresses, 2 );
}
sx126x_status_t sx126x_set_cad_params( const void* context, const sx126x_cad_params_t* params )
{
const uint8_t buf[SX126X_SIZE_SET_CAD_PARAMS] = {
SX126X_SET_CAD_PARAMS,
( uint8_t ) params->cad_symb_nb,
params->cad_detect_peak,
params->cad_detect_min,
( uint8_t ) params->cad_exit_mode,
( uint8_t )( params->cad_timeout >> 16 ),
( uint8_t )( params->cad_timeout >> 8 ),
( uint8_t )( params->cad_timeout >> 0 ),
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_CAD_PARAMS, 0, 0 );
}
sx126x_status_t sx126x_set_buffer_base_address( const void* context, const uint8_t tx_base_address,
const uint8_t rx_base_address )
{
const uint8_t buf[SX126X_SIZE_SET_BUFFER_BASE_ADDRESS] = {
SX126X_SET_BUFFER_BASE_ADDRESS,
tx_base_address,
rx_base_address,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_BUFFER_BASE_ADDRESS, 0, 0 );
}
sx126x_status_t sx126x_set_lora_symb_nb_timeout( const void* context, const uint8_t nb_of_symbs )
{
uint8_t exp = 0;
uint8_t mant =
( ( ( nb_of_symbs > SX126X_MAX_LORA_SYMB_NUM_TIMEOUT ) ? SX126X_MAX_LORA_SYMB_NUM_TIMEOUT : nb_of_symbs ) +
1 ) >>
1;
while( mant > 31 )
{
mant = ( mant + 3 ) >> 2;
exp++;
}
const uint8_t buf[SX126X_SIZE_SET_LORA_SYMB_NUM_TIMEOUT] = {
SX126X_SET_LORA_SYMB_NUM_TIMEOUT,
mant << ( 2 * exp + 1 ),
};
sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_SET_LORA_SYMB_NUM_TIMEOUT, 0, 0 );
if( ( status == SX126X_STATUS_OK ) && ( nb_of_symbs > 0 ) )
{
uint8_t reg = exp + ( mant << 3 );
status = sx126x_write_register( context, SX126X_REG_LR_SYNCH_TIMEOUT, &reg, 1 );
}
return status;
}
//
// Communication Status Information
//
sx126x_status_t sx126x_get_status( const void* context, sx126x_chip_status_t* radio_status )
{
const uint8_t buf[SX126X_SIZE_GET_STATUS] = {
SX126X_GET_STATUS,
};
uint8_t status_local = 0;
const sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_STATUS, &status_local, 1 );
if( status == SX126X_STATUS_OK )
{
radio_status->cmd_status =
( sx126x_cmd_status_t )( ( status_local & SX126X_CMD_STATUS_MASK ) >> SX126X_CMD_STATUS_POS );
radio_status->chip_mode =
( sx126x_chip_modes_t )( ( status_local & SX126X_CHIP_MODES_MASK ) >> SX126X_CHIP_MODES_POS );
}
return status;
}
sx126x_status_t sx126x_get_rx_buffer_status( const void* context, sx126x_rx_buffer_status_t* rx_buffer_status )
{
const uint8_t buf[SX126X_SIZE_GET_RX_BUFFER_STATUS] = {
SX126X_GET_RX_BUFFER_STATUS,
SX126X_NOP,
};
uint8_t status_local[sizeof( sx126x_rx_buffer_status_t )] = { 0x00 };
const sx126x_status_t status = ( sx126x_status_t ) sx126x_hal_read(
context, buf, SX126X_SIZE_GET_RX_BUFFER_STATUS, status_local, sizeof( sx126x_rx_buffer_status_t ) );
if( status == SX126X_STATUS_OK )
{
rx_buffer_status->pld_len_in_bytes = status_local[0];
rx_buffer_status->buffer_start_pointer = status_local[1];
}
return status;
}
sx126x_status_t sx126x_get_gfsk_pkt_status( const void* context, sx126x_pkt_status_gfsk_t* pkt_status )
{
const uint8_t buf[SX126X_SIZE_GET_PKT_STATUS] = {
SX126X_GET_PKT_STATUS,
SX126X_NOP,
};
uint8_t pkt_status_local[3] = { 0x00 };
const sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_PKT_STATUS, pkt_status_local, 3 );
if( status == SX126X_STATUS_OK )
{
pkt_status->rx_status.pkt_sent =
( ( pkt_status_local[0] & SX126X_GFSK_RX_STATUS_PKT_SENT_MASK ) != 0 ) ? true : false;
pkt_status->rx_status.pkt_received =
( ( pkt_status_local[0] & SX126X_GFSK_RX_STATUS_PKT_RECEIVED_MASK ) != 0 ) ? true : false;
pkt_status->rx_status.abort_error =
( ( pkt_status_local[0] & SX126X_GFSK_RX_STATUS_ABORT_ERROR_MASK ) != 0 ) ? true : false;
pkt_status->rx_status.length_error =
( ( pkt_status_local[0] & SX126X_GFSK_RX_STATUS_LENGTH_ERROR_MASK ) != 0 ) ? true : false;
pkt_status->rx_status.crc_error =
( ( pkt_status_local[0] & SX126X_GFSK_RX_STATUS_CRC_ERROR_MASK ) != 0 ) ? true : false;
pkt_status->rx_status.adrs_error =
( ( pkt_status_local[0] & SX126X_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;
}
sx126x_status_t sx126x_get_lora_pkt_status( const void* context, sx126x_pkt_status_lora_t* pkt_status )
{
const uint8_t buf[SX126X_SIZE_GET_PKT_STATUS] = {
SX126X_GET_PKT_STATUS,
SX126X_NOP,
};
uint8_t pkt_status_local[sizeof( sx126x_pkt_status_lora_t )] = { 0x00 };
const sx126x_status_t status = ( sx126x_status_t ) sx126x_hal_read(
context, buf, SX126X_SIZE_GET_PKT_STATUS, pkt_status_local, sizeof( sx126x_pkt_status_lora_t ) );
if( status == SX126X_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;
}
sx126x_status_t sx126x_get_rssi_inst( const void* context, int16_t* rssi_in_dbm )
{
const uint8_t buf[SX126X_SIZE_GET_RSSI_INST] = {
SX126X_GET_RSSI_INST,
SX126X_NOP,
};
uint8_t rssi_local = 0x00;
const sx126x_status_t status =
( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_RSSI_INST, &rssi_local, 1 );
if( status == SX126X_STATUS_OK )
{
*rssi_in_dbm = ( int8_t )( -rssi_local >> 1 );
}
return status;
}
sx126x_status_t sx126x_get_gfsk_stats( const void* context, sx126x_stats_gfsk_t* stats )
{
const uint8_t buf[SX126X_SIZE_GET_STATS] = {
SX126X_GET_STATS,
SX126X_NOP,
};
uint8_t stats_local[sizeof( sx126x_stats_gfsk_t )] = { 0 };
const sx126x_status_t status = ( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_STATS,
stats_local, sizeof( sx126x_stats_gfsk_t ) );
if( status == SX126X_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;
}
sx126x_status_t sx126x_get_lora_stats( const void* context, sx126x_stats_lora_t* stats )
{
const uint8_t buf[SX126X_SIZE_GET_STATS] = {
SX126X_GET_STATS,
SX126X_NOP,
};
uint8_t stats_local[sizeof( sx126x_stats_lora_t )] = { 0 };
const sx126x_status_t status = ( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_STATS,
stats_local, sizeof( sx126x_stats_lora_t ) );
if( status == SX126X_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;
}
sx126x_status_t sx126x_reset_stats( const void* context )
{
const uint8_t buf[SX126X_SIZE_RESET_STATS] = {
SX126X_RESET_STATS, SX126X_NOP, SX126X_NOP, SX126X_NOP, SX126X_NOP, SX126X_NOP, SX126X_NOP,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_RESET_STATS, 0, 0 );
}
//
// Miscellaneous
//
sx126x_status_t sx126x_reset( const void* context )
{
return ( sx126x_status_t ) sx126x_hal_reset( context );
}
sx126x_status_t sx126x_wakeup( const void* context )
{
return ( sx126x_status_t ) sx126x_hal_wakeup( context );
}
sx126x_status_t sx126x_get_device_errors( const void* context, sx126x_errors_mask_t* errors )
{
const uint8_t buf[SX126X_SIZE_GET_DEVICE_ERRORS] = {
SX126X_GET_DEVICE_ERRORS,
SX126X_NOP,
};
uint8_t errors_local[sizeof( sx126x_errors_mask_t )] = { 0x00 };
const sx126x_status_t status = ( sx126x_status_t ) sx126x_hal_read( context, buf, SX126X_SIZE_GET_DEVICE_ERRORS,
errors_local, sizeof( sx126x_errors_mask_t ) );
if( status == SX126X_STATUS_OK )
{
*errors = ( ( sx126x_errors_mask_t ) errors_local[0] << 8 ) + ( ( sx126x_errors_mask_t ) errors_local[1] << 0 );
}
return status;
}
sx126x_status_t sx126x_clear_device_errors( const void* context )
{
const uint8_t buf[SX126X_SIZE_CLR_DEVICE_ERRORS] = {
SX126X_CLR_DEVICE_ERRORS,
SX126X_NOP,
SX126X_NOP,
};
return ( sx126x_status_t ) sx126x_hal_write( context, buf, SX126X_SIZE_CLR_DEVICE_ERRORS, 0, 0 );
}
sx126x_status_t sx126x_get_gfsk_bw_param( const uint32_t bw, uint8_t* param )
{
sx126x_status_t status = SX126X_STATUS_ERROR;
if( bw != 0 )
{
status = SX126X_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 = SX126X_STATUS_OK;
break;
}
}
}
return status;
}
uint32_t sx126x_get_lora_bw_in_hz( sx126x_lora_bw_t bw )
{
uint32_t bw_in_hz = 0;
switch( bw )
{
case SX126X_LORA_BW_007:
bw_in_hz = 7812UL;
break;
case SX126X_LORA_BW_010:
bw_in_hz = 10417UL;
break;
case SX126X_LORA_BW_015:
bw_in_hz = 15625UL;
break;
case SX126X_LORA_BW_020:
bw_in_hz = 20833UL;
break;
case SX126X_LORA_BW_031:
bw_in_hz = 31250UL;
break;
case SX126X_LORA_BW_041:
bw_in_hz = 41667UL;
break;
case SX126X_LORA_BW_062:
bw_in_hz = 62500UL;
break;
case SX126X_LORA_BW_125:
bw_in_hz = 125000UL;
break;
case SX126X_LORA_BW_250:
bw_in_hz = 250000UL;
break;
case SX126X_LORA_BW_500:
bw_in_hz = 500000UL;
break;
}
return bw_in_hz;
}
uint32_t sx126x_get_lora_time_on_air_numerator( const sx126x_pkt_params_lora_t* pkt_p,
const sx126x_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 == SX126X_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 sx126x_get_lora_time_on_air_in_ms( const sx126x_pkt_params_lora_t* pkt_p,
const sx126x_mod_params_lora_t* mod_p )
{
uint32_t numerator = 1000U * sx126x_get_lora_time_on_air_numerator( pkt_p, mod_p );
uint32_t denominator = sx126x_get_lora_bw_in_hz( mod_p->bw );
// Perform integral ceil()
return ( numerator + denominator - 1 ) / denominator;
}
uint32_t sx126x_get_gfsk_time_on_air_numerator( const sx126x_pkt_params_gfsk_t* pkt_p )
{
return pkt_p->preamble_len_in_bits + ( pkt_p->header_type == SX126X_GFSK_PKT_VAR_LEN ? 8 : 0 ) +
pkt_p->sync_word_len_in_bits +
( ( pkt_p->pld_len_in_bytes + ( pkt_p->address_filtering == SX126X_GFSK_ADDRESS_FILTERING_DISABLE ? 0 : 1 ) +
sx126x_get_gfsk_crc_len_in_bytes( pkt_p->crc_type ) )
<< 3 );
}
uint32_t sx126x_get_gfsk_time_on_air_in_ms( const sx126x_pkt_params_gfsk_t* pkt_p,
const sx126x_mod_params_gfsk_t* mod_p )
{
uint32_t numerator = 1000U * sx126x_get_gfsk_time_on_air_numerator( pkt_p );
uint32_t denominator = mod_p->br_in_bps;
// Perform integral ceil()
return ( numerator + denominator - 1 ) / denominator;
}
sx126x_status_t sx126x_get_random_numbers( const void* context, uint32_t* numbers, unsigned int n )
{
sx126x_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 = sx126x_read_register( context, SX126X_REG_ANA_LNA, &tmp_ana_lna, 1 );
if( status != SX126X_STATUS_OK )
{
return status;
}
tmp = tmp_ana_lna & ~( 1 << 0 );
status = sx126x_write_register( context, SX126X_REG_ANA_LNA, &tmp, 1 );
if( status != SX126X_STATUS_OK )
{
return status;
}
status = sx126x_read_register( context, SX126X_REG_ANA_MIXER, &tmp_ana_mixer, 1 );
if( status != SX126X_STATUS_OK )
{
return status;
}
tmp = tmp_ana_mixer & ~( 1 << 7 );
status = sx126x_write_register( context, SX126X_REG_ANA_MIXER, &tmp, 1 );
if( status != SX126X_STATUS_OK )
{
return status;
}
// Start RX continuous
status = sx126x_set_rx_with_timeout_in_rtc_step( context, SX126X_RX_CONTINUOUS );
if( status != SX126X_STATUS_OK )
{
return status;
}
// Store values
for( unsigned int i = 0; i < n; i++ )
{
status = sx126x_read_register( context, SX126X_REG_RNGBASEADDRESS, ( uint8_t* ) &numbers[i], 4 );
if( status != SX126X_STATUS_OK )
{
return status;
}
}
status = sx126x_set_standby( context, SX126X_STANDBY_CFG_RC );
if( status != SX126X_STATUS_OK )
{
return status;
}
// Restore registers
status = sx126x_write_register( context, SX126X_REG_ANA_LNA, &tmp_ana_lna, 1 );
if( status != SX126X_STATUS_OK )
{
return status;
}
status = sx126x_write_register( context, SX126X_REG_ANA_MIXER, &tmp_ana_mixer, 1 );
return status;
}
uint32_t sx126x_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 / SX126X_PLL_STEP_SCALED;
steps_frac = freq_in_hz - ( steps_int * SX126X_PLL_STEP_SCALED );
// Apply the scaling factor to retrieve a frequency in Hz (+ ceiling)
return ( steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT ) +
( ( ( steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT ) + ( SX126X_PLL_STEP_SCALED >> 1 ) ) /
SX126X_PLL_STEP_SCALED );
}
uint32_t sx126x_convert_timeout_in_ms_to_rtc_step( uint32_t timeout_in_ms )
{
return ( uint32_t )( timeout_in_ms * ( SX126X_RTC_FREQ_IN_HZ / 1000 ) );
}
sx126x_status_t sx126x_handle_rx_done( const void* context )
{
return sx126x_stop_rtc( context );
}
//
// Registers access
//
sx126x_status_t sx126x_cfg_rx_boosted( const void* context, const bool state )
{
if( state == true )
{
return sx126x_write_register( context, SX126X_REG_RXGAIN, ( const uint8_t[] ){ 0x96 }, 1 );
}
else
{
return sx126x_write_register( context, SX126X_REG_RXGAIN, ( const uint8_t[] ){ 0x94 }, 1 );
}
}
sx126x_status_t sx126x_set_gfsk_sync_word( const void* context, const uint8_t* sync_word, const uint8_t sync_word_len )
{
sx126x_status_t status = SX126X_STATUS_ERROR;
if( sync_word_len <= 8 )
{
uint8_t buf[8] = { 0 };
for( int i = 0; i < sync_word_len; i++ )
{
buf[i] = sync_word[i];
}
status = sx126x_write_register( context, SX126X_REG_SYNCWORDBASEADDRESS, buf, 8 );
}
return status;
}
sx126x_status_t sx126x_set_lora_sync_word( const void* context, const uint8_t sync_word )
{
uint8_t buffer[2] = { 0x00 };
sx126x_status_t status = sx126x_read_register( context, SX126X_REG_LR_SYNCWORD, buffer, 2 );
if( status == SX126X_STATUS_OK )
{
buffer[0] = ( buffer[0] & ~0xF0 ) + ( sync_word & 0xF0 );
buffer[1] = ( buffer[1] & ~0xF0 ) + ( ( sync_word & 0x0F ) << 4 );
status = sx126x_write_register( context, SX126X_REG_LR_SYNCWORD, buffer, 2 );
}
return status;
}
sx126x_status_t sx126x_set_gfsk_crc_seed( const void* context, uint16_t seed )
{
uint8_t s[] = { ( uint8_t )( seed >> 8 ), ( uint8_t ) seed };
return sx126x_write_register( context, SX126X_REG_CRCSEEDBASEADDRESS, s, sizeof( s ) );
}
sx126x_status_t sx126x_set_gfsk_crc_polynomial( const void* context, const uint16_t polynomial )
{
uint8_t poly[] = { ( uint8_t )( polynomial >> 8 ), ( uint8_t ) polynomial };
return sx126x_write_register( context, SX126X_REG_CRCPOLYBASEADDRESS, poly, sizeof( poly ) );
}
sx126x_status_t sx126x_set_gfsk_whitening_seed( const void* context, const uint16_t seed )
{
uint8_t reg_value = 0;
// The SX126X_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.
sx126x_status_t status = sx126x_read_register( context, SX126X_REG_WHITSEEDBASEADDRESS, &reg_value, 1 );
if( status == SX126X_STATUS_OK )
{
reg_value = ( reg_value & 0xFE ) | ( ( uint8_t )( seed >> 8 ) & 0x01 );
status = sx126x_write_register( context, SX126X_REG_WHITSEEDBASEADDRESS, &reg_value, 1 );
if( status == SX126X_STATUS_OK )
{
reg_value = ( uint8_t ) seed;
status = sx126x_write_register( context, SX126X_REG_WHITSEEDBASEADDRESS + 1, &reg_value, 1 );
}
}
return status;
}
sx126x_status_t sx126x_cfg_tx_clamp( const void* context )
{
uint8_t reg_value = 0x00;
sx126x_status_t status = sx126x_read_register( context, SX126X_REG_TX_CLAMP_CFG, &reg_value, 1 );
if( status == SX126X_STATUS_OK )
{
reg_value |= SX126X_REG_TX_CLAMP_CFG_MASK;
status = sx126x_write_register( context, SX126X_REG_TX_CLAMP_CFG, &reg_value, 1 );
}
return status;
}
sx126x_status_t sx126x_stop_rtc( const void* context )
{
uint8_t reg_value = 0;
sx126x_status_t status = sx126x_write_register( context, SX126X_REG_RTC_CTRL, &reg_value, 1 );
if( status == SX126X_STATUS_OK )
{
status = sx126x_read_register( context, SX126X_REG_EVT_CLR, &reg_value, 1 );
if( status == SX126X_STATUS_OK )
{
reg_value |= SX126X_REG_EVT_CLR_TIMEOUT_MASK;
status = sx126x_write_register( context, SX126X_REG_EVT_CLR, &reg_value, 1 );
}
}
return status;
}
sx126x_status_t sx126x_set_ocp_value( const void* context, const uint8_t ocp_in_step_of_2_5_ma )
{
return ( sx126x_status_t ) sx126x_write_register( context, SX126X_REG_OCP, &ocp_in_step_of_2_5_ma, 1 );
}
sx126x_status_t sx126x_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 ( sx126x_status_t ) sx126x_write_register( context, SX126X_REG_XTATRIM, trimming_capacitor_values, 2 );
}
sx126x_status_t sx126x_add_registers_to_retention_list( const void* context, const uint16_t* register_addr,
uint8_t register_nb )
{
uint8_t buffer[9] = {0};
sx126x_status_t status = sx126x_read_register( context, SX126X_REG_RETENTION_LIST_BASE_ADDRESS, buffer, 9 );
if( status == SX126X_STATUS_OK )
{
const uint8_t initial_nb_of_registers = buffer[0];
uint8_t* register_list = &buffer[1];
for( uint8_t index = 0; index < register_nb; index++ )
{
bool register_has_to_be_added = true;
// Check if the current register is already added to the list
for( uint8_t i = 0; i < buffer[0]; i++ )
{
if( register_addr[index] == ( ( uint16_t ) register_list[2 * i] << 8 ) + register_list[2 * i + 1] )
{
register_has_to_be_added = false;
break;
}
}
if( register_has_to_be_added == true )
{
if( buffer[0] < SX126X_MAX_NB_REG_IN_RETENTION )
{
register_list[2 * buffer[0]] = ( uint8_t )( register_addr[index] >> 8 );
register_list[2 * buffer[0] + 1] = ( uint8_t )( register_addr[index] >> 0 );
buffer[0] += 1;
}
else
{
return SX126X_STATUS_ERROR;
}
}
}
if( buffer[0] != initial_nb_of_registers )
{
status = sx126x_write_register( context, SX126X_REG_RETENTION_LIST_BASE_ADDRESS, buffer, 9 );
}
}
return status;
}
sx126x_status_t sx126x_init_retention_list( const void* context )
{
const uint16_t list_of_registers[3] = { SX126X_REG_RXGAIN, SX126X_REG_TX_MODULATION, SX126X_REG_IQ_POLARITY };
return sx126x_add_registers_to_retention_list( context, list_of_registers,
sizeof( list_of_registers ) / sizeof( list_of_registers[0] ) );
}
sx126x_status_t sx126x_get_lora_params_from_header( const void* context, sx126x_lora_cr_t* cr, bool* crc_is_on )
{
uint8_t buffer_cr = 0;
uint8_t buffer_crc = 0;
sx126x_status_t status = sx126x_read_register( context, SX126X_REG_LR_HEADER_CR, &buffer_cr, 1 );
if( status == SX126X_STATUS_OK )
{
status = sx126x_read_register( context, SX126X_REG_LR_HEADER_CRC, &buffer_crc, 1 );
if( status == SX126X_STATUS_OK )
{
*cr = ( sx126x_lora_cr_t )( ( buffer_cr & SX126X_REG_LR_HEADER_CR_MASK ) >> SX126X_REG_LR_HEADER_CR_POS );
*crc_is_on = ( ( buffer_crc & SX126X_REG_LR_HEADER_CRC_MASK ) != 0 ) ? true : false;
}
}
return status;
}
/*
* -----------------------------------------------------------------------------
* --- PRIVATE FUNCTIONS DEFINITION --------------------------------------------
*/
static sx126x_status_t sx126x_tx_modulation_workaround( const void* context, sx126x_pkt_type_t pkt_type,
sx126x_lora_bw_t bw )
{
uint8_t reg_value = 0;
sx126x_status_t status = sx126x_read_register( context, SX126X_REG_TX_MODULATION, &reg_value, 1 );
if( status == SX126X_STATUS_OK )
{
if( pkt_type == SX126X_PKT_TYPE_LORA )
{
if( bw == SX126X_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 = sx126x_write_register( context, SX126X_REG_TX_MODULATION, &reg_value, 1 );
}
return status;
}
static inline uint32_t sx126x_get_gfsk_crc_len_in_bytes( sx126x_gfsk_crc_types_t crc_type )
{
switch( crc_type )
{
case SX126X_GFSK_CRC_OFF:
return 0;
case SX126X_GFSK_CRC_1_BYTE:
return 1;
case SX126X_GFSK_CRC_2_BYTES:
return 2;
case SX126X_GFSK_CRC_1_BYTE_INV:
return 1;
case SX126X_GFSK_CRC_2_BYTES_INV:
return 2;
}
return 0;
}
/* --- EOF ------------------------------------------------------------------ */