1c3626d58b
Add an explicit rf-switch-mode devicetree property for LLCC68 instances, covering no switch handling, TXEN/RXEN complementary GPIO control, and DIO2 single-pin control for PE4259-style RF switches. Preserve the existing default behavior with an auto Kconfig default that only enables complementary GPIO handling when both TXEN and RXEN GPIOs are present. Resolve the RF switch mode into llcc68_config at build time and validate incompatible devicetree combinations with BUILD_ASSERT checks. Configure optional RXEN GPIO handling for DIO2 single-pin mode and keep DIO2 RF switch control disabled unless that mode is selected. Replace the old fire-and-forget TX/RX GPIO helper with a result-returning mode-aware RF switch state helper, and apply it across standby, sleep, CAD, TX, RX, continuous wave, infinite preamble, and modem init paths. Add SetRxDutyCycle support with explicit raw 24-bit LLCC68 period units, plus helpers and a millisecond wrapper for callers that work in time units. Select the RX RF path before issuing the duty-cycle command so RXEN stays valid for duty-cycle listen windows.
314 lines
12 KiB
C++
314 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;
|
|
};
|
|
} // namespace app::driver::llcc68
|
|
|
|
namespace app::radio {
|
|
namespace llcc68 = app::driver::llcc68;
|
|
}
|
|
|
|
#endif /* AA1F69D0_32E3_44B6_BFD7_2013E1E86A6B */
|