feat: update LLCC68 documentation and replace outdated PDF with the latest version

This commit is contained in:
2025-08-08 12:04:32 +08:00
parent 92130d4a7a
commit 333d2f5874
3 changed files with 77 additions and 96 deletions

View File

@ -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
* @note some read operation would still succeed even if the command processing error occurs
*/
#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 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)
*/
constexpr bool USE_REGULATOR_LDO = true;
/*!
\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
* \param freq the frequency to set, which must be in range [150, 960]
* \param calibrate whether to calibrate the image
* \sa set_frequency_raw
* @brief Image calibration is done through the command CalibrateImage(...) for
* a given range of frequencies defined by the parameters freq1
* and freq2. Once performed, the calibration is valid for all frequencies between
* 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>
set_frequency(freq_t freq, const bool calibrate = true) {
using f_t = decltype(freq);
calibrate_image(freq_t freq) {
if (not valid_freq(freq)) {
return ue_t{error_t{error::RADIO_INVALID_FREQUENCY}};
}
if (calibrate) {
uint8_t data[2];
if (freq > f_t{900}) {
data[0] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_2;
} else if (freq > f_t{850.0}) {
data[0] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_2;
} else if (freq > f_t{770.0}) {
data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2;
} else if (freq > f_t{460.0}) {
data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2;
} else {
data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1;
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);
uint8_t data[2];
if (freq > 900.0) {
data[0] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_2;
} else if (freq > 850.0) {
data[0] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_2;
} else if (freq > 770.0) {
data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2;
} else if (freq > 460.0) {
data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2;
} else {
data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1;
data[1] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_2;
}
return set_rf_frequency(frequency_raw(freq));
auto res = spi::write_stream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data);
APP_RADIO_RETURN_ERR(res);
delay_ms(5);
return {};
}
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.
*/
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 dio1Mask = irq_params.dio1Mask;
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;
constexpr auto mod = RADIOLIB_SX126X_PACKET_TYPE_LORA;
uint8_t data[7];
data[0] = mod;
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_PACKET_TYPE, std::span<const uint8_t>{data, 1});
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;
res = spi::write_stream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, std::span<const uint8_t>{data, 1});
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});
APP_RADIO_RETURN_ERR(res);
clear_irq_status();
// default init to be non-interrupt
std::ignore = clear_irq_status();
constexpr auto irq_params = irq_params_t{};
res = set_dio_irq_params(irq_params);
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);
// 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
@ -516,7 +533,6 @@ inline Result<Unit, error_t> config_packet_type(uint8_t sf) {
// end of the procedure.
delay_ms(5);
while (gpio::digital_read(BUSY_PIN) == gpio::HIGH) {}
#endif /** APP_RADIO_DISABLE_CALIBRATION */
return {};
}
@ -525,13 +541,12 @@ inline Result<Unit, error_t> config_packet_type(uint8_t sf) {
\param len Payload length in bytes.
\returns Expected time-on-air in microseconds.
*/
constexpr uint32_t
calc_time_on_air(const size_t len,
uint8_t sf,
uint8_t bw,
uint8_t cr,
uint16_t preamble_length,
uint8_t header_type) {
constexpr uint32_t calc_time_on_air(const size_t len,
uint8_t sf,
uint8_t bw,
uint8_t cr,
uint16_t preamble_length,
uint8_t header_type) {
// 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
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
* \param sf spread factor (5-12)
@ -1296,32 +1301,6 @@ static constexpr auto begin = [](const lora_parameters_t &params) -> Result<Unit
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) {
const auto res = set_TCXO(DEFAULT_TCXO_VOLTAGE);
APP_RADIO_RETURN_ERR_CTX(res, "set TCXO");
@ -1329,10 +1308,14 @@ static constexpr auto begin = [](const lora_parameters_t &params) -> Result<Unit
const auto &mod_params = params.mod_params;
const auto &packet_params = params.packet_params;
// SetPacketType
res = config_packet_type(mod_params.sf);
res = set_packet_type_lora(mod_params.sf);
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,
mod_params.bw,
mod_params.cr,
@ -1352,17 +1335,15 @@ static constexpr auto begin = [](const lora_parameters_t &params) -> Result<Unit
res = set_dio2_as_rf_switch(false);
APP_RADIO_RETURN_ERR_CTX(res, "set dio2 as rf switch");
#ifdef APP_RADIO_DISABLE_CALIBRATION
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 */
res = set_rf_frequency(params.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,
packet_params.payload_len,
packet_params.crc_type,