feat: add shared LLCC68 Zephyr module
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
#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 = 128;
|
||||
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)
|
||||
*/
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
void tx_rx_en_pin_set(TxRxPinState state);
|
||||
|
||||
/** LLCC68 DataSheet Function */
|
||||
|
||||
expected<status_t, error_code> get_status();
|
||||
expected<packet_status_t, error_code> get_packet_status();
|
||||
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> 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);
|
||||
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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
#ifndef BD6F9621_EF52_430C_86CB_3BC688233342
|
||||
#define BD6F9621_EF52_430C_86CB_3BC688233342
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define LLCC68_MAX_BUFFER_PAYLOAD 128
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*llcc68_user_dio1_handler_t)(const struct device *dev, void *user_data);
|
||||
|
||||
struct llcc68_config {
|
||||
struct spi_dt_spec spi;
|
||||
struct gpio_dt_spec reset_gpio;
|
||||
struct gpio_dt_spec busy_gpio;
|
||||
struct gpio_dt_spec dio1_gpio;
|
||||
struct gpio_dt_spec tx_enable_gpio;
|
||||
struct gpio_dt_spec rx_enable_gpio;
|
||||
};
|
||||
|
||||
struct llcc68_data {
|
||||
struct gpio_callback dio1_irq_callback;
|
||||
const struct device *self;
|
||||
uint8_t tx_buffer[LLCC68_MAX_BUFFER_PAYLOAD + 4];
|
||||
uint8_t rx_buffer[LLCC68_MAX_BUFFER_PAYLOAD + 4];
|
||||
size_t tx_xfer_size;
|
||||
llcc68_user_dio1_handler_t dio1_user_handler;
|
||||
void *dio1_user_data;
|
||||
};
|
||||
|
||||
int llcc68_init(const struct device *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BD6F9621_EF52_430C_86CB_3BC688233342 */
|
||||
Reference in New Issue
Block a user