init commit

This commit is contained in:
2026-03-30 13:45:38 +03:00
commit 0b982e5d4d
570 changed files with 280071 additions and 0 deletions

View File

@@ -0,0 +1,862 @@
/*!
* @file lr_fhss_mac.c
*
* @brief Radio-independent LR-FHSS 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 "lr_fhss_mac.h"
#include <string.h>
/*
* -----------------------------------------------------------------------------
* --- PRIVATE MACROS-----------------------------------------------------------
*/
#ifdef TEST
#define STATIC
#else
#define STATIC static
#endif
/*
* -----------------------------------------------------------------------------
* --- PRIVATE CONSTANTS -------------------------------------------------------
*/
#define LR_FHSS_MAX_TMP_BUF_BYTES ( 608 )
/*
* -----------------------------------------------------------------------------
* --- PRIVATE TYPES -----------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PRIVATE VARIABLES -------------------------------------------------------
*/
/** @brief Channel count as function of bandwidth index, from Table 9 specification v18 */
STATIC const uint16_t lr_fhss_channel_count[] = { 80, 176, 280, 376, 688, 792, 1480, 1584, 3120, 3224 };
/** @brief Generating polynomial as function of polynomial index, n_grid in { 10, 22, 28, 30, 35, 47 } */
STATIC const uint8_t lr_fhss_lfsr_poly1[] = { 33, 45, 48, 51, 54, 57 };
/** @brief Generating polynomial as function of polynomial index, n_grid in { 86, 99 } */
STATIC const uint8_t lr_fhss_lfsr_poly2[] = { 65, 68, 71, 72 };
/** @brief Generating polynomial as function of polynomial index, n_grid in { 185, 198 } */
STATIC const uint8_t lr_fhss_lfsr_poly3[] = { 142, 149 };
/** @brief used for 1/3 rate viterbi encoding */
STATIC const uint8_t lr_fhss_viterbi_1_3_table[64][2] =
{
{ 0, 7 }, { 3, 4 }, { 7, 0 }, { 4, 3 }, { 6, 1 }, { 5, 2 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 6, 1 },
{ 5, 2 }, { 7, 0 }, { 4, 3 }, { 0, 7 }, { 3, 4 }, { 4, 3 }, { 7, 0 }, { 3, 4 }, { 0, 7 }, { 2, 5 }, { 1, 6 },
{ 5, 2 }, { 6, 1 }, { 5, 2 }, { 6, 1 }, { 2, 5 }, { 1, 6 }, { 3, 4 }, { 0, 7 }, { 4, 3 }, { 7, 0 }, { 7, 0 },
{ 4, 3 }, { 0, 7 }, { 3, 4 }, { 1, 6 }, { 2, 5 }, { 6, 1 }, { 5, 2 }, { 6, 1 }, { 5, 2 }, { 1, 6 }, { 2, 5 },
{ 0, 7 }, { 3, 4 }, { 7, 0 }, { 4, 3 }, { 3, 4 }, { 0, 7 }, { 4, 3 }, { 7, 0 }, { 5, 2 }, { 6, 1 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 1, 6 }, { 5, 2 }, { 6, 1 }, { 4, 3 }, { 7, 0 }, { 3, 4 }, { 0, 7 }
};
/** @brief used for 1/2 rate viterbi encoding */
STATIC const uint8_t lr_fhss_viterbi_1_2_table[16][2] =
{
{ 0, 3 }, { 1, 2 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 3, 0 },
{ 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 1, 2 }, { 0, 3 },
{ 1, 2 }, { 0, 3 }, { 3, 0 }, { 2, 1 }
};
/** @brief used header interleaving */
STATIC const uint8_t lr_fhss_header_interleaver_minus_one[80] =
{
0, 18, 36, 54, 72, 4, 22, 40, //
58, 76, 8, 26, 44, 62, 12, 30, //
48, 66, 16, 34, 52, 70, 1, 19, //
37, 55, 73, 5, 23, 41, 59, 77, //
9, 27, 45, 63, 13, 31, 49, 67, //
17, 35, 53, 71, 2, 20, 38, 56, //
74, 6, 24, 42, 60, 78, 10, 28, //
46, 64, 14, 32, 50, 68, 3, 21, //
39, 57, 75, 7, 25, 43, 61, 79, //
11, 29, 47, 65, 15, 33, 51, 69 //
};
/** @brief lookup table for lr_fhss_header_crc8 */
const uint8_t lr_fhss_header_crc8_lut[256] =
{
0, 47, 94, 113, 188, 147, 226, 205, 87, 120, 9, 38, 235, 196, 181, 154, //
174, 129, 240, 223, 18, 61, 76, 99, 249, 214, 167, 136, 69, 106, 27, 52, //
115, 92, 45, 2, 207, 224, 145, 190, 36, 11, 122, 85, 152, 183, 198, 233, //
221, 242, 131, 172, 97, 78, 63, 16, 138, 165, 212, 251, 54, 25, 104, 71, //
230, 201, 184, 151, 90, 117, 4, 43, 177, 158, 239, 192, 13, 34, 83, 124, //
72, 103, 22, 57, 244, 219, 170, 133, 31, 48, 65, 110, 163, 140, 253, 210, //
149, 186, 203, 228, 41, 6, 119, 88, 194, 237, 156, 179, 126, 81, 32, 15, //
59, 20, 101, 74, 135, 168, 217, 246, 108, 67, 50, 29, 208, 255, 142, 161, //
227, 204, 189, 146, 95, 112, 1, 46, 180, 155, 234, 197, 8, 39, 86, 121, //
77, 98, 19, 60, 241, 222, 175, 128, 26, 53, 68, 107, 166, 137, 248, 215, //
144, 191, 206, 225, 44, 3, 114, 93, 199, 232, 153, 182, 123, 84, 37, 10, //
62, 17, 96, 79, 130, 173, 220, 243, 105, 70, 55, 24, 213, 250, 139, 164, //
5, 42, 91, 116, 185, 150, 231, 200, 82, 125, 12, 35, 238, 193, 176, 159, //
171, 132, 245, 218, 23, 56, 73, 102, 252, 211, 162, 141, 64, 111, 30, 49, //
118, 89, 40, 7, 202, 229, 148, 187, 33, 14, 127, 80, 157, 178, 195, 236, //
216, 247, 134, 169, 100, 75, 58, 21, 143, 160, 209, 254, 51, 28, 109, 66 //
};
/** @brief lookup table for lr_fhss_payload_crc16 */
const uint16_t lr_fhss_payload_crc16_lut[256] =
{
0, 30043, 60086, 40941, 41015, 54636, 19073, 16346, 13621, 16494, 57219, 43736, 38146, 57433, 32692, 2799, //
27242, 7985, 32988, 62855, 51805, 48902, 8427, 21936, 24415, 10756, 46569, 49330, 65384, 35379, 5598, 24709, //
54484, 41359, 15970, 19257, 29923, 440, 40533, 60174, 57825, 38074, 2903, 32268, 16854, 13453, 43872, 56891, //
48830, 52197, 21512, 8531, 7817, 27602, 62527, 33124, 35723, 65232, 24893, 5222, 11196, 24295, 49418, 46161, //
56563, 43432, 13893, 17182, 31940, 2463, 38514, 58153, 59846, 40093, 880, 30251, 18929, 15530, 41799, 54812, //
46745, 50114, 23599, 10612, 5806, 25589, 64536, 35139, 33708, 63223, 26906, 7233, 9115, 22208, 51501, 48246, //
2087, 32124, 58001, 38858, 43024, 56651, 17062, 14333, 15634, 18505, 55204, 41727, 40229, 59518, 30611, 712, //
25165, 5910, 35067, 64928, 49786, 46881, 10444, 23959, 22392, 8739, 48590, 51349, 63311, 33300, 7673, 26786, //
52413, 47590, 9739, 21328, 27786, 6609, 34364, 62311, 63880, 36051, 4926, 26213, 22975, 11492, 45833, 50770, //
42711, 54156, 19553, 14650, 1760, 29627, 60502, 39181, 37858, 59065, 31060, 3087, 13269, 18062, 55651, 44088, //
6249, 27954, 62175, 34692, 47198, 52485, 21224, 10163, 11612, 22535, 51178, 45745, 36203, 63536, 26589, 4742, //
29187, 1880, 39093, 60910, 53812, 42863, 14466, 19929, 18230, 12909, 44416, 55515, 59137, 37466, 3511, 30956, //
4174, 25877, 64248, 36771, 45177, 50466, 23247, 12180, 9595, 20512, 53197, 47766, 34124, 61463, 28666, 6817, //
31268, 3967, 37010, 58825, 55827, 44872, 12453, 17918, 20241, 14922, 42407, 53500, 61222, 39549, 1424, 28875, //
50330, 45505, 11820, 23415, 25773, 4598, 36379, 64320, 61871, 34036, 6937, 28226, 20888, 9411, 47918, 52853, //
44784, 56235, 17478, 12573, 3783, 31644, 58481, 37162, 39877, 61086, 29043, 1064, 15346, 20137, 53572, 42015
};
/*!
* @brief integral square root, rounded up
*
* @param [in] x argument
*
* @returns Square root of argument, rounded up to next integer
*
* @remark This function is only appropriate to use for reasonably small arguments
*/
STATIC uint16_t sqrt_uint16( uint16_t x );
/*
* -----------------------------------------------------------------------------
* --- PRIVATE FUNCTION DECLARATIONS -------------------------------------------
*/
/*!
* @brief Compute 16-bit payload CRC
*
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bytecount Input buffer length, in bytes
*
* @returns 16-bit CRC
*/
STATIC uint16_t lr_fhss_payload_crc16( const uint8_t *data_in, uint16_t data_in_bytecount );
/*!
* @brief Compute 8-bit header CRC
*
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bytecount Input buffer length, in bytes
*
* @returns 8-bit CRC
*/
STATIC uint8_t lr_fhss_header_crc8( const uint8_t *data_in, uint16_t data_in_bytecount );
/*!
* @brief Whiten the payload
*
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bytecount Input buffer length, in bytes
* @param [out] data_out Pointer to output buffer, of same length as input buffer
*/
STATIC void lr_fhss_payload_whitening( const uint8_t *data_in, uint16_t data_in_bytecount, uint8_t *data_out );
/*!
* @brief Extract specific bit from array of bytes
*
* @param [in] data_in Array of bytes
* @param [in] bit_number Index of bit in array
*
* @returns Value of the bit
*/
STATIC uint8_t lr_fhss_extract_bit_in_byte_vector( const uint8_t *data_in, uint32_t bit_number );
/*!
* @brief Set specific bit in array of bytes
*
* @param [in] data_in Array of bytes
* @param [in] bit_number Index of bit in array
* @param [in] bit_value Value to be set
*/
STATIC void lr_fhss_set_bit_in_byte_vector( uint8_t *vector, uint32_t bit_number, uint8_t bit_value );
/*!
* @brief Compute 1/2 rate Viterbi encoding
*
* @param [in,out] encod_state Pointer to encoded state
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bitcount Length of input buffer, in bits
* @param [in] data_out Pointer to output buffer
*
* @returns Length of output buffer, in bits
*/
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_2_base( uint8_t *encod_state, const uint8_t *data_in,
uint16_t data_in_bitcount, uint8_t *data_out );
/*!
* @brief Compute 1/3 rate Viterbi encoding
*
* @param [in,out] encod_state Pointer to encoded state
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bitcount Length of input buffer, in bits
* @param [out] data_out Pointer to output buffer
*
* @returns Length of output buffer, in bits
*/
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_3_base( uint8_t *encod_state, const uint8_t *data_in,
uint16_t data_in_bitcount, uint8_t *data_out );
/*!
* @brief Convolute using lr_fhss_convolution_encode_viterbi_1_2_base with optional tail-biting
*
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bitcount Length of input buffer, in bits
* @param [in] tail_biting Set to true to activate tail-biting
* @param [out] data_out Pointer to output buffer
*
* @remark If tail-biting is activated, this function calls lr_fhss_convolution_encode_viterbi_1_2_base twice
*
* @returns Length of output buffer, in bits
*/
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_2( const uint8_t *data_in, uint16_t data_in_bitcount,
bool tail_biting, uint8_t *data_out );
/*!
* @brief Convolute using lr_fhss_convolution_encode_viterbi_1_3_base
*
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bitcount Length of input buffer, in bits
* @param [out] data_out Pointer to output buffer
*
* @returns Length of output buffer, in bits
*/
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_3( const uint8_t *data_in, uint16_t data_in_bitcount,
uint8_t *data_out );
/*!
* @brief Computes payload interleaving
*
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bitcount Length of input buffer, in bits
* @param [out] data_out Pointer to output buffer
* @param [in] output_offset Output offset indicating where data must be placed, in bits, relative to data_out bit 0
*
* @returns Length of output buffer, in bits
*/
STATIC uint16_t lr_fhss_payload_interleaving( const uint8_t *data_in, uint16_t data_in_bitcount, uint8_t *data_out,
uint32_t output_offset );
/*!
* @brief Create the raw LR-FHSS header
*
* @param [in] params Parameter structure
* @param [in] hop_sequence_id The hop sequence ID that will be used to obtain hop-related data
* @param [in] payload_length Length of application payload, in bytes
* @param [out] data_out Pointer to output buffer
*/
STATIC void lr_fhss_raw_header( const lr_fhss_v1_params_t *params, uint16_t hop_sequence_id, uint16_t payload_length,
uint8_t *data_out );
/*!
* @brief Store sync word index inside provided header
*
* @param [in] sync_word_index The sync word index to store
* @param [out] data_out Pointer to output buffer
*/
STATIC void lr_fhss_store_header_sync_word_index( uint8_t sync_word_index, uint8_t *data_out );
/*!
* @brief Get the bit count and block count for a LR-FHSS frame
*
* @param [in] params Parameter structure
* @param [in] payload_length Length of physical payload, in bytes
* @param [out] nb_hops_out Number of LR-FHSS hops
*
* @returns Length of physical payload, in bits
*/
STATIC uint16_t lr_fhss_get_bit_and_hop_count( const lr_fhss_v1_params_t *params, uint16_t payload_length,
uint8_t *nb_hops_out );
/*
* -----------------------------------------------------------------------------
* --- PUBLIC FUNCTION DEFINITIONS ---------------------------------------------
*/
unsigned int lr_fhss_get_hop_sequence_count( const lr_fhss_v1_params_t *params )
{
if( ( params->grid == LR_FHSS_V1_GRID_25391_HZ ) ||
( ( params->grid == LR_FHSS_V1_GRID_3906_HZ ) && ( params->bw < LR_FHSS_V1_BW_335938_HZ ) ) )
{
return 384;
}
return 512;
}
void lr_fhss_process_parameters( const lr_fhss_v1_params_t *params, uint16_t payload_length, lr_fhss_digest_t *digest )
{
digest->nb_bits = lr_fhss_get_bit_and_hop_count( params, payload_length, &digest->nb_hops );
digest->nb_bytes = ( digest->nb_bits + 8 - 1 ) / 8;
if( params->enable_hopping )
{
digest->nb_hops = digest->nb_hops;
}
else
{
digest->nb_hops = 1;
}
}
radio_status_t lr_fhss_get_hop_params( const lr_fhss_v1_params_t *params, lr_fhss_hop_params_t *hop_params,
uint16_t *initial_state, uint16_t hop_sequence_id )
{
uint32_t channel_count = lr_fhss_channel_count[params->bw];
if( params->grid == LR_FHSS_V1_GRID_3906_HZ )
{
hop_params->n_grid = channel_count / 8;
}
else
{
hop_params->n_grid = channel_count / 52;
}
switch( hop_params->n_grid )
{
case 10:
case 22:
case 28:
case 30:
case 35:
case 47:
{
*initial_state = 6;
hop_params->polynomial = lr_fhss_lfsr_poly1[hop_sequence_id >> 6];
hop_params->xoring_seed = hop_sequence_id & 0x3F;
if( hop_sequence_id >= 384 )
{
return RADIO_STATUS_ERROR;
}
break;
}
case 60:
case 62:
{
*initial_state = 56;
hop_params->polynomial = lr_fhss_lfsr_poly1[hop_sequence_id >> 6];
hop_params->xoring_seed = hop_sequence_id & 0x3F;
if( hop_sequence_id >= 384 )
{
return RADIO_STATUS_ERROR;
}
break;
}
case 86:
case 99:
{
*initial_state = 6;
hop_params->polynomial = lr_fhss_lfsr_poly2[hop_sequence_id >> 7];
hop_params->xoring_seed = hop_sequence_id & 0x7F;
break;
}
case 185:
case 198:
{
*initial_state = 6;
hop_params->polynomial = lr_fhss_lfsr_poly3[hop_sequence_id >> 8];
hop_params->xoring_seed = hop_sequence_id & 0xFF;
break;
}
case 390:
case 403:
{
*initial_state = 6;
hop_params->polynomial = 264;
hop_params->xoring_seed = hop_sequence_id;
break;
}
default:
return RADIO_STATUS_ERROR;
}
hop_params->hop_sequence_id = hop_sequence_id;
return RADIO_STATUS_OK;
}
uint16_t lr_fhss_get_next_state( uint16_t *lfsr_state, const lr_fhss_hop_params_t *hop_params )
{
uint16_t hop;
do
{
uint16_t lsb = *lfsr_state & 1;
*lfsr_state >>= 1;
if( lsb )
{
*lfsr_state ^= hop_params->polynomial;
}
hop = hop_params->xoring_seed;
if( hop != *lfsr_state )
{
hop ^= *lfsr_state;
}
} while( hop > hop_params->n_grid );
return hop - 1;
}
int16_t lr_fhss_get_next_freq_in_grid( uint16_t *lfsr_state, const lr_fhss_hop_params_t *hop_params,
const lr_fhss_v1_params_t *params )
{
uint16_t n_i;
if( params->enable_hopping )
{
n_i = lr_fhss_get_next_state( lfsr_state, hop_params );
}
else
{
n_i = hop_params->hop_sequence_id % hop_params->n_grid;
}
if( n_i < ( hop_params->n_grid >> 1 ) )
{
return n_i;
}
else
{
return n_i - hop_params->n_grid;
}
}
/**************************** Build LR-FHSS Frame ***********************************************************
* Core of the LR-FHSS frame generator *
* *
* In |---------| |-----| |-----------------| |-------| |------------| |----------------------| Out *
**---|Whitening|--|CRC16|--|Outer Code + CRC8|--|Viterbi|--|Interleaving|--|Sync+header+crc Header|------ *
* |---------| |-----| |-----------------|--|-------| |------------| |----------------------| *
* *
**********************************************************************************************************/
uint16_t lr_fhss_build_frame( const lr_fhss_v1_params_t *params, uint16_t hop_sequence_id, const uint8_t *data_in,
uint16_t data_in_bytecount, uint8_t *data_out )
{
uint8_t data_out_tmp[LR_FHSS_MAX_TMP_BUF_BYTES] = { 0 };
lr_fhss_payload_whitening( data_in, data_in_bytecount, data_out );
uint16_t payload_crc = lr_fhss_payload_crc16( data_out, data_in_bytecount );
data_out[data_in_bytecount] = ( payload_crc >> 8 ) & 0xFF;
data_out[data_in_bytecount + 1] = payload_crc & 0xFF;
data_out[data_in_bytecount + 2] = 0;
// the 1/3 encoded bytes can go up to LR_FHSS_MAX_TMP_BUF_BYTES temporarily, before puncturing it
uint16_t nb_bits =
lr_fhss_convolution_encode_viterbi_1_3( data_out, 8 * ( data_in_bytecount + 2 ) + 6, data_out_tmp );
// Avoid putting random stack data into payload
memset( data_out, 0, LR_FHSS_MAX_PHY_PAYLOAD_BYTES );
if( params->cr != LR_FHSS_V1_CR_1_3 )
{
// this assumes first matrix values are always the same, which is the case
uint32_t matrix_index = 0;
uint8_t matrix[15] = { 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 };
uint8_t matrix_len = 0;
switch( params->cr )
{
case LR_FHSS_V1_CR_5_6:
matrix_len = 15;
break;
case LR_FHSS_V1_CR_2_3:
matrix_len = 6;
break;
case LR_FHSS_V1_CR_1_2:
matrix_len = 3;
break;
default:
// LR_FHSS_V1_CR_1_3 is excluded from this code block
break;
}
uint32_t j = 0;
for( uint32_t i = 0; i < nb_bits; i++ )
{
if( matrix[matrix_index] )
{
lr_fhss_set_bit_in_byte_vector( data_out, j++, lr_fhss_extract_bit_in_byte_vector( data_out_tmp, i ) );
}
if( ++matrix_index == matrix_len )
{
matrix_index = 0;
}
}
nb_bits = j;
memcpy( data_out_tmp, data_out, ( nb_bits + 7 ) / 8 );
}
// Interleave directly to data_out
nb_bits =
lr_fhss_payload_interleaving( data_out_tmp, nb_bits, data_out, LR_FHSS_HEADER_BITS * params->header_count );
// Build the header
uint8_t raw_header[LR_FHSS_HALF_HDR_BYTES];
lr_fhss_raw_header( params, hop_sequence_id, data_in_bytecount, raw_header );
uint16_t header_offset = 0;
for( uint32_t i = 0; i < params->header_count; i++ )
{
// Insert appropriate index into header
lr_fhss_store_header_sync_word_index( params->header_count - i - 1, raw_header );
raw_header[4] = lr_fhss_header_crc8( raw_header, 4 );
// Convolutional encode
uint8_t coded_header[LR_FHSS_HDR_BYTES] = { 0 };
lr_fhss_convolution_encode_viterbi_1_2( raw_header, LR_FHSS_HALF_HDR_BITS, 1, coded_header );
// Header guard bits
lr_fhss_set_bit_in_byte_vector( data_out, header_offset + 0, 0 );
lr_fhss_set_bit_in_byte_vector( data_out, header_offset + 1, 0 );
// Interleave the header directly to the physical payload buffer
for( uint32_t j = 0; j < LR_FHSS_HALF_HDR_BITS; j++ )
{
lr_fhss_set_bit_in_byte_vector(
data_out, header_offset + 2 + j,
lr_fhss_extract_bit_in_byte_vector( coded_header, lr_fhss_header_interleaver_minus_one[j] ) );
}
for( uint32_t j = 0; j < LR_FHSS_HALF_HDR_BITS; j++ )
{
lr_fhss_set_bit_in_byte_vector(
data_out, header_offset + 2 + LR_FHSS_HALF_HDR_BITS + LR_FHSS_SYNC_WORD_BITS + j,
lr_fhss_extract_bit_in_byte_vector( coded_header,
lr_fhss_header_interleaver_minus_one[LR_FHSS_HALF_HDR_BITS + j] ) );
}
// Copy the sync word to the physical payload buffer
for( uint32_t j = 0; j < LR_FHSS_SYNC_WORD_BITS; j++ )
{
lr_fhss_set_bit_in_byte_vector( data_out, header_offset + 2 + LR_FHSS_HALF_HDR_BITS + j,
lr_fhss_extract_bit_in_byte_vector( params->sync_word, j ) );
}
header_offset += LR_FHSS_HEADER_BITS;
}
return ( header_offset + nb_bits + 7 ) / 8;
}
uint32_t lr_fhss_get_time_on_air_in_ms( const lr_fhss_v1_params_t *params, uint16_t payload_length )
{
// Multiply by 1000 / 488.28125, or equivalently 256/125, rounding up
return ( ( lr_fhss_get_time_on_air_numerator( params, payload_length ) << 8 ) + 124 ) / 125;
}
/*
* -----------------------------------------------------------------------------
* --- PRIVATE FUNCTION DEFINITIONS --------------------------------------------
*/
STATIC uint16_t lr_fhss_payload_crc16( const uint8_t *data_in, uint16_t data_in_bytecount )
{
uint16_t crc16 = 65535;
uint8_t pos = 0;
for( uint16_t k = 0; k < data_in_bytecount; k++ )
{
pos = ( ( crc16 >> 8 ) ^ data_in[k] );
crc16 = ( crc16 << 8 ) ^ lr_fhss_payload_crc16_lut[pos];
}
return crc16;
}
STATIC uint8_t lr_fhss_header_crc8( const uint8_t *data_in, uint16_t data_in_bytecount )
{
uint8_t crc8 = 255;
for( uint16_t k = 0; k < data_in_bytecount; k++ )
{
uint8_t pos = ( crc8 ^ data_in[k] );
crc8 = lr_fhss_header_crc8_lut[pos];
}
return crc8;
}
STATIC void lr_fhss_payload_whitening( const uint8_t *data_in, uint16_t data_in_bytecount, uint8_t *data_out )
{
uint8_t lfsr = 0xFF;
for( uint8_t index = 0; index < data_in_bytecount; index++ )
{
uint8_t u = data_in[index] ^ lfsr;
data_out[index] = ( ( u & 0x0F ) << 4 ) | ( ( u & 0xF0 ) >> 4 );
lfsr =
( lfsr << 1 ) | ( ( ( lfsr & 0x80 ) >> 7 ) ^
( ( ( lfsr & 0x20 ) >> 5 ) ^ ( ( ( lfsr & 0x10 ) >> 4 ) ^ ( ( lfsr & 0x8 ) >> 3 ) ) ) );
}
}
STATIC uint8_t lr_fhss_extract_bit_in_byte_vector( const uint8_t *data_in, uint32_t bit_number )
{
uint32_t index = bit_number >> 3;
uint8_t bit_pos = 7 - ( bit_number % 8 );
if( data_in[index] & ( 1 << bit_pos ) )
{
return 1;
}
return 0;
}
STATIC void lr_fhss_set_bit_in_byte_vector( uint8_t *vector, uint32_t bit_number, uint8_t bit_value )
{
uint32_t index = bit_number >> 3;
uint8_t bit_pos = 7 - ( bit_number % 8 );
vector[index] = ( vector[index] & ( 0xff - ( 1 << bit_pos ) ) ) | ( bit_value << bit_pos );
}
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_2_base( uint8_t *encod_state, const uint8_t *data_in,
uint16_t data_in_bitcount, uint8_t *data_out )
{
uint8_t g1g0;
uint8_t cur_bit;
uint16_t ind_bit;
uint16_t data_out_bitcount = 0;
uint16_t bin_out_16 = 0;
for( ind_bit = 0; ind_bit < data_in_bitcount; ind_bit++ )
{
cur_bit = lr_fhss_extract_bit_in_byte_vector( data_in, ind_bit );
g1g0 = lr_fhss_viterbi_1_2_table[*encod_state][cur_bit];
*encod_state = ( *encod_state * 2 + cur_bit ) % 16;
bin_out_16 |= ( g1g0 << ( ( 7 - ( ind_bit % 8 ) ) << 1 ) );
if( ind_bit % 8 == 7 )
{
*data_out++ = ( uint8_t )( bin_out_16 >> 8 );
*data_out++ = ( uint8_t ) bin_out_16;
bin_out_16 = 0;
}
data_out_bitcount += 2;
}
if( ind_bit % 8 )
{
*data_out++ = ( uint8_t )( bin_out_16 >> 8 );
*data_out++ = ( uint8_t ) bin_out_16;
bin_out_16 = 0;
}
return data_out_bitcount;
}
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_3_base( uint8_t *encod_state, const uint8_t *data_in,
uint16_t data_in_bitcount, uint8_t *data_out )
{
uint8_t g1g0;
uint8_t cur_bit;
uint16_t ind_bit;
uint16_t data_out_bitcount = 0;
uint32_t bin_out_32 = 0;
for( ind_bit = 0; ind_bit < data_in_bitcount; ind_bit++ )
{
cur_bit = lr_fhss_extract_bit_in_byte_vector( data_in, ind_bit );
g1g0 = lr_fhss_viterbi_1_3_table[*encod_state][cur_bit];
*encod_state = ( *encod_state * 2 + cur_bit ) % 64;
bin_out_32 |= ( g1g0 << ( ( 7 - ( ind_bit % 8 ) ) * 3 ) );
if( ind_bit % 8 == 7 )
{
*data_out++ = ( uint8_t )( bin_out_32 >> 16 );
*data_out++ = ( uint8_t )( bin_out_32 >> 8 );
*data_out++ = ( uint8_t ) bin_out_32;
bin_out_32 = 0;
}
data_out_bitcount += 3;
}
if( ind_bit % 8 )
{
*data_out++ = ( uint8_t )( bin_out_32 >> 16 );
*data_out++ = ( uint8_t )( bin_out_32 >> 8 );
*data_out++ = ( uint8_t ) bin_out_32;
bin_out_32 = 0;
}
return data_out_bitcount;
}
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_2( const uint8_t *data_in, uint16_t data_in_bitcount,
bool tail_biting, uint8_t *data_out )
{
uint8_t encode_state = 0;
uint16_t data_out_bitcount;
data_out_bitcount =
lr_fhss_convolution_encode_viterbi_1_2_base( &encode_state, data_in, data_in_bitcount, data_out );
if( tail_biting )
{
data_out_bitcount =
lr_fhss_convolution_encode_viterbi_1_2_base( &encode_state, data_in, data_in_bitcount, data_out );
}
return data_out_bitcount;
}
STATIC uint16_t lr_fhss_convolution_encode_viterbi_1_3( const uint8_t *data_in, uint16_t data_in_bitcount,
uint8_t *data_out )
{
uint8_t encode_state = 0;
return lr_fhss_convolution_encode_viterbi_1_3_base( &encode_state, data_in, data_in_bitcount, data_out );
}
STATIC uint16_t sqrt_uint16( uint16_t x )
{
uint16_t y = 0;
while( y * y < x )
{
y += 1;
}
return y;
}
STATIC uint16_t lr_fhss_payload_interleaving( const uint8_t *data_in, uint16_t data_in_bitcount, uint8_t *data_out,
uint32_t output_offset )
{
uint16_t step = sqrt_uint16( data_in_bitcount );
const uint16_t step_v = step >> 1;
step = step << 1;
uint16_t pos = 0;
uint16_t st_idx = 0;
uint16_t st_idx_init = 0;
int16_t bits_left = data_in_bitcount;
uint16_t out_row_index = output_offset;
while( bits_left > 0 )
{
int16_t in_row_width = bits_left;
if( in_row_width > LR_FHSS_FRAG_BITS )
{
in_row_width = LR_FHSS_FRAG_BITS;
}
lr_fhss_set_bit_in_byte_vector( data_out, 0 + out_row_index, 0 ); // guard bits
lr_fhss_set_bit_in_byte_vector( data_out, 1 + out_row_index, 0 ); // guard bits
for( uint32_t j = 0; j < in_row_width; j++ )
{
lr_fhss_set_bit_in_byte_vector( data_out, j + 2 + out_row_index,
lr_fhss_extract_bit_in_byte_vector( data_in, pos ) ); // guard bit
pos += step;
if( pos >= data_in_bitcount )
{
st_idx += step_v;
if( st_idx >= step )
{
st_idx_init++;
st_idx = st_idx_init;
}
pos = st_idx;
}
}
bits_left -= LR_FHSS_FRAG_BITS;
out_row_index += 2 + in_row_width;
}
return out_row_index - output_offset;
}
STATIC void lr_fhss_raw_header( const lr_fhss_v1_params_t *params, uint16_t hop_sequence_id, uint16_t payload_length,
uint8_t *data_out )
{
data_out[0] = payload_length;
data_out[1] = ( params->modulation_type << 5 ) + ( params->cr << 3 ) + ( params->grid << 2 ) +
( params->enable_hopping ? 2 : 0 ) + ( params->bw >> 3 );
data_out[2] = ( ( params->bw & 0x07 ) << 5 ) + ( hop_sequence_id >> 4 );
data_out[3] = ( ( hop_sequence_id & 0x000F ) << 4 );
}
STATIC void lr_fhss_store_header_sync_word_index( uint8_t sync_word_index, uint8_t *data_out )
{
data_out[3] = ( data_out[3] & ~0x0C ) | ( sync_word_index << 2 );
}
STATIC uint16_t lr_fhss_get_bit_and_hop_count( const lr_fhss_v1_params_t *params, uint16_t payload_length,
uint8_t *nb_hops_out )
{
// check length : payload + 16bit crc, encoded, padded to 48bits, adding 2 guard bit / 48bits
uint16_t length_bits = ( payload_length + 2 ) * 8 + 6;
switch( params->cr )
{
case LR_FHSS_V1_CR_5_6:
length_bits = ( ( length_bits * 6 ) + 4 ) / 5;
break;
case LR_FHSS_V1_CR_2_3:
length_bits = length_bits * 3 / 2;
break;
case LR_FHSS_V1_CR_1_2:
length_bits = length_bits * 2;
break;
case LR_FHSS_V1_CR_1_3:
length_bits = length_bits * 3;
break;
}
*nb_hops_out = ( length_bits + 47 ) / 48 + params->header_count;
// calculate total number of payload bits, after breaking into blocks
uint16_t payload_bits = length_bits / LR_FHSS_FRAG_BITS * LR_FHSS_BLOCK_BITS;
uint16_t last_block_bits = length_bits % LR_FHSS_FRAG_BITS;
if( last_block_bits > 0 )
{
// add the 2 guard bits for the last block + the actual remaining payload bits
payload_bits += last_block_bits + 2;
}
return ( LR_FHSS_HEADER_BITS * params->header_count ) + payload_bits;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@@ -0,0 +1,198 @@
/*!
* @file lr_fhss_mac.h
*
* @brief Radio-independent LR-FHSS algorithms driver internal API
*
* 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.
*/
#ifndef LR_FHSS_MAC_H__
#define LR_FHSS_MAC_H__
/*
* -----------------------------------------------------------------------------
* --- DEPENDENCIES ------------------------------------------------------------
*/
#include <stdint.h>
#include <stdbool.h>
#include "radio_def.h"
#include "lr_fhss_v1_base_types.h"
/*
* -----------------------------------------------------------------------------
* --- PUBLIC MACROS -----------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PUBLIC CONSTANTS --------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PUBLIC TYPES ------------------------------------------------------------
*/
/*!
* Digest, holding physical payload length and hop information
*/
typedef struct lr_fhss_digest_s
{
uint16_t nb_bytes; /**< Length of LR-FHSS frame, in bytes */
uint16_t nb_bits; /**< Number of bits */
uint8_t nb_hops; /**< Number of hops */
} lr_fhss_digest_t;
/*!
* Hopping configuration, created by @ref lr_fhss_get_hop_params, and used to generate hop sequence
*/
typedef struct lr_fhss_hop_params_s
{
uint16_t n_grid; /**< Ngrid, as described in specification */
uint16_t polynomial; /**< polynomial, as described in specification, used for hop sequence generation */
uint16_t xoring_seed; /**< xoring seed, as described in specification, used for hop sequence generation */
uint16_t hop_sequence_id; /**< Hopping sequence seed, as described in specification, determines which hop sequence
will be used */
} lr_fhss_hop_params_t;
/*!
* -----------------------------------------------------------------------------
* --- PUBLIC FUNCTIONS PROTOTYPES ---------------------------------------------
*/
#ifdef __cplusplus
extern "C" {
#endif
/*!
* @brief Return the number of hop sequences available using the given parameters
*
* @param [in] params LR-FHSS parameter structure
*
* @returns Number of valid hop sequences (512 or 384)
*/
unsigned int lr_fhss_get_hop_sequence_count( const lr_fhss_v1_params_t *params );
/*!
* @brief Fill the digest structure with various size-related data for a LR-FHSS frame
*
* @param [in] params LR-FHSS parameter structure
* @param [in] payload_length Length of payload, in bytes
* @param [out] digest Contains block count byte count, and number of hops
*/
void lr_fhss_process_parameters( const lr_fhss_v1_params_t *params, uint16_t payload_length, lr_fhss_digest_t *digest );
/*!
* @brief Fill the hop structure with various hop-related data for a LR-FHSS frame, and provide initial state
*
* @param [in] params LR-FHSS parameter structure
* @param [out] hop_params Hop parameter structure
* @param [out] initial_state Initial LFSR state
* @param [in] hop_sequence_id The hop sequence ID that will be used to obtain hop-related data
*
* @returns Operation status
*/
radio_status_t lr_fhss_get_hop_params( const lr_fhss_v1_params_t *params, lr_fhss_hop_params_t *hop_params,
uint16_t *initial_state, uint16_t hop_sequence_id );
/*!
* @brief Update the LFSR state by performing a hop, and return the hop grid position
*
* @param [in,out] lfsr_state LFSR state
* @param [in] hop_params Hop parameter structure
*
* @returns Hop position in the grid
*/
uint16_t lr_fhss_get_next_state( uint16_t *lfsr_state, const lr_fhss_hop_params_t *hop_params );
/*!
* @brief Return the frequency in grid units for given LR-FHSS parameters and hop index
*
* @param [in,out] lfsr_state LFSR state
* @param [in] hop_params Hop parameter structure
* @param [in] params LR-FHSS parameter structure
*
* @returns Frequency, in grid units
*/
int16_t lr_fhss_get_next_freq_in_grid( uint16_t *lfsr_state, const lr_fhss_hop_params_t *hop_params,
const lr_fhss_v1_params_t *params );
/*!
* @brief Construct the LR-FHSS frame
*
* @param [in] params LR-FHSS parameter structure
* @param [in] hop_sequence_id The hop sequence ID that will be used to obtain hop-related data
* @param [in] data_in Pointer to input buffer
* @param [in] data_in_bytecount Length of input buffer, in bytes
* @param [out] data_out Pointer to a buffer into which the final LR-FHSS frame is stored, large enough to hold
* 255 bytes
*
* @returns Length of frame, in bytes
*/
uint16_t lr_fhss_build_frame( const lr_fhss_v1_params_t *params, uint16_t hop_sequence_id, const uint8_t *data_in,
uint16_t data_in_bytecount, uint8_t *data_out );
/*!
* @brief Compute the numerator for LR-FHSS time-on-air computation.
*
* @remark To get the actual time-on-air in seconds, this value must be divided by the LR-FHSS bitrate in bits per
* second, 488.28125.
*
* @param [in] params LR-FHSS parameter structure
* @param [in] payload_length Length of application payload, in bytes
*
* @returns LR-FHSS time-on-air numerator
*/
static inline uint32_t lr_fhss_get_time_on_air_numerator( const lr_fhss_v1_params_t *params, uint16_t payload_length )
{
lr_fhss_digest_t digest;
lr_fhss_process_parameters( params, payload_length, &digest );
return digest.nb_bits;
}
/*!
* @brief Get the time on air in ms for LR-FHSS transmission
*
* @param [in] params LR-FHSS parameter structure
* @param [in] payload_length Length of application-layer payload
*
* @returns Time-on-air value in ms for LR-FHSS transmission
*/
uint32_t lr_fhss_get_time_on_air_in_ms( const lr_fhss_v1_params_t *params, uint16_t payload_length );
#ifdef __cplusplus
}
#endif
#endif // LR_FHSS_MAC_H__
/* --- EOF ------------------------------------------------------------------ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,154 @@
/**
******************************************************************************
* @file radio_fw.h
* @author MCD Application Team
* @brief Extends radio capabilities (whitening, long packet)
******************************************************************************
* @attention
*
* Copyright (c) 2020(-2021) STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __RADIO_FW_H__
#define __RADIO_FW_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "radio_def.h"
#include "radio_ex.h"
/* Exported types ------------------------------------------------------------*/
/*reserved for SubGHz_Phy internal MW communication*/
typedef enum
{
CONFIG_RX = 0,
CONFIG_TX,
} ConfigGenericRTx_t;
typedef struct
{
TxConfigGeneric_t *TxConfig;
RxConfigGeneric_t *RxConfig;
ConfigGenericRTx_t rtx;
} ConfigGeneric_t;
/* Exported constants --------------------------------------------------------*/
/* External variables --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions prototypes ---------------------------------------------*/
/*!
* @brief Initialise the RFW module and enables custom whitening, optionally long packet feature
*
* @param [in] config rx or tx config from the application
* @param [in] RadioEvents from the radio
* @param [in] TimeoutTimerEvent Timer for Rx or Tx timeout event
* @return 0 when no parameters error, -1 otherwise
*/
int32_t RFW_Init( ConfigGeneric_t *config, RadioEvents_t *RadioEvents, TimerEvent_t *TimeoutTimerEvent );
/*!
* @brief Return whether the RFW module is enabled
*
* @return 0 when not initialised, -1 otherwise
*/
uint8_t RFW_Is_Init( void );
/*!
* @brief Return whether the RFW module long packet is enabled
*
* @return 0 when not initialised, -1 otherwise
*/
uint8_t RFW_Is_LongPacketModeEnabled( void );
/*!
* @brief Return whether the RFW module long packet is enabled
*
* @param [in] Modem set in the radio
*/
void RFW_SetRadioModem( RadioModems_t Modem );
/*!
* @brief DeInitialise the RFW module and enable custom whitening and optionally long packet feature
*
*/
void RFW_DeInit( void );
/*!
* @brief DeInitialise the TxLongPacket
*
*/
void RFW_DeInit_TxLongPacket( void );
/*!
* @brief Set antenna switch output to be used in Tx
*
* @param [in] AntSwitch RFO_LP or FRO_HP
*
*/
void RFW_SetAntSwitch( uint8_t AntSwitch );
/*!
* @brief Initialise reception for IBM whitening case
*
* @return 0 when RFW_ENABLE exists, -1 otherwise
*/
int32_t RFW_ReceiveInit( void );
/*!
* @brief Initialise transmission for IBM whitening case
*
* @param [in,out] inOutBuffer pointer of exchange buffer to send or receive data
* @param [in] size input buffer size
* @param [out] outSize output buffer size
*
*/
int32_t RFW_TransmitInit( uint8_t *inOutBuffer, uint8_t size, uint8_t *outSize );
/*!
* @brief Starts receiving payload. Called at Rx Sync IRQ
*
*/
void RFW_ReceivePayload( void );
/*!
* @brief Starts transmitting long Packet, note packet length may be on 1 bytes depending on config
*
* @param [in] payload_size total payload size to be sent
* @param [in] timeout Reception timeout [ms]
* @param [in] TxLongPacketGetNextChunkCb callback to be implemented on user side to feed partial chunk
* buffer: source buffer allocated by the app
* size: size in bytes to feed. User to implement the offset based on previous chunk request
* @return 0 when no parameters error, -1 otherwise
*/
int32_t RFW_TransmitLongPacket( uint16_t payload_size, uint32_t timeout, void ( *TxLongPacketGetNextChunkCb )( uint8_t **buffer, uint8_t buffer_size ) );
/*!
* @brief Starts receiving long Packet, packet maybe short
*
* @param [in] boosted_mode boosted_mode: 0 normal Rx, 1:improved sensitivity
* @param [in] timeout Reception timeout [ms]
* @param [in] RxLongStorePacketChunkCb callback to be implemented on user side to record partial chunk in the application
* buffer: source buffer allocated in the radio driver
* size: size in bytes to record
* @return 0 when no parameters error, -1 otherwise
*/
int32_t RFW_ReceiveLongPacket( uint8_t boosted_mode, uint32_t timeout, void ( *RxLongStorePacketChunkCb )( uint8_t *buffer, uint8_t chunk_size ) );
#ifdef __cplusplus
}
#endif
#endif /*__RADIO_FW_H__*/

