feat: update LLCC68 documentation and replace outdated PDF with the latest version
This commit is contained in:
BIN
docs/DS_LLCC68 V1-1.pdf
LFS
Normal file
BIN
docs/DS_LLCC68 V1-1.pdf
LFS
Normal file
Binary file not shown.
BIN
docs/DS_LLCC68_V1.0.pdf
LFS
BIN
docs/DS_LLCC68_V1.0.pdf
LFS
Binary file not shown.
167
inc/llcc68.hpp
167
inc/llcc68.hpp
@ -65,6 +65,7 @@ constexpr auto MAX_RX_BUFFER_SIZE = 0xff - DEFAULT_RX_BUFFER_ADDRESS;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief return if STATEVAR has value that is NOT RADIO_TRANS_CMD_PROC_ERR
|
* @brief return if STATEVAR has value that is NOT RADIO_TRANS_CMD_PROC_ERR
|
||||||
|
* @note some read operation would still succeed even if the command processing error occurs
|
||||||
*/
|
*/
|
||||||
#define APP_RADIO_RETURN_ERR_IGNORE_PROC_ERR(STATEVAR) \
|
#define APP_RADIO_RETURN_ERR_IGNORE_PROC_ERR(STATEVAR) \
|
||||||
{ \
|
{ \
|
||||||
@ -100,12 +101,13 @@ template <typename T, typename E>
|
|||||||
using Result = app::utils::Result<T, E>;
|
using Result = app::utils::Result<T, E>;
|
||||||
using Unit = app::utils::Unit;
|
using Unit = app::utils::Unit;
|
||||||
|
|
||||||
using millisecond = std::chrono::duration<size_t, std::milli>;
|
using millisecond = app::utils::Instant::milliseconds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Whether to use only LDO regulator (true) or DC-DC regulator (false)
|
* \brief Whether to use only LDO regulator (true) or DC-DC regulator (false)
|
||||||
*/
|
*/
|
||||||
constexpr bool USE_REGULATOR_LDO = true;
|
constexpr bool USE_REGULATOR_LDO = true;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Whether the module has an XTAL (true) or TCXO (false)
|
\brief Whether the module has an XTAL (true) or TCXO (false)
|
||||||
|
|
||||||
@ -306,41 +308,41 @@ inline constexpr uint32_t frequency_raw(const freq_t freq) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Set the RF frequency in MHz, with necessary calibration and checks
|
* @brief Image calibration is done through the command CalibrateImage(...) for
|
||||||
* \param freq the frequency to set, which must be in range [150, 960]
|
* a given range of frequencies defined by the parameters freq1
|
||||||
* \param calibrate whether to calibrate the image
|
* and freq2. Once performed, the calibration is valid for all frequencies between
|
||||||
* \sa set_frequency_raw
|
* the two extremes used as parameters. Typically, the user can select the
|
||||||
|
* parameters freq1 and freq2 to cover any specific ISM band.
|
||||||
|
*
|
||||||
|
* @param freq the expected frequency in MHz
|
||||||
|
* @sa 9.2.1 Image Calibration for Specific Frequency Bands
|
||||||
*/
|
*/
|
||||||
inline Result<Unit, error_t>
|
inline Result<Unit, error_t>
|
||||||
set_frequency(freq_t freq, const bool calibrate = true) {
|
calibrate_image(freq_t freq) {
|
||||||
using f_t = decltype(freq);
|
|
||||||
if (not valid_freq(freq)) {
|
if (not valid_freq(freq)) {
|
||||||
return ue_t{error_t{error::RADIO_INVALID_FREQUENCY}};
|
return ue_t{error_t{error::RADIO_INVALID_FREQUENCY}};
|
||||||
}
|
}
|
||||||
if (calibrate) {
|
uint8_t data[2];
|
||||||
uint8_t data[2];
|
if (freq > 900.0) {
|
||||||
if (freq > f_t{900}) {
|
data[0] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_1;
|
||||||
data[0] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_1;
|
data[1] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_2;
|
||||||
data[1] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_2;
|
} else if (freq > 850.0) {
|
||||||
} else if (freq > f_t{850.0}) {
|
data[0] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_1;
|
||||||
data[0] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_1;
|
data[1] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_2;
|
||||||
data[1] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_2;
|
} else if (freq > 770.0) {
|
||||||
} else if (freq > f_t{770.0}) {
|
data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1;
|
||||||
data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1;
|
data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2;
|
||||||
data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2;
|
} else if (freq > 460.0) {
|
||||||
} else if (freq > f_t{460.0}) {
|
data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1;
|
||||||
data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1;
|
data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2;
|
||||||
data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2;
|
} else {
|
||||||
} else {
|
data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1;
|
||||||
data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1;
|
data[1] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_2;
|
||||||
data[1] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_2;
|
|
||||||
}
|
|
||||||
auto res = spi::write_stream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data);
|
|
||||||
APP_RADIO_RETURN_ERR(res);
|
|
||||||
delay_ms(5);
|
|
||||||
}
|
}
|
||||||
|
auto res = spi::write_stream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data);
|
||||||
return set_rf_frequency(frequency_raw(freq));
|
APP_RADIO_RETURN_ERR(res);
|
||||||
|
delay_ms(5);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Result<uint8_t, error_t>
|
inline Result<uint8_t, error_t>
|
||||||
@ -455,7 +457,7 @@ struct irq_params_t {
|
|||||||
* By default, all IRQ are masked (all ‘0’) and the user can enable them one by one (or several at a time) by setting the corresponding mask to ‘1’.
|
* By default, all IRQ are masked (all ‘0’) and the user can enable them one by one (or several at a time) by setting the corresponding mask to ‘1’.
|
||||||
*/
|
*/
|
||||||
inline Result<Unit, error_t>
|
inline Result<Unit, error_t>
|
||||||
set_dio_irq_params(const irq_params_t &irq_params) {
|
set_dio_irq_params(irq_params_t irq_params) {
|
||||||
const auto irqMask = irq_params.irqMask;
|
const auto irqMask = irq_params.irqMask;
|
||||||
const auto dio1Mask = irq_params.dio1Mask;
|
const auto dio1Mask = irq_params.dio1Mask;
|
||||||
const auto dio2Mask = irq_params.dio2Mask;
|
const auto dio2Mask = irq_params.dio2Mask;
|
||||||
@ -477,15 +479,18 @@ set_dio2_as_rf_switch(const bool en) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief set packet type and do the calibration
|
* \sa SetPacketType
|
||||||
*/
|
*/
|
||||||
inline Result<Unit, error_t> config_packet_type(uint8_t sf) {
|
inline Result<Unit, error_t>
|
||||||
|
set_packet_type_lora(uint8_t sf) {
|
||||||
Result<Unit, error_t> res;
|
Result<Unit, error_t> res;
|
||||||
constexpr auto mod = RADIOLIB_SX126X_PACKET_TYPE_LORA;
|
constexpr auto mod = RADIOLIB_SX126X_PACKET_TYPE_LORA;
|
||||||
uint8_t data[7];
|
uint8_t data[7];
|
||||||
data[0] = mod;
|
data[0] = mod;
|
||||||
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_PACKET_TYPE, std::span<const uint8_t>{data, 1});
|
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_PACKET_TYPE, std::span<const uint8_t>{data, 1});
|
||||||
APP_RADIO_RETURN_ERR(res);
|
APP_RADIO_RETURN_ERR(res);
|
||||||
|
// the command SetRxTxFallbackMode() defines into which mode the chip goes
|
||||||
|
// after a successful transmission or after a packet reception.
|
||||||
data[0] = RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_RC;
|
data[0] = RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_RC;
|
||||||
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, std::span<const uint8_t>{data, 1});
|
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, std::span<const uint8_t>{data, 1});
|
||||||
APP_RADIO_RETURN_ERR(res);
|
APP_RADIO_RETURN_ERR(res);
|
||||||
@ -500,15 +505,27 @@ inline Result<Unit, error_t> config_packet_type(uint8_t sf) {
|
|||||||
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, std::span<const uint8_t>{data, 7});
|
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, std::span<const uint8_t>{data, 7});
|
||||||
APP_RADIO_RETURN_ERR(res);
|
APP_RADIO_RETURN_ERR(res);
|
||||||
|
|
||||||
clear_irq_status();
|
std::ignore = clear_irq_status();
|
||||||
// default init to be non-interrupt
|
|
||||||
constexpr auto irq_params = irq_params_t{};
|
constexpr auto irq_params = irq_params_t{};
|
||||||
res = set_dio_irq_params(irq_params);
|
res = set_dio_irq_params(irq_params);
|
||||||
APP_RADIO_RETURN_ERR(res);
|
APP_RADIO_RETURN_ERR(res);
|
||||||
|
while (gpio::digital_read(BUSY_PIN) == gpio::HIGH) {}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef APP_RADIO_DISABLE_CALIBRATION
|
|
||||||
data[0] = RADIOLIB_SX126X_CALIBRATE_ALL;
|
/**
|
||||||
res = spi::write_stream(RADIOLIB_SX126X_CMD_CALIBRATE, std::span<const uint8_t>{data, 1});
|
* \brief At power up the radio performs calibration of RC64k, RC13M, PLL and
|
||||||
|
* ADC. It is however possible to launch a calibration of one or several
|
||||||
|
* blocks at any time starting in STDBY_RC mode. The calibrate function starts the
|
||||||
|
* calibration of a block defined by calibParam.
|
||||||
|
* \sa 13.1.12 Calibrate Function
|
||||||
|
*/
|
||||||
|
inline Result<Unit, error_t>
|
||||||
|
calibrate(uint8_t calibParam = RADIOLIB_SX126X_CALIBRATE_ALL) {
|
||||||
|
uint8_t data[1];
|
||||||
|
data[0] = calibParam;
|
||||||
|
auto res = spi::write_stream(RADIOLIB_SX126X_CMD_CALIBRATE, data);
|
||||||
APP_RADIO_RETURN_ERR(res);
|
APP_RADIO_RETURN_ERR(res);
|
||||||
// The total calibration time if all blocks are calibrated is 3.5 ms. The
|
// The total calibration time if all blocks are calibrated is 3.5 ms. The
|
||||||
// calibration must be launched in STDBY_RC mode and the BUSY pins will be
|
// calibration must be launched in STDBY_RC mode and the BUSY pins will be
|
||||||
@ -516,7 +533,6 @@ inline Result<Unit, error_t> config_packet_type(uint8_t sf) {
|
|||||||
// end of the procedure.
|
// end of the procedure.
|
||||||
delay_ms(5);
|
delay_ms(5);
|
||||||
while (gpio::digital_read(BUSY_PIN) == gpio::HIGH) {}
|
while (gpio::digital_read(BUSY_PIN) == gpio::HIGH) {}
|
||||||
#endif /** APP_RADIO_DISABLE_CALIBRATION */
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,13 +541,12 @@ inline Result<Unit, error_t> config_packet_type(uint8_t sf) {
|
|||||||
\param len Payload length in bytes.
|
\param len Payload length in bytes.
|
||||||
\returns Expected time-on-air in microseconds.
|
\returns Expected time-on-air in microseconds.
|
||||||
*/
|
*/
|
||||||
constexpr uint32_t
|
constexpr uint32_t calc_time_on_air(const size_t len,
|
||||||
calc_time_on_air(const size_t len,
|
uint8_t sf,
|
||||||
uint8_t sf,
|
uint8_t bw,
|
||||||
uint8_t bw,
|
uint8_t cr,
|
||||||
uint8_t cr,
|
uint16_t preamble_length,
|
||||||
uint16_t preamble_length,
|
uint8_t header_type) {
|
||||||
uint8_t header_type) {
|
|
||||||
// everything is in microseconds to allow integer arithmetic
|
// everything is in microseconds to allow integer arithmetic
|
||||||
// some constants have .25, these are multiplied by 4, and have _x4 postfix to indicate that fact
|
// some constants have .25, these are multiplied by 4, and have _x4 postfix to indicate that fact
|
||||||
const auto bw_ = float{bw_khz(bw)};
|
const auto bw_ = float{bw_khz(bw)};
|
||||||
@ -840,16 +855,6 @@ set_TCXO(const TcxoVoltage voltage, const uint32_t delay = 5000, const bool XTAL
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief if the symbol (length := 2^sf / bw_khz) > 16, then we're using LDR
|
|
||||||
*/
|
|
||||||
template <uint8_t bw, uint32_t sf>
|
|
||||||
bool static_is_use_ldr() {
|
|
||||||
constexpr auto khz = bw_khz(bw);
|
|
||||||
constexpr auto symbol_length = static_cast<uint32_t>(std::pow(2, sf) / khz);
|
|
||||||
return symbol_length > 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief set LoRa modulation parameters
|
* \brief set LoRa modulation parameters
|
||||||
* \param sf spread factor (5-12)
|
* \param sf spread factor (5-12)
|
||||||
@ -1296,32 +1301,6 @@ static constexpr auto begin = [](const lora_parameters_t ¶ms) -> Result<Unit
|
|||||||
return ue_t{error_t{error::RADIO_CHIP_NOT_FOUND}};
|
return ue_t{error_t{error::RADIO_CHIP_NOT_FOUND}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* FIXME
|
|
||||||
*
|
|
||||||
* The hardware of the SX126x chips can be designed to use either an
|
|
||||||
* internal LDO or an internal DCDC converter. The DCDC converter provides
|
|
||||||
* better current savings and will be used in most modules. If there are
|
|
||||||
* problems to get the SX126x to work, check which HW configuration is used
|
|
||||||
* and set USE_LDO accordingly.
|
|
||||||
* If USE_LDO is not set in the hwConfig, DCDC is used as default.
|
|
||||||
*
|
|
||||||
* - RADIO_TXEN and RADIO_RXEN are used on eByte E22-900M22S module to
|
|
||||||
* switch the antenna between RX and TX
|
|
||||||
* - DIO2 as antenna switch is used in the example Semtech design as default
|
|
||||||
* and might be used by many modules
|
|
||||||
* - DIO3 as antenna switch is used by e.g. Insight SIP ISP4520 module which
|
|
||||||
* integrates a nRF52832 and a SX126x chip
|
|
||||||
* - Some modules use DIO3 to control the power supply of the TXCO.
|
|
||||||
* - Some modules use DIO2 to switch the antenna between RX and TX and a
|
|
||||||
* separate GPIO to power the antenna switch on or off. Switching the
|
|
||||||
* antenna switch off can reduce the power consumption. The GPIO used to
|
|
||||||
* control the antenna power is defined as RADIO_RXEN. LOW == power off,
|
|
||||||
* HIGH == power on.
|
|
||||||
*
|
|
||||||
* @sa https://github.com/beegee-tokyo/SX126x-Arduino?tab=readme-ov-file#explanation-for-ldo-and-dcdc-selection
|
|
||||||
*/
|
|
||||||
|
|
||||||
if constexpr (USE_TXCO) {
|
if constexpr (USE_TXCO) {
|
||||||
const auto res = set_TCXO(DEFAULT_TCXO_VOLTAGE);
|
const auto res = set_TCXO(DEFAULT_TCXO_VOLTAGE);
|
||||||
APP_RADIO_RETURN_ERR_CTX(res, "set TCXO");
|
APP_RADIO_RETURN_ERR_CTX(res, "set TCXO");
|
||||||
@ -1329,10 +1308,14 @@ static constexpr auto begin = [](const lora_parameters_t ¶ms) -> Result<Unit
|
|||||||
const auto &mod_params = params.mod_params;
|
const auto &mod_params = params.mod_params;
|
||||||
const auto &packet_params = params.packet_params;
|
const auto &packet_params = params.packet_params;
|
||||||
|
|
||||||
// SetPacketType
|
res = set_packet_type_lora(mod_params.sf);
|
||||||
res = config_packet_type(mod_params.sf);
|
|
||||||
APP_RADIO_RETURN_ERR_CTX(res, "config packet type");
|
APP_RADIO_RETURN_ERR_CTX(res, "config packet type");
|
||||||
|
|
||||||
|
#ifndef APP_RADIO_DISABLE_CALIBRATION
|
||||||
|
res = calibrate();
|
||||||
|
APP_RADIO_RETURN_ERR_CTX(res, "calibrate");
|
||||||
|
#endif /* APP_RADIO_DISABLE_CALIBRATION */
|
||||||
|
|
||||||
res = set_modulation_params(mod_params.sf,
|
res = set_modulation_params(mod_params.sf,
|
||||||
mod_params.bw,
|
mod_params.bw,
|
||||||
mod_params.cr,
|
mod_params.cr,
|
||||||
@ -1352,17 +1335,15 @@ static constexpr auto begin = [](const lora_parameters_t ¶ms) -> Result<Unit
|
|||||||
res = set_dio2_as_rf_switch(false);
|
res = set_dio2_as_rf_switch(false);
|
||||||
APP_RADIO_RETURN_ERR_CTX(res, "set dio2 as rf switch");
|
APP_RADIO_RETURN_ERR_CTX(res, "set dio2 as rf switch");
|
||||||
|
|
||||||
#ifdef APP_RADIO_DISABLE_CALIBRATION
|
res = set_rf_frequency(params.frequency);
|
||||||
res = set_frequency(params.frequency, false);
|
|
||||||
res = get_device_error_print_and_clear(TAG);
|
|
||||||
#else
|
|
||||||
res = set_frequency(params.frequency, true);
|
|
||||||
res = get_device_error_print_and_clear(TAG);
|
|
||||||
#endif /* APP_RADIO_DISABLE_CALIBRATION */
|
|
||||||
|
|
||||||
APP_RADIO_RETURN_ERR_CTX(res, "set frequency");
|
APP_RADIO_RETURN_ERR_CTX(res, "set frequency");
|
||||||
|
|
||||||
// SetPacketParams
|
#ifndef APP_RADIO_DISABLE_CALIBRATION
|
||||||
|
res = calibrate_image(params.frequency);
|
||||||
|
res = get_device_error_print_and_clear(TAG);
|
||||||
|
APP_RADIO_RETURN_ERR_CTX(res, "calibrate image");
|
||||||
|
#endif /* APP_RADIO_DISABLE_CALIBRATION */
|
||||||
|
|
||||||
res = set_packet_params(packet_params.preamble_len,
|
res = set_packet_params(packet_params.preamble_len,
|
||||||
packet_params.payload_len,
|
packet_params.payload_len,
|
||||||
packet_params.crc_type,
|
packet_params.crc_type,
|
||||||
|
|||||||
Reference in New Issue
Block a user