Files
crosstyan e6f96ea0e3 fix(llcc68): cache detected chip type
Store the detected chip type after the first successful version-register read so repeated radio operations do not keep querying the chip identity.

This preserves Unknown as uncached so transient read failures or unsupported responses can still be retried on a later call.
2026-06-16 19:36:45 +08:00

315 lines
12 KiB
C++

#ifndef AA1F69D0_32E3_44B6_BFD7_2013E1E86A6B
#define AA1F69D0_32E3_44B6_BFD7_2013E1E86A6B
#include "llcc68_definitions.hpp"
#include "llcc68_raw.h"
#include <cstdint>
#include <expected>
#include <optional>
#include <span>
#include <variant>
#include <zephyr/drivers/gpio.h>
namespace app::driver::llcc68 {
constexpr size_t MAX_BUFFER_PAYLOAD = CONFIG_LLCC68_MAX_PAYLOAD_LENGTH;
constexpr uint32_t TIMEOUT_NONE = 0;
constexpr uint32_t TIMEOUT_INF = 0xffffffff;
constexpr auto DEFAULT_TX_BUFFER_ADDRESS = 0x00;
constexpr auto DEFAULT_RX_BUFFER_ADDRESS = 0x80;
using user_dio1_handler_t = void (*)(const struct device *dev, void *user_data);
} // namespace app::driver::llcc68
namespace app::driver::llcc68 {
template <typename T, typename E>
using expected = std::expected<T, E>;
template <typename E>
using unexpected = std::unexpected<E>;
using unit = std::monostate;
constexpr auto DEFAULT_BUSY_TIMEOUT_MS = 100;
/**
* @brief calculate time-on-air for a LoRa packet
*
* @param len Payload length in bytes
* @param sf Spreading factor (7-12)
* @param bw Bandwidth (125, 250, 500 kHz)
* @param cr Coding rate (4/5, 4/6, 4/7, 4/8)
* @param preamble_length Preamble length in symbols
* @param header_type Header type (implicit or explicit).
* @param crc_type CRC type (none or 16-bit)
* @param ldro_on Low data rate optimization setting sent to the radio
*/
constexpr airtime_t calc_time_on_air(uint8_t len, uint8_t sf, LoRaBandwidth bw,
LoRaCodingRate cr,
uint16_t preamble_length,
LoRaHeaderType header_type,
LoRaCrcType crc_type, bool ldro_on);
struct LLCC68 {
/** trivial getter */
[[nodiscard]]
const struct llcc68_config &config() const;
struct llcc68_data &data();
[[nodiscard]]
std::optional<gpio_dt_spec> tx_enable_gpio() const;
[[nodiscard]]
std::optional<gpio_dt_spec> rx_enable_gpio() const;
using timeout_ms_t = uint16_t;
/** SPI operation API */
expected<unit, error_code>
read_stream(uint8_t cmd, std::span<uint8_t> data_to_host,
timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
expected<unit, error_code>
write_stream(uint8_t cmd, std::span<const uint8_t> data_from_host,
timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
expected<unit, error_code>
read_register(uint16_t reg, std::span<uint8_t> data_to_host,
timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
expected<unit, error_code>
write_register(uint16_t reg, std::span<const uint8_t> data_from_host,
timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
/** READ BUFFER */
/**
* @brief read buffer of any offset and length, copying to user buffer
* @param[in] offset offset in the LLCC68 buffer to read from
* @param[out] data_to_host buffer to copy data into, the read length is
* determined by the size of this span
* @returns number of bytes read
*/
expected<uint8_t, error_code>
read_buffer_copy(uint8_t offset, std::span<uint8_t> data_to_host,
timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
/**
* @brief read buffer of any offset and length
* @returns span referencing internal buffer
*/
expected<std::span<const uint8_t>, error_code>
read_buffer_ref(uint8_t offset, uint8_t n,
timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
/**
* @brief read the RX buffer (whose offset would be DEFAULT_RX_BUFFER_ADDRESS)
* with all its current available data
* @returns span referencing internal buffer
*/
expected<std::span<const uint8_t>, error_code>
read_rx_buffer_ref(timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
/** WRITE BUFFER */
expected<unit, error_code>
write_tx_buffer(std::span<const uint8_t> data_from_host);
expected<unit, error_code>
flush_tx_buffer(timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
expected<unit, error_code> write_buffer_immediate_mode(
uint8_t offset, std::span<const uint8_t> data_from_host,
timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
expected<unit, error_code> set_rf_switch_state(RfSwitchState state);
/** LLCC68 DataSheet Function */
expected<status_t, error_code> get_status();
expected<packet_status_t, error_code> get_packet_status();
expected<uint8_t, error_code> get_rssi_inst_x2_neg();
expected<unit, error_code> reset();
expected<unit, error_code> set_standby();
expected<ChipType, error_code> hal_get_chip_type();
expected<unit, error_code> set_dio_irq_params(irq_params_t params);
expected<irq_status_t, error_code> get_irq_status();
expected<unit, error_code>
clear_irq_status(irq_status_t irq = irq_status_t::all());
expected<unit, error_code> set_packet_type_lora();
expected<unit, error_code> set_packet_type_gfsk();
expected<unit, error_code>
set_modulation_params(modulation_params_t mod_params);
expected<unit, error_code> set_modulation_params(uint8_t sf, uint8_t bw,
uint8_t cr, uint8_t ldro);
expected<unit, error_code>
set_gfsk_modulation_params(gfsk_modulation_params_t mod_params);
/*!
\brief Sets LoRa sync word.
\param sync_word LoRa sync word to be set.
\note Differentiate the LoRa signal for Public or Private Network
Set to 0x3444 for Public Network
Set to 0x1424 for Private Network
*/
expected<unit, error_code> set_lora_sync_word(uint16_t sync_word);
expected<unit, error_code> set_dio2_as_rf_switch(bool en);
expected<unit, error_code> set_rf_frequency(freq_t freq_mhz);
expected<unit, error_code> set_rf_frequency(uint32_t freq_raw);
expected<unit, error_code>
set_packet_params(uint16_t preamble_length, uint8_t payload_length,
uint8_t crc_type, uint8_t hdr_type, uint8_t iq_type);
expected<unit, error_code>
set_gfsk_packet_params(gfsk_packet_params_t packet_params);
expected<unit, error_code>
set_gfsk_sync_word(std::span<const uint8_t> sync_word);
expected<unit, error_code>
set_gfsk_address_filtering(uint8_t node_address, uint8_t broadcast_address);
expected<unit, error_code> set_gfsk_crc_seed(uint16_t seed);
expected<unit, error_code> set_gfsk_crc_polynomial(uint16_t polynomial);
expected<unit, error_code> set_gfsk_whitening_seed(uint16_t seed);
expected<unit, error_code> calibrate_image(uint8_t freq1, uint8_t freq2);
expected<unit, error_code> set_tx_params(tx_params_t params);
expected<unit, error_code> set_tx_params(int8_t pwr,
LoRaTxRampTime ramp_time);
expected<unit, error_code> set_pa_config(pa_setting_t settings);
/**
* @brief fixes overly eager PA clamping
*
* fixes overly eager PA clamping
* see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.2 for
* details
*
* The LLCC68 platform embeds a Power Amplifier (PA) clamping mechanism,
* backing-off the power when over-voltage conditions are detected internally.
* This method is put in place to protect the internal devices and ensure
* long-term reliability of the chip. Considering a high-power operation of
* the LLCC68 (supporting +22dBm on-chip), these "clamping" devices are overly
* protective, causing the chip to back-down its output power when even a
* reasonable mismatch is detected at the PA output. The observation is
* typically 5 to 6 dB less output power than the expected.
*
* @param enable
* @return Result<Unit, error_code>
*/
expected<unit, error_code> fix_pa_clamping(bool enable = true);
/**
* \brief fixes inverted IQ on SX1262 rev. B
* \param iq_config RADIOLIB_SX126X_LORA_IQ_STANDARD or
* RADIOLIB_SX126X_LORA_IQ_INVERTED
* \see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.4
* for details
*/
expected<unit, error_code> fix_inverted_iq(uint8_t iq_config);
/**
* \brief Some sensitivity degradation may be observed on any LoRa device,
when receiving signals transmitted by the LLCC68 with a LoRa BW of 500 kHz.
* \note should be used before each packet transmission, to properly configure
the chip
* \param bw the bandwidth
* \sa SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.1 for
details
*/
expected<unit, error_code> fix_sensitivity(LoRaBandwidth bw);
expected<unit, error_code> fix_gfsk_tx_modulation();
expected<unit, error_code> fix_gfsk_low_bitrate(uint32_t bitrate_bps);
/**
* \brief Channel Activity Detection (CAD) params
*/
expected<unit, error_code> set_cad_params(cad_params_t params);
/**
* \brief Set the CAD mode
*
* The command SetCAD() can be used only in LoRa packet type. The Channel
* Activity Detection is a LoRa specific mode of operation where the device
* searches for the presence of a LoRa preamble signal. After the search has
* completed, the device returns in STDBY_RC mode. The length of the search is
* configured via the command SetCadParams(...). At the end of the search
* period, the device triggers the IRQ CAD DONE if it has been enabled. If a
* valid signal has been detected it also generates the IRQ CAD DETECTED.
*
* The time taken for the channel activity detection is dependent upon the
* LoRa® modulation settings used. For a given configuration (SF/BW) the
* typical CAD detection time can be selected to be either 1, 2, 4, 8 or 16
* symbols. Once the duration of the selected number of symbols has been done,
* the radio will remains for around half a symbol in Rx to post-process the
* measurement.
*/
expected<unit, error_code> set_cad();
expected<unit, error_code> set_tx(uint32_t timeout = TIMEOUT_NONE);
expected<unit, error_code> set_rx(uint32_t timeout = TIMEOUT_INF);
/**
* @brief Start LLCC68 RX duty-cycle/listen mode.
*
* rx_period and sleep_period are raw 24-bit LLCC68 RTC periods, not
* milliseconds. Datasheet section 13.1.7 defines one period as 15.625 us:
* RX duration = rx_period * 15.625 us
* sleep duration = sleep_period * 15.625 us
*
* Use rx_duty_cycle_period_from_ms() or set_rx_duty_cycle_ms() when caller
* inputs are in milliseconds.
*/
expected<unit, error_code> set_rx_duty_cycle(uint32_t rx_period,
uint32_t sleep_period);
expected<unit, error_code> set_rx_duty_cycle_ms(uint32_t rx_period_ms,
uint32_t sleep_period_ms);
expected<unit, error_code> set_sleep(sleep_config_t config);
expected<unit, error_code> set_tx_continuous_wave();
expected<unit, error_code> set_tx_infinite_preamble();
expected<unit, error_code>
set_buffer_base_address(uint8_t tx_base_addr = DEFAULT_TX_BUFFER_ADDRESS,
uint8_t rx_base_addr = DEFAULT_RX_BUFFER_ADDRESS);
struct rx_buffer_status_t {
uint8_t payload_length;
uint8_t start_address;
};
expected<rx_buffer_status_t, error_code>
get_rx_buffer_status(timeout_ms_t busy_timeout = DEFAULT_BUSY_TIMEOUT_MS);
/**
* @brief Random Number Generator (RNG)
*/
expected<uint32_t, error_code> random_number_gen();
/** DIO1 IRQ registration helpers (like sx126x) */
expected<unit, error_code> dio1_irq_enable();
void dio1_irq_disable();
void set_dio1_irq_handler(user_dio1_handler_t handler, void *user_data);
/** high level API that wraps the raw function call */
expected<unit, error_code> hal_modem_init(lora_parameters_t params);
expected<unit, error_code> hal_gfsk_modem_init(gfsk_parameters_t params);
expected<transmit_result, error_code>
hal_async_transmit(std::span<const uint8_t> data, lora_parameters_t params);
expected<transmit_result, error_code>
hal_async_flush(lora_parameters_t params);
expected<unit, error_code> hal_async_rx(lora_parameters_t params);
expected<transmit_result, error_code>
hal_gfsk_async_transmit(std::span<const uint8_t> data,
gfsk_parameters_t params);
expected<transmit_result, error_code>
hal_gfsk_async_flush(gfsk_parameters_t params);
expected<unit, error_code> hal_gfsk_async_rx(gfsk_parameters_t params);
expected<unit, error_code> hal_set_output_power(ChipType chip,
tx_params_t params);
error_code init();
/** properties */
const struct device *dev;
std::optional<ChipType> cached_chip_type{};
};
} // namespace app::driver::llcc68
namespace app::radio {
namespace llcc68 = app::driver::llcc68;
}
#endif /* AA1F69D0_32E3_44B6_BFD7_2013E1E86A6B */