View File

@@ -0,0 +1,53 @@
/******************************************************************************
* @file subghz_phy_version.h
* @author MCD Application Team
* @brief defines the radio driver version
******************************************************************************
* @attention
*
* Copyright (c) 2020(-2021) STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __SUBGHZ_PHY_VERSION_H__
#define __SUBGHZ_PHY_VERSION_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* __SUBGHZ_PHY_TYPE: 0x01 STM32WL
0x61 SX126X
0x72 SX1272
0x76 SX1276 */
#define SUBGHZ_PHY_VERSION_MAIN (0x01U) /*!< [31:24] main version */
#define SUBGHZ_PHY_VERSION_SUB1 (0x03U) /*!< [23:16] sub1 version */
#define SUBGHZ_PHY_VERSION_SUB2 (0x01U) /*!< [15:8] sub2 version */
#define SUBGHZ_PHY_TYPE (0x01U) /*!< [7:0] type version */
#define SUBGHZ_PHY_VERSION ((SUBGHZ_PHY_VERSION_MAIN << 24) \
|(SUBGHZ_PHY_VERSION_SUB1 << 16) \
|(SUBGHZ_PHY_VERSION_SUB2 << 8) \
|(SUBGHZ_PHY_TYPE))
/* Exported types ------------------------------------------------------------*/
/* External variables --------------------------------------------------------*/
/* Exported macros -----------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
#ifdef __cplusplus
}
#endif
#endif /*__SUBGHZ_PHY_VERSION_H__*/

View File

@@ -0,0 +1,425 @@
/*!
* @file wl_lr_fhss.c
*
* @brief STM32WL LR-FHSS 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 "wl_lr_fhss.h"
#include "lr_fhss_mac.h"
/*
* -----------------------------------------------------------------------------
* --- PRIVATE MACROS-----------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PRIVATE CONSTANTS -------------------------------------------------------
*/
/* \cond */
#define WL_SET_MODULATION_PARAMS ( 0x8B )
#define WL_SET_PKT_PARAMS ( 0x8C )
#define WL_LR_FHSS_DISABLE_HOPPING ( 0 )
#define WL_LR_FHSS_ENABLE_HOPPING ( 1 )
#define WL_LR_FHSS_HOP_TABLE_SIZE ( 16 )
#define WL_LR_FHSS_HOP_ENTRY_SIZE ( 6 )
#define WL_LR_FHSS_GRID_3906_HZ_PLL_STEPS ( 4096 )
#define WL_LR_FHSS_GRID_25391_HZ_PLL_STEPS ( 26624 )
#define WL_LR_FHSS_GRID_INDEX_TO_PLL_STEPS ( 512 )
/* \endcond */
/*
* -----------------------------------------------------------------------------
* --- PRIVATE TYPES -----------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PRIVATE VARIABLES -------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PRIVATE FUNCTION DECLARATIONS -------------------------------------------
*/
/*!
* @brief Configure the radio to perform frequency hopping
*
* @param [in] context Chip implementation context
* @param [in] nb_bytes Total number of bytes
* @param [in] nb_hops Total number of hops
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_write_hop_config( const uint8_t nb_bytes, const uint8_t nb_hops );
/*!
* @brief Write a hop frequency/duration pair to the radio hop table
*
* @param [in] context Chip implementation context
* @param [in] index Index to chip hop table
* @param [in] nb_symbols Hop duration in symbols
* @param [in] freq_in_pll_steps Hop frequency, in PLL steps
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_write_hop( const uint8_t index, const uint16_t nb_symbols,
const uint32_t freq_in_pll_steps );
/*!
* @brief Get Frequency, in PLL steps, of the next hop
*
* @param [in] params stm32wl LR-FHSS parameter structure
* @param [out] state stm32wl LR-FHSS state structure that will be initialized by this function
*
* @returns Frequency, in PLL steps, of the next hop
*/
uint32_t wl_lr_fhss_get_next_freq_in_pll_steps( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state );
/*!
* @brief Get grid frequency, in PLL steps
*
* @param [in] params stm32wl LR-FHSS parameter structure
*
* @returns Grid frequency, in PLL steps
*/
static inline unsigned int wl_lr_fhss_get_grid_in_pll_steps( const wl_lr_fhss_params_t *params );
/*
* -----------------------------------------------------------------------------
* --- PUBLIC FUNCTIONS DEFINITION ---------------------------------------------
*/
radio_status_t wl_lr_fhss_init( const wl_lr_fhss_params_t *params )
{
radio_status_t status = RADIO_STATUS_OK;
SUBGRF_SetPacketType( PACKET_TYPE_LR_FHSS );
uint8_t pkt_params_buffer[8] = {0};
uint8_t mod_params_buffer[] = {32, 0, 0, MOD_SHAPING_G_BT_1, 0, 0, 0, 0};
SUBGRF_WriteCommand( RADIO_SET_PACKETPARAMS, pkt_params_buffer, sizeof( pkt_params_buffer ) );
SUBGRF_WriteCommand( RADIO_SET_MODULATIONPARAMS, mod_params_buffer, sizeof( mod_params_buffer ) );
SUBGRF_SetBufferBaseAddress( 0x00, 0x00 );
return status;
}
radio_status_t wl_lr_fhss_process_parameters( const wl_lr_fhss_params_t *params, uint16_t hop_sequence_id,
uint16_t payload_length, wl_lr_fhss_state_t *state )
{
lr_fhss_process_parameters( &params->lr_fhss_params, payload_length, &state->digest );
if( state->digest.nb_bytes > LR_FHSS_MAX_PHY_PAYLOAD_BYTES )
{
return RADIO_STATUS_UNKNOWN_VALUE;
}
if( params->lr_fhss_params.grid == LR_FHSS_V1_GRID_25391_HZ )
{
if( params->device_offset > 25 || params->device_offset < -26 )
{
return RADIO_STATUS_UNKNOWN_VALUE;
}
if( params->lr_fhss_params.bw < LR_FHSS_V1_BW_722656_HZ )
{
return RADIO_STATUS_UNKNOWN_VALUE;
}
}
if( params->lr_fhss_params.grid == LR_FHSS_V1_GRID_3906_HZ )
{
if( params->device_offset > 3 || params->device_offset < -4 )
{
return RADIO_STATUS_UNKNOWN_VALUE;
}
}
// Initialize hop index and params
state->current_hop = 0;
radio_status_t status =
lr_fhss_get_hop_params( &params->lr_fhss_params, &state->hop_params, &state->lfsr_state, hop_sequence_id );
if( status != RADIO_STATUS_OK )
{
return ( radio_status_t ) status;
}
// Skip the hop frequencies inside the set [0, 4 - header_count):
if( params->lr_fhss_params.enable_hopping != 0 )
{
for( int i = 0; i < 4 - params->lr_fhss_params.header_count; ++i )
{
lr_fhss_get_next_state( &state->lfsr_state, &state->hop_params );
}
}
state->next_freq_in_pll_steps = wl_lr_fhss_get_next_freq_in_pll_steps( params, state );
return RADIO_STATUS_OK;
}
radio_status_t wl_lr_fhss_write_hop_sequence_head( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state )
{
radio_status_t status = wl_lr_fhss_write_hop_config( state->digest.nb_bytes, state->digest.nb_hops );
if( status != RADIO_STATUS_OK )
{
return status;
}
const uint16_t pulse_shape_compensation = 1;
if( params->lr_fhss_params.enable_hopping == 0 )
{
// (LR_FHSS_HEADER_BITS + pulse_shape_compensation) symbols on first sync_word, LR_FHSS_HEADER_BITS on next
// sync_words, LR_FHSS_BLOCK_BITS on payload
const uint16_t nb_symbols = state->digest.nb_bits + pulse_shape_compensation;
status = wl_lr_fhss_write_hop( state->current_hop, nb_symbols, state->next_freq_in_pll_steps );
if( status != RADIO_STATUS_OK )
{
return status;
}
state->current_hop++;
state->digest.nb_bits = 0;
}
else
{
// fill at most WL_LR_FHSS_HOP_TABLE_SIZE hops of the hardware hop table
uint8_t truncated_hops = state->digest.nb_hops;
if( truncated_hops > WL_LR_FHSS_HOP_TABLE_SIZE )
{
truncated_hops = WL_LR_FHSS_HOP_TABLE_SIZE;
}
while( state->current_hop < truncated_hops )
{
uint16_t nb_symbols;
// (LR_FHSS_HEADER_BITS + pulse_shape_compensation) symbols on first sync_word, LR_FHSS_HEADER_BITS on
// next sync_words, LR_FHSS_BLOCK_BITS on payload
if( state->current_hop >= params->lr_fhss_params.header_count )
{
if( state->digest.nb_bits > LR_FHSS_BLOCK_BITS )
{
nb_symbols = LR_FHSS_BLOCK_BITS;
}
else
{
nb_symbols = state->digest.nb_bits;
}
}
else if( state->current_hop > 0 )
{
nb_symbols = LR_FHSS_HEADER_BITS;
}
else
{
nb_symbols = LR_FHSS_HEADER_BITS + pulse_shape_compensation;
}
status = wl_lr_fhss_write_hop( state->current_hop, nb_symbols, state->next_freq_in_pll_steps );
if( status != RADIO_STATUS_OK )
{
return status;
}
state->current_hop++;
state->digest.nb_bits -= nb_symbols;
state->next_freq_in_pll_steps = wl_lr_fhss_get_next_freq_in_pll_steps( params, state );
}
}
return status;
}
radio_status_t wl_lr_fhss_write_payload( const wl_lr_fhss_state_t *state,
const uint8_t *payload )
{
//return wl_write_buffer( context, 0x00, payload, state->digest.nb_bytes );
SUBGRF_WriteBuffer( 0x00, ( uint8_t * ) payload, state->digest.nb_bytes );
return RADIO_STATUS_OK;
}
radio_status_t wl_lr_fhss_build_frame( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state, uint16_t hop_sequence_id,
const uint8_t *payload, uint16_t payload_length,
uint32_t *first_frequency_in_pll_steps )
{
radio_status_t status = wl_lr_fhss_process_parameters( params, hop_sequence_id, payload_length, state );
if( status != RADIO_STATUS_OK )
{
return status;
}
if( first_frequency_in_pll_steps != NULL )
{
*first_frequency_in_pll_steps = state->next_freq_in_pll_steps;
}
uint8_t tx_buffer[LR_FHSS_MAX_PHY_PAYLOAD_BYTES];
lr_fhss_build_frame( &params->lr_fhss_params, state->hop_params.hop_sequence_id, payload, payload_length,
tx_buffer );
status = wl_lr_fhss_write_payload( state, tx_buffer );
if( status != RADIO_STATUS_OK )
{
return status;
}
status = wl_lr_fhss_write_hop_sequence_head( params, state );
return status;
}
radio_status_t wl_lr_fhss_handle_hop( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state )
{
if( state->current_hop < state->digest.nb_hops )
{
uint16_t nb_bits;
if( state->digest.nb_bits > LR_FHSS_BLOCK_BITS )
{
nb_bits = LR_FHSS_BLOCK_BITS;
}
else
{
nb_bits = state->digest.nb_bits;
}
radio_status_t status = wl_lr_fhss_write_hop( state->current_hop % WL_LR_FHSS_HOP_TABLE_SIZE,
LR_FHSS_BLOCK_BITS, state->next_freq_in_pll_steps );
if( status != RADIO_STATUS_OK )
{
return status;
}
state->current_hop++;
state->digest.nb_bits -= nb_bits;
state->next_freq_in_pll_steps = wl_lr_fhss_get_next_freq_in_pll_steps( params, state );
}
return RADIO_STATUS_OK;
}
radio_status_t wl_lr_fhss_handle_tx_done( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state )
{
//const uint8_t ctrl = WL_LR_FHSS_DISABLE_HOPPING;
//return wl_write_register( context, WL_LR_FHSS_REG_CTRL, &ctrl, 1 );
SUBGRF_WriteRegister( WL_LR_FHSS_REG_CTRL, WL_LR_FHSS_DISABLE_HOPPING );
return RADIO_STATUS_OK;
}
/*
* -----------------------------------------------------------------------------
* --- PRIVATE FUNCTIONS DEFINITION --------------------------------------------
*/
radio_status_t wl_lr_fhss_write_hop_config( const uint8_t nb_bytes, const uint8_t nb_hops )
{
uint8_t data[] = { WL_LR_FHSS_ENABLE_HOPPING, nb_bytes, nb_hops };
//return wl_write_register( context, WL_LR_FHSS_REG_CTRL, data, 3 );
SUBGRF_WriteRegisters( WL_LR_FHSS_REG_CTRL, data, 3 );
return RADIO_STATUS_OK;
}
radio_status_t wl_lr_fhss_write_hop( const uint8_t index, const uint16_t nb_symbols,
const uint32_t freq_in_pll_steps )
{
if( index >= WL_LR_FHSS_HOP_TABLE_SIZE )
{
return RADIO_STATUS_ERROR;
}
uint8_t data[WL_LR_FHSS_HOP_ENTRY_SIZE] =
{
( uint8_t )( nb_symbols >> 8 ), ( uint8_t ) nb_symbols,
( uint8_t )( freq_in_pll_steps >> 24 ), ( uint8_t )( freq_in_pll_steps >> 16 ),
( uint8_t )( freq_in_pll_steps >> 8 ), ( uint8_t ) freq_in_pll_steps,
};
//return wl_write_register( context, WL_LR_FHSS_REG_NUM_SYMBOLS_0 + ( WL_LR_FHSS_HOP_ENTRY_SIZE * index ),
// data, WL_LR_FHSS_HOP_ENTRY_SIZE );
SUBGRF_WriteRegisters( WL_LR_FHSS_REG_NUM_SYMBOLS_0 + ( WL_LR_FHSS_HOP_ENTRY_SIZE * index ),
data, WL_LR_FHSS_HOP_ENTRY_SIZE );
return RADIO_STATUS_OK;
}
uint32_t wl_lr_fhss_get_next_freq_in_pll_steps( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state )
{
#ifdef HOP_AT_CENTER_FREQ
const int16_t freq_table = 0;
uint32_t grid_offset = 0;
#else
const int16_t freq_table =
lr_fhss_get_next_freq_in_grid( &state->lfsr_state, &state->hop_params, &params->lr_fhss_params );
uint32_t nb_channel_in_grid = params->lr_fhss_params.grid ? 8 : 52;
uint32_t grid_offset = ( 1 + ( state->hop_params.n_grid % 2 ) ) * ( nb_channel_in_grid / 2 );
#endif
unsigned int grid_in_pll_steps = wl_lr_fhss_get_grid_in_pll_steps( params );
uint32_t freq = params->center_freq_in_pll_steps - freq_table * grid_in_pll_steps -
( params->device_offset + grid_offset ) * WL_LR_FHSS_GRID_INDEX_TO_PLL_STEPS;
#ifndef HOP_AT_CENTER_FREQ
// Perform frequency correction for every other sync header
if( params->lr_fhss_params.enable_hopping && ( state->current_hop < params->lr_fhss_params.header_count ) )
{
if( ( ( ( params->lr_fhss_params.header_count - state->current_hop ) % 2 ) == 0 ) )
{
// OFFSET_SYNCWORD = 488.28125 / 2, and FREQ_STEP = 0.95367431640625, so
// OFFSET_SYNCWORD / FREQ_STEP = 256
freq = freq + 256;
}
}
#endif
return freq;
}
static inline unsigned int wl_lr_fhss_get_grid_in_pll_steps( const wl_lr_fhss_params_t *params )
{
return ( params->lr_fhss_params.grid == LR_FHSS_V1_GRID_3906_HZ ) ? WL_LR_FHSS_GRID_3906_HZ_PLL_STEPS : WL_LR_FHSS_GRID_25391_HZ_PLL_STEPS;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@@ -0,0 +1,238 @@
/*!
* @file wl_lr_fhss.h
*
* @brief STM32WL LR-FHSS driver module API (internal to SubGHz_Phy middleware)
*
* 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.
*/
#ifndef WL_LR_FHSS_H__
#define WL_LR_FHSS_H__
/*
* -----------------------------------------------------------------------------
* --- DEPENDENCIES ------------------------------------------------------------
*/
#include <stdint.h>
#include "radio_driver.h"
#include "lr_fhss_mac.h"
/*
* -----------------------------------------------------------------------------
* --- PUBLIC MACROS -----------------------------------------------------------
*/
/*
* -----------------------------------------------------------------------------
* --- PUBLIC CONSTANTS --------------------------------------------------------
*/
#define WL_LR_FHSS_REG_CTRL ( 0x0385 )
#define WL_LR_FHSS_REG_PACKET_LEN ( 0x0386 )
#define WL_LR_FHSS_REG_NUM_HOPS ( 0x0387 )
#define WL_LR_FHSS_REG_NUM_SYMBOLS_0 ( 0x0388 )
#define WL_LR_FHSS_REG_FREQ_0 ( 0x038A )
/*
* -----------------------------------------------------------------------------
* --- PUBLIC TYPES ------------------------------------------------------------
*/
/*!
* @brief STM32WL LR-FHSS LR-FHSS parameter definition
*/
typedef struct wl_lr_fhss_params_s
{
lr_fhss_v1_params_t lr_fhss_params;
uint32_t center_freq_in_pll_steps; /**< Center frequency in transceiver units */
int8_t device_offset; //<! Per device offset to avoid collisions over the air. Possible values:
//<! - if (lr_fhss_params.grid == LR_FHSS_V1_GRID_25391_HZ): [-26, 25]
//<! - if (lr_fhss_params.grid == LR_FHSS_V1_GRID_3906_HZ): [-4, 3]
} wl_lr_fhss_params_t;
/*!
* @brief STM32WL LR-FHSS LR-FHSS state definition
*/
typedef struct wl_lr_fhss_state_s
{
lr_fhss_hop_params_t hop_params;
lr_fhss_digest_t digest;
uint32_t next_freq_in_pll_steps; /**< Frequency that will be used on next hop */
uint16_t lfsr_state; /**< LFSR state for hop sequence generation */
uint8_t current_hop; /**< Index of the current hop */
} wl_lr_fhss_state_t;
/*
* -----------------------------------------------------------------------------
* --- PUBLIC FUNCTIONS PROTOTYPES ---------------------------------------------
*/
#ifdef __cplusplus
extern "C" {
#endif
/*!
* @brief Initialize LR-FHSS packet type and modulation parameters
*
* @param [in] params stm32wl LR-FHSS parameter structure
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_init( const wl_lr_fhss_params_t *params );
/*!
* @brief Check the parameters, and in case of success, generate the digest summary which contains important size info
*
* @param [in] params stm32wl LR-FHSS parameter structure
* @param [in] hop_sequence_id Specifies which hop sequence to use
* @param [in] payload_length Length of application-layer payload
* @param [out] state stm32wl LR-FHSS state structure that will be initialized by this function
*
* @remark It is not necessary to explicitly call this function if the helper function @ref wl_lr_fhss_build_frame
* is used. If the preprocessor symbol HOP_AT_CENTER_FREQ is defined, hopping will be performed with PA ramp
* up/down, but without actually changing frequencies.
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_process_parameters( const wl_lr_fhss_params_t *params, uint16_t hop_sequence_id,
uint16_t payload_length, wl_lr_fhss_state_t *state );
/*!
* @brief Sent the initial hopping confifguration to the radio
*
* @param [in] params stm32wl LR-FHSS parameter structure
* @param [in] state stm32wl LR-FHSS state structure
*
* @remark It is not necessary to explicitly call this function if the helper function @ref wl_lr_fhss_build_frame
* is used. If the preprocessor symbol HOP_AT_CENTER_FREQ is defined, hopping will be performed with PA ramp
* up/down, but without actually changing frequencies.
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_write_hop_sequence_head( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state );
/*!
* @brief Write physical LR-FHSS payload to radio
*
* @param [in] state stm32wl LR-FHSS state structure
* @param [in] payload Array containing application-layer payload
*
* @remark It is not necessary to explicitly call this function if the helper function @ref wl_lr_fhss_build_frame
* is used.
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_write_payload( const wl_lr_fhss_state_t *state,
const uint8_t *payload );
/*!
* @brief Check parameter validity, build a frame, then send it
*
* @param [in] params stm32wl LR-FHSS parameter structure
* @param [in] state stm32wl LR-FHSS state structure
* @param [in] hop_sequence_id Specifies which hop sequence to use
* @param [in] payload Array containing application-layer payload
* @param [in] payload_length Length of application-layer payload
* @param [out] first_frequency_in_pll_steps If non-NULL, provides the frequency that will be used on the first hop
*
* @remark This helper function calls the @ref wl_lr_fhss_process_parameters,
* lr_fhss_build_frame, @ref wl_lr_fhss_write_hop_sequence_head, and @ref wl_lr_fhss_write_payload
* functions. If the preprocessor symbol HOP_AT_CENTER_FREQ is defined, hopping will be performed with PA ramp
* up/down, but without actually changing frequencies.
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_build_frame( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state, uint16_t hop_sequence_id,
const uint8_t *payload, uint16_t payload_length,
uint32_t *first_frequency_in_pll_steps );
/*!
* @brief Perform an actual frequency hop
*
* @param [in] params stm32wl LR-FHSS parameter structure
* @param [in] state stm32wl LR-FHSS state structure
*
* @remark This should be called to respond to the WL_IRQ_LR_FHSS_HOP interrupt. If the preprocessor symbol
* HOP_AT_CENTER_FREQ is defined, hopping will be performed with PA ramp up/down, but without actually
* changing frequencies.
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_handle_hop( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state );
/*!
* @brief Indicate to the radio that frequency hopping is no longer needed
*
* @param [in] params stm32wl LR-FHSS parameter structure
* @param [in] state stm32wl LR-FHSS state structure
*
* @remark The should be called to respond to the WL_IRQ_TX_DONE interrupt following LR-FHSS transmission.
*
* @returns Operation status
*/
radio_status_t wl_lr_fhss_handle_tx_done( const wl_lr_fhss_params_t *params,
wl_lr_fhss_state_t *state );
/*!
* @brief Get the time on air in ms for LR-FHSS transmission
*
* @param [in] params stm32wl LR-FHSS parameter structure
* @param [in] payload_length Length of application-layer payload
*
* @returns Time-on-air value in ms for LR-FHSS transmission
*/
static inline uint32_t wl_lr_fhss_get_time_on_air_in_ms( const wl_lr_fhss_params_t *params,
uint16_t payload_length )
{
return lr_fhss_get_time_on_air_in_ms( &params->lr_fhss_params, payload_length );
}
/*!
* @brief Return the number of hop sequences available using the given parameters
*
* @param [in] params stm32wl LR-FHSS parameter structure
*
* @return Returns the number of valid hop sequences (512 or 384)
*/
static inline unsigned int wl_lr_fhss_get_hop_sequence_count( const wl_lr_fhss_params_t *params )
{
return lr_fhss_get_hop_sequence_count( &params->lr_fhss_params );
}
#ifdef __cplusplus
}
#endif
#endif // WL_LR_FHSS_H__
/* --- EOF ------------------------------------------------------------------ */