Merge #1407
1407: Remove legacy LoRa drivers r=Dirbaio a=ceekdee Remove legacy LoRa drivers and associated configuration. Co-authored-by: ceekdee <taigatensor@gmail.com> Co-authored-by: Chuck Davis <taigatensor@gmail.com>
This commit is contained in:
		@@ -35,7 +35,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
 | 
			
		||||
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
 | 
			
		||||
 | 
			
		||||
- **LoRa** - 
 | 
			
		||||
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers.
 | 
			
		||||
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
 | 
			
		||||
 | 
			
		||||
- **USB** - 
 | 
			
		||||
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
 | 
			
		||||
 
 | 
			
		||||
@@ -7,22 +7,13 @@ license = "MIT OR Apache-2.0"
 | 
			
		||||
[package.metadata.embassy_docs]
 | 
			
		||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/"
 | 
			
		||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
 | 
			
		||||
features = ["time", "defmt"]
 | 
			
		||||
flavors = [
 | 
			
		||||
    { name = "sx126x",  target = "thumbv7em-none-eabihf", features = ["sx126x"] },
 | 
			
		||||
    { name = "sx127x",  target = "thumbv7em-none-eabihf", features = ["sx127x"] },
 | 
			
		||||
    { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[lib]
 | 
			
		||||
features = ["stm32wl", "time", "defmt"]
 | 
			
		||||
target = "thumbv7em-none-eabi"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
sx126x = []
 | 
			
		||||
sx127x = []
 | 
			
		||||
stm32wl = ["dep:embassy-stm32"]
 | 
			
		||||
time = []
 | 
			
		||||
defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"]
 | 
			
		||||
external-lora-phy = ["dep:lora-phy"]
 | 
			
		||||
defmt = ["dep:defmt", "lorawan-device/defmt"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +30,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw
 | 
			
		||||
embedded-hal = { version = "0.2", features = ["unproven"] }
 | 
			
		||||
bit_field = { version = "0.10" }
 | 
			
		||||
 | 
			
		||||
lora-phy = { version = "1", optional = true }
 | 
			
		||||
lora-phy = { version = "1" }
 | 
			
		||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
 | 
			
		||||
lorawan = { version = "0.7.3", default-features = false }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,12 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![feature(async_fn_in_trait, impl_trait_projections)]
 | 
			
		||||
#![allow(incomplete_features)]
 | 
			
		||||
//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device
 | 
			
		||||
//! crate's async LoRaWAN MAC implementation.
 | 
			
		||||
//! embassy-lora holds LoRa-specific functionality.
 | 
			
		||||
 | 
			
		||||
pub(crate) mod fmt;
 | 
			
		||||
#[cfg(feature = "external-lora-phy")]
 | 
			
		||||
/// interface variants required by the external lora crate
 | 
			
		||||
pub mod iv;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "stm32wl")]
 | 
			
		||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
 | 
			
		||||
pub mod stm32wl;
 | 
			
		||||
#[cfg(feature = "sx126x")]
 | 
			
		||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
 | 
			
		||||
pub mod sx126x;
 | 
			
		||||
#[cfg(feature = "sx127x")]
 | 
			
		||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
 | 
			
		||||
pub mod sx127x;
 | 
			
		||||
/// interface variants required by the external lora physical layer crate (lora-phy)
 | 
			
		||||
pub mod iv;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "time")]
 | 
			
		||||
use embassy_time::{Duration, Instant, Timer};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,291 +0,0 @@
 | 
			
		||||
//! A radio driver integration for the radio found on STM32WL family devices.
 | 
			
		||||
#![allow(deprecated)]
 | 
			
		||||
use core::future::poll_fn;
 | 
			
		||||
use core::task::Poll;
 | 
			
		||||
 | 
			
		||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
 | 
			
		||||
use embassy_stm32::dma::NoDma;
 | 
			
		||||
use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO};
 | 
			
		||||
use embassy_stm32::subghz::{
 | 
			
		||||
    CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams,
 | 
			
		||||
    LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk,
 | 
			
		||||
    Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams,
 | 
			
		||||
};
 | 
			
		||||
use embassy_sync::waitqueue::AtomicWaker;
 | 
			
		||||
use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
 | 
			
		||||
use lorawan_device::async_device::Timings;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum State {
 | 
			
		||||
    Idle,
 | 
			
		||||
    Txing,
 | 
			
		||||
    Rxing,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct RadioError;
 | 
			
		||||
 | 
			
		||||
static IRQ_WAKER: AtomicWaker = AtomicWaker::new();
 | 
			
		||||
 | 
			
		||||
/// The radio peripheral keeping the radio state and owning the radio IRQ.
 | 
			
		||||
pub struct SubGhzRadio<'d, RS> {
 | 
			
		||||
    radio: SubGhz<'d, NoDma, NoDma>,
 | 
			
		||||
    switch: RS,
 | 
			
		||||
    irq: PeripheralRef<'d, SUBGHZ_RADIO>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Default)]
 | 
			
		||||
#[non_exhaustive]
 | 
			
		||||
pub struct SubGhzRadioConfig {
 | 
			
		||||
    pub reg_mode: RegMode,
 | 
			
		||||
    pub calibrate_image: CalibrateImage,
 | 
			
		||||
    pub pa_config: PaConfig,
 | 
			
		||||
    pub tx_params: TxParams,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
 | 
			
		||||
    /// Create a new instance of a SubGhz radio for LoRaWAN.
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        mut radio: SubGhz<'d, NoDma, NoDma>,
 | 
			
		||||
        switch: RS,
 | 
			
		||||
        irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd,
 | 
			
		||||
        config: SubGhzRadioConfig,
 | 
			
		||||
    ) -> Result<Self, RadioError> {
 | 
			
		||||
        into_ref!(irq);
 | 
			
		||||
 | 
			
		||||
        radio.reset();
 | 
			
		||||
 | 
			
		||||
        irq.disable();
 | 
			
		||||
        irq.set_handler(|_| {
 | 
			
		||||
            IRQ_WAKER.wake();
 | 
			
		||||
            unsafe { SUBGHZ_RADIO::steal().disable() };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        configure_radio(&mut radio, config)?;
 | 
			
		||||
 | 
			
		||||
        Ok(Self { radio, switch, irq })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form
 | 
			
		||||
    /// the upcoming RX window start.
 | 
			
		||||
    async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> {
 | 
			
		||||
        trace!("TX request: {:?}", config);
 | 
			
		||||
        self.switch.set_tx();
 | 
			
		||||
 | 
			
		||||
        self.radio
 | 
			
		||||
            .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?;
 | 
			
		||||
 | 
			
		||||
        self.set_lora_mod_params(config.rf)?;
 | 
			
		||||
 | 
			
		||||
        let packet_params = LoRaPacketParams::new()
 | 
			
		||||
            .set_preamble_len(8)
 | 
			
		||||
            .set_header_type(HeaderType::Variable)
 | 
			
		||||
            .set_payload_len(buf.len() as u8)
 | 
			
		||||
            .set_crc_en(true)
 | 
			
		||||
            .set_invert_iq(false);
 | 
			
		||||
 | 
			
		||||
        self.radio.set_lora_packet_params(&packet_params)?;
 | 
			
		||||
 | 
			
		||||
        let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout);
 | 
			
		||||
        self.radio.set_irq_cfg(&irq_cfg)?;
 | 
			
		||||
 | 
			
		||||
        self.radio.set_buffer_base_address(0, 0)?;
 | 
			
		||||
        self.radio.write_buffer(0, buf)?;
 | 
			
		||||
 | 
			
		||||
        // The maximum airtime for any LoRaWAN package is 2793.5ms.
 | 
			
		||||
        // The value of 4000ms is copied from C driver and gives us a good safety margin.
 | 
			
		||||
        self.radio.set_tx(Timeout::from_millis_sat(4000))?;
 | 
			
		||||
        trace!("TX started");
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            let (_status, irq_status) = self.irq_wait().await;
 | 
			
		||||
 | 
			
		||||
            if irq_status & Irq::TxDone.mask() != 0 {
 | 
			
		||||
                trace!("TX done");
 | 
			
		||||
                return Ok(0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if irq_status & Irq::Timeout.mask() != 0 {
 | 
			
		||||
                return Err(RadioError);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> {
 | 
			
		||||
        let mod_params = LoRaModParams::new()
 | 
			
		||||
            .set_sf(convert_spreading_factor(&config.spreading_factor))
 | 
			
		||||
            .set_bw(convert_bandwidth(&config.bandwidth))
 | 
			
		||||
            .set_cr(CodingRate::Cr45)
 | 
			
		||||
            .set_ldro_en(matches!(
 | 
			
		||||
                (config.spreading_factor, config.bandwidth),
 | 
			
		||||
                (SpreadingFactor::_12, Bandwidth::_125KHz)
 | 
			
		||||
                    | (SpreadingFactor::_12, Bandwidth::_250KHz)
 | 
			
		||||
                    | (SpreadingFactor::_11, Bandwidth::_125KHz)
 | 
			
		||||
            ));
 | 
			
		||||
        self.radio.set_lora_mod_params(&mod_params)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Perform a radio receive operation with the radio config and receive buffer. The receive buffer must
 | 
			
		||||
    /// be able to hold a single LoRaWAN packet.
 | 
			
		||||
    async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> {
 | 
			
		||||
        assert!(buf.len() >= 255);
 | 
			
		||||
        trace!("RX request: {:?}", config);
 | 
			
		||||
        self.switch.set_rx();
 | 
			
		||||
 | 
			
		||||
        self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?;
 | 
			
		||||
 | 
			
		||||
        self.set_lora_mod_params(config)?;
 | 
			
		||||
 | 
			
		||||
        let packet_params = LoRaPacketParams::new()
 | 
			
		||||
            .set_preamble_len(8)
 | 
			
		||||
            .set_header_type(HeaderType::Variable)
 | 
			
		||||
            .set_payload_len(0xFF)
 | 
			
		||||
            .set_crc_en(false)
 | 
			
		||||
            .set_invert_iq(true);
 | 
			
		||||
        self.radio.set_lora_packet_params(&packet_params)?;
 | 
			
		||||
 | 
			
		||||
        let irq_cfg = CfgIrq::new()
 | 
			
		||||
            .irq_enable_all(Irq::RxDone)
 | 
			
		||||
            .irq_enable_all(Irq::PreambleDetected)
 | 
			
		||||
            .irq_enable_all(Irq::HeaderValid)
 | 
			
		||||
            .irq_enable_all(Irq::HeaderErr)
 | 
			
		||||
            .irq_enable_all(Irq::Err)
 | 
			
		||||
            .irq_enable_all(Irq::Timeout);
 | 
			
		||||
        self.radio.set_irq_cfg(&irq_cfg)?;
 | 
			
		||||
 | 
			
		||||
        self.radio.set_buffer_base_address(0, 0)?;
 | 
			
		||||
 | 
			
		||||
        // NOTE: Upper layer handles timeout by cancelling the future
 | 
			
		||||
        self.radio.set_rx(Timeout::DISABLED)?;
 | 
			
		||||
 | 
			
		||||
        trace!("RX started");
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            let (_status, irq_status) = self.irq_wait().await;
 | 
			
		||||
 | 
			
		||||
            if irq_status & Irq::RxDone.mask() != 0 {
 | 
			
		||||
                let (_status, len, ptr) = self.radio.rx_buffer_status()?;
 | 
			
		||||
                let packet_status = self.radio.lora_packet_status()?;
 | 
			
		||||
                let rssi = packet_status.rssi_pkt().to_integer();
 | 
			
		||||
                let snr = packet_status.snr_pkt().to_integer();
 | 
			
		||||
                self.radio.read_buffer(ptr, &mut buf[..len as usize])?;
 | 
			
		||||
                self.radio.set_standby(StandbyClk::Rc)?;
 | 
			
		||||
 | 
			
		||||
                #[cfg(feature = "defmt")]
 | 
			
		||||
                trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]);
 | 
			
		||||
 | 
			
		||||
                #[cfg(feature = "log")]
 | 
			
		||||
                trace!("RX done: {:02x?}", &mut buf[..len as usize]);
 | 
			
		||||
                return Ok((len as usize, RxQuality::new(rssi, snr as i8)));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if irq_status & Irq::Timeout.mask() != 0 {
 | 
			
		||||
                return Err(RadioError);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn irq_wait(&mut self) -> (Status, u16) {
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            self.irq.unpend();
 | 
			
		||||
            self.irq.enable();
 | 
			
		||||
            IRQ_WAKER.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
            let (status, irq_status) = self.radio.irq_status().expect("error getting irq status");
 | 
			
		||||
            self.radio
 | 
			
		||||
                .clear_irq_status(irq_status)
 | 
			
		||||
                .expect("error clearing irq status");
 | 
			
		||||
 | 
			
		||||
            trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status);
 | 
			
		||||
 | 
			
		||||
            if irq_status == 0 {
 | 
			
		||||
                Poll::Pending
 | 
			
		||||
            } else {
 | 
			
		||||
                Poll::Ready((status, irq_status))
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> {
 | 
			
		||||
    trace!("Configuring STM32WL SUBGHZ radio");
 | 
			
		||||
 | 
			
		||||
    radio.set_regulator_mode(config.reg_mode)?;
 | 
			
		||||
    radio.set_standby(StandbyClk::Rc)?;
 | 
			
		||||
 | 
			
		||||
    let tcxo_mode = TcxoMode::new()
 | 
			
		||||
        .set_txco_trim(TcxoTrim::Volts1pt7)
 | 
			
		||||
        .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100)));
 | 
			
		||||
    radio.set_tcxo_mode(&tcxo_mode)?;
 | 
			
		||||
    // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control".
 | 
			
		||||
    // The STM32CUBE C driver also does this.
 | 
			
		||||
    radio.set_hse_in_trim(HseTrim::MIN)?;
 | 
			
		||||
 | 
			
		||||
    // Re-calibrate everything after setting the TXCO config.
 | 
			
		||||
    radio.calibrate(0x7F)?;
 | 
			
		||||
    radio.calibrate_image(config.calibrate_image)?;
 | 
			
		||||
 | 
			
		||||
    radio.set_pa_config(&config.pa_config)?;
 | 
			
		||||
    radio.set_tx_params(&config.tx_params)?;
 | 
			
		||||
    radio.set_pa_ocp(Ocp::Max140m)?;
 | 
			
		||||
 | 
			
		||||
    radio.set_packet_type(PacketType::LoRa)?;
 | 
			
		||||
    radio.set_lora_sync_word(LoRaSyncWord::Public)?;
 | 
			
		||||
 | 
			
		||||
    trace!("Done initializing STM32WL SUBGHZ radio");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
 | 
			
		||||
    type PhyError = RadioError;
 | 
			
		||||
 | 
			
		||||
    async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
 | 
			
		||||
        self.do_tx(config, buf).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
 | 
			
		||||
        self.do_rx(config, buf).await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<embassy_stm32::spi::Error> for RadioError {
 | 
			
		||||
    fn from(_: embassy_stm32::spi::Error) -> Self {
 | 
			
		||||
        RadioError
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, RS> Timings for SubGhzRadio<'d, RS> {
 | 
			
		||||
    fn get_rx_window_offset_ms(&self) -> i32 {
 | 
			
		||||
        -3
 | 
			
		||||
    }
 | 
			
		||||
    fn get_rx_window_duration_ms(&self) -> u32 {
 | 
			
		||||
        1003
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait RadioSwitch {
 | 
			
		||||
    fn set_rx(&mut self);
 | 
			
		||||
    fn set_tx(&mut self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn convert_spreading_factor(sf: &SpreadingFactor) -> SF {
 | 
			
		||||
    match sf {
 | 
			
		||||
        SpreadingFactor::_7 => SF::Sf7,
 | 
			
		||||
        SpreadingFactor::_8 => SF::Sf8,
 | 
			
		||||
        SpreadingFactor::_9 => SF::Sf9,
 | 
			
		||||
        SpreadingFactor::_10 => SF::Sf10,
 | 
			
		||||
        SpreadingFactor::_11 => SF::Sf11,
 | 
			
		||||
        SpreadingFactor::_12 => SF::Sf12,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth {
 | 
			
		||||
    match bw {
 | 
			
		||||
        Bandwidth::_125KHz => LoRaBandwidth::Bw125,
 | 
			
		||||
        Bandwidth::_250KHz => LoRaBandwidth::Bw250,
 | 
			
		||||
        Bandwidth::_500KHz => LoRaBandwidth::Bw500,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,137 +0,0 @@
 | 
			
		||||
use defmt::Format;
 | 
			
		||||
use embedded_hal::digital::v2::OutputPin;
 | 
			
		||||
use embedded_hal_async::digital::Wait;
 | 
			
		||||
use embedded_hal_async::spi::*;
 | 
			
		||||
use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig};
 | 
			
		||||
use lorawan_device::async_device::Timings;
 | 
			
		||||
 | 
			
		||||
mod sx126x_lora;
 | 
			
		||||
use sx126x_lora::LoRa;
 | 
			
		||||
 | 
			
		||||
use self::sx126x_lora::mod_params::RadioError;
 | 
			
		||||
 | 
			
		||||
/// Semtech Sx126x LoRa peripheral
 | 
			
		||||
pub struct Sx126xRadio<SPI, CTRL, WAIT, BUS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = BUS> + 'static,
 | 
			
		||||
    CTRL: OutputPin + 'static,
 | 
			
		||||
    WAIT: Wait + 'static,
 | 
			
		||||
    BUS: Error + Format + 'static,
 | 
			
		||||
{
 | 
			
		||||
    pub lora: LoRa<SPI, CTRL, WAIT>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SPI, CTRL, WAIT, BUS> Sx126xRadio<SPI, CTRL, WAIT, BUS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = BUS> + 'static,
 | 
			
		||||
    CTRL: OutputPin + 'static,
 | 
			
		||||
    WAIT: Wait + 'static,
 | 
			
		||||
    BUS: Error + Format + 'static,
 | 
			
		||||
{
 | 
			
		||||
    pub async fn new(
 | 
			
		||||
        spi: SPI,
 | 
			
		||||
        cs: CTRL,
 | 
			
		||||
        reset: CTRL,
 | 
			
		||||
        antenna_rx: CTRL,
 | 
			
		||||
        antenna_tx: CTRL,
 | 
			
		||||
        dio1: WAIT,
 | 
			
		||||
        busy: WAIT,
 | 
			
		||||
        enable_public_network: bool,
 | 
			
		||||
    ) -> Result<Self, RadioError<BUS>> {
 | 
			
		||||
        let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy);
 | 
			
		||||
        lora.init().await?;
 | 
			
		||||
        lora.set_lora_modem(enable_public_network).await?;
 | 
			
		||||
        Ok(Self { lora })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SPI, CTRL, WAIT, BUS> Timings for Sx126xRadio<SPI, CTRL, WAIT, BUS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = BUS> + 'static,
 | 
			
		||||
    CTRL: OutputPin + 'static,
 | 
			
		||||
    WAIT: Wait + 'static,
 | 
			
		||||
    BUS: Error + Format + 'static,
 | 
			
		||||
{
 | 
			
		||||
    fn get_rx_window_offset_ms(&self) -> i32 {
 | 
			
		||||
        -50
 | 
			
		||||
    }
 | 
			
		||||
    fn get_rx_window_duration_ms(&self) -> u32 {
 | 
			
		||||
        1050
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SPI, CTRL, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CTRL, WAIT, BUS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = BUS> + 'static,
 | 
			
		||||
    CTRL: OutputPin + 'static,
 | 
			
		||||
    WAIT: Wait + 'static,
 | 
			
		||||
    BUS: Error + Format + 'static,
 | 
			
		||||
{
 | 
			
		||||
    type PhyError = RadioError<BUS>;
 | 
			
		||||
 | 
			
		||||
    async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result<u32, Self::PhyError> {
 | 
			
		||||
        trace!("TX START");
 | 
			
		||||
        self.lora
 | 
			
		||||
            .set_tx_config(
 | 
			
		||||
                config.pw,
 | 
			
		||||
                config.rf.spreading_factor.into(),
 | 
			
		||||
                config.rf.bandwidth.into(),
 | 
			
		||||
                config.rf.coding_rate.into(),
 | 
			
		||||
                8,
 | 
			
		||||
                false,
 | 
			
		||||
                true,
 | 
			
		||||
                false,
 | 
			
		||||
                0,
 | 
			
		||||
                false,
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.lora.set_max_payload_length(buffer.len() as u8).await?;
 | 
			
		||||
        self.lora.set_channel(config.rf.frequency).await?;
 | 
			
		||||
        self.lora.send(buffer, 0xffffff).await?;
 | 
			
		||||
        self.lora.process_irq(None, None, None).await?;
 | 
			
		||||
        trace!("TX DONE");
 | 
			
		||||
        return Ok(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn rx(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        config: RfConfig,
 | 
			
		||||
        receiving_buffer: &mut [u8],
 | 
			
		||||
    ) -> Result<(usize, RxQuality), Self::PhyError> {
 | 
			
		||||
        trace!("RX START");
 | 
			
		||||
        self.lora
 | 
			
		||||
            .set_rx_config(
 | 
			
		||||
                config.spreading_factor.into(),
 | 
			
		||||
                config.bandwidth.into(),
 | 
			
		||||
                config.coding_rate.into(),
 | 
			
		||||
                8,
 | 
			
		||||
                4,
 | 
			
		||||
                false,
 | 
			
		||||
                0u8,
 | 
			
		||||
                true,
 | 
			
		||||
                false,
 | 
			
		||||
                0,
 | 
			
		||||
                true,
 | 
			
		||||
                true,
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
 | 
			
		||||
        self.lora.set_channel(config.frequency).await?;
 | 
			
		||||
        self.lora.rx(90 * 1000).await?;
 | 
			
		||||
        let mut received_len = 0u8;
 | 
			
		||||
        self.lora
 | 
			
		||||
            .process_irq(Some(receiving_buffer), Some(&mut received_len), None)
 | 
			
		||||
            .await?;
 | 
			
		||||
        trace!("RX DONE");
 | 
			
		||||
 | 
			
		||||
        let packet_status = self.lora.get_latest_packet_status();
 | 
			
		||||
        let mut rssi = 0i16;
 | 
			
		||||
        let mut snr = 0i8;
 | 
			
		||||
        if packet_status.is_some() {
 | 
			
		||||
            rssi = packet_status.unwrap().rssi as i16;
 | 
			
		||||
            snr = packet_status.unwrap().snr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok((received_len as usize, RxQuality::new(rssi, snr)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,256 +0,0 @@
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use embedded_hal::digital::v2::OutputPin;
 | 
			
		||||
use embedded_hal_async::digital::Wait;
 | 
			
		||||
use embedded_hal_async::spi::SpiBus;
 | 
			
		||||
 | 
			
		||||
use super::mod_params::RadioError::*;
 | 
			
		||||
use super::mod_params::*;
 | 
			
		||||
use super::LoRa;
 | 
			
		||||
 | 
			
		||||
// Defines the time required for the TCXO to wakeup [ms].
 | 
			
		||||
const BRD_TCXO_WAKEUP_TIME: u32 = 10;
 | 
			
		||||
 | 
			
		||||
// Provides board-specific functionality for Semtech SX126x-based boards.
 | 
			
		||||
 | 
			
		||||
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = BUS>,
 | 
			
		||||
    CTRL: OutputPin,
 | 
			
		||||
    WAIT: Wait,
 | 
			
		||||
{
 | 
			
		||||
    // De-initialize the radio I/Os pins interface.  Useful when going into MCU low power modes.
 | 
			
		||||
    pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        Ok(()) // no operation currently
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Initialize the TCXO power pin
 | 
			
		||||
    pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let timeout = self.brd_get_board_tcxo_wakeup_time() << 6;
 | 
			
		||||
        self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout)
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Initialize RF switch control pins
 | 
			
		||||
    pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_dio2_as_rf_switch_ctrl(true).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Initialize the radio debug pins
 | 
			
		||||
    pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        Ok(()) // no operation currently
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Hardware reset of the radio
 | 
			
		||||
    pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        Timer::after(Duration::from_millis(10)).await;
 | 
			
		||||
        self.reset.set_low().map_err(|_| Reset)?;
 | 
			
		||||
        Timer::after(Duration::from_millis(20)).await;
 | 
			
		||||
        self.reset.set_high().map_err(|_| Reset)?;
 | 
			
		||||
        Timer::after(Duration::from_millis(10)).await;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wait while the busy pin is high
 | 
			
		||||
    pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.busy.wait_for_low().await.map_err(|_| Busy)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wake up the radio
 | 
			
		||||
    pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.cs.set_low().map_err(|_| CS)?;
 | 
			
		||||
        self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi.write(&[0x00]).await.map_err(SPI)?;
 | 
			
		||||
        self.cs.set_high().map_err(|_| CS)?;
 | 
			
		||||
 | 
			
		||||
        self.brd_wait_on_busy().await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Send a command that writes data to the radio
 | 
			
		||||
    pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_check_device_ready().await?;
 | 
			
		||||
 | 
			
		||||
        self.cs.set_low().map_err(|_| CS)?;
 | 
			
		||||
        self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi.write(buffer).await.map_err(SPI)?;
 | 
			
		||||
        self.cs.set_high().map_err(|_| CS)?;
 | 
			
		||||
 | 
			
		||||
        if op_code != OpCode::SetSleep {
 | 
			
		||||
            self.brd_wait_on_busy().await?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Send a command that reads data from the radio, filling the provided buffer and returning a status
 | 
			
		||||
    pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
 | 
			
		||||
        let mut status = [0u8];
 | 
			
		||||
        let mut input = [0u8];
 | 
			
		||||
 | 
			
		||||
        self.sub_check_device_ready().await?;
 | 
			
		||||
 | 
			
		||||
        self.cs.set_low().map_err(|_| CS)?;
 | 
			
		||||
        self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?;
 | 
			
		||||
        for i in 0..buffer.len() {
 | 
			
		||||
            self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
 | 
			
		||||
            buffer[i] = input[0];
 | 
			
		||||
        }
 | 
			
		||||
        self.cs.set_high().map_err(|_| CS)?;
 | 
			
		||||
 | 
			
		||||
        self.brd_wait_on_busy().await?;
 | 
			
		||||
 | 
			
		||||
        Ok(status[0])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Write one or more bytes of data to the radio memory
 | 
			
		||||
    pub(super) async fn brd_write_registers(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        start_register: Register,
 | 
			
		||||
        buffer: &[u8],
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_check_device_ready().await?;
 | 
			
		||||
 | 
			
		||||
        self.cs.set_low().map_err(|_| CS)?;
 | 
			
		||||
        self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi
 | 
			
		||||
            .write(&[
 | 
			
		||||
                ((start_register.addr() & 0xFF00) >> 8) as u8,
 | 
			
		||||
                (start_register.addr() & 0x00FF) as u8,
 | 
			
		||||
            ])
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(SPI)?;
 | 
			
		||||
        self.spi.write(buffer).await.map_err(SPI)?;
 | 
			
		||||
        self.cs.set_high().map_err(|_| CS)?;
 | 
			
		||||
 | 
			
		||||
        self.brd_wait_on_busy().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Read one or more bytes of data from the radio memory
 | 
			
		||||
    pub(super) async fn brd_read_registers(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        start_register: Register,
 | 
			
		||||
        buffer: &mut [u8],
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut input = [0u8];
 | 
			
		||||
 | 
			
		||||
        self.sub_check_device_ready().await?;
 | 
			
		||||
 | 
			
		||||
        self.cs.set_low().map_err(|_| CS)?;
 | 
			
		||||
        self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi
 | 
			
		||||
            .write(&[
 | 
			
		||||
                ((start_register.addr() & 0xFF00) >> 8) as u8,
 | 
			
		||||
                (start_register.addr() & 0x00FF) as u8,
 | 
			
		||||
                0x00u8,
 | 
			
		||||
            ])
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(SPI)?;
 | 
			
		||||
        for i in 0..buffer.len() {
 | 
			
		||||
            self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
 | 
			
		||||
            buffer[i] = input[0];
 | 
			
		||||
        }
 | 
			
		||||
        self.cs.set_high().map_err(|_| CS)?;
 | 
			
		||||
 | 
			
		||||
        self.brd_wait_on_busy().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Write data to the buffer holding the payload in the radio
 | 
			
		||||
    pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_check_device_ready().await?;
 | 
			
		||||
 | 
			
		||||
        self.cs.set_low().map_err(|_| CS)?;
 | 
			
		||||
        self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi.write(&[offset]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi.write(buffer).await.map_err(SPI)?;
 | 
			
		||||
        self.cs.set_high().map_err(|_| CS)?;
 | 
			
		||||
 | 
			
		||||
        self.brd_wait_on_busy().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Read data from the buffer holding the payload in the radio
 | 
			
		||||
    pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut input = [0u8];
 | 
			
		||||
 | 
			
		||||
        self.sub_check_device_ready().await?;
 | 
			
		||||
 | 
			
		||||
        self.cs.set_low().map_err(|_| CS)?;
 | 
			
		||||
        self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi.write(&[offset]).await.map_err(SPI)?;
 | 
			
		||||
        self.spi.write(&[0x00]).await.map_err(SPI)?;
 | 
			
		||||
        for i in 0..buffer.len() {
 | 
			
		||||
            self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
 | 
			
		||||
            buffer[i] = input[0];
 | 
			
		||||
        }
 | 
			
		||||
        self.cs.set_high().map_err(|_| CS)?;
 | 
			
		||||
 | 
			
		||||
        self.brd_wait_on_busy().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio output power
 | 
			
		||||
    pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_tx_params(power, RampTime::Ramp40Us).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the radio type
 | 
			
		||||
    pub(super) fn brd_get_radio_type(&mut self) -> RadioType {
 | 
			
		||||
        RadioType::SX1262
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Quiesce the antenna(s).
 | 
			
		||||
    pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.antenna_tx.set_low().map_err(|_| AntTx)?;
 | 
			
		||||
        self.antenna_rx.set_low().map_err(|_| AntRx)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Prepare the antenna(s) for a receive operation
 | 
			
		||||
    pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.antenna_tx.set_low().map_err(|_| AntTx)?;
 | 
			
		||||
        self.antenna_rx.set_high().map_err(|_| AntRx)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Prepare the antenna(s) for a send operation
 | 
			
		||||
    pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.antenna_rx.set_low().map_err(|_| AntRx)?;
 | 
			
		||||
        self.antenna_tx.set_high().map_err(|_| AntTx)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check if the given RF frequency is supported by the hardware
 | 
			
		||||
    pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> {
 | 
			
		||||
        Ok(true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the duration required for the TCXO to wakeup [ms].
 | 
			
		||||
    pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 {
 | 
			
		||||
        BRD_TCXO_WAKEUP_TIME
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process
 | 
			
		||||
    pub(super) async fn brd_get_dio1_pin_state(
 | 
			
		||||
        &mut self,
 | 
			
		||||
    ) -> Result<u32, RadioError<BUS>> {
 | 
			
		||||
        Ok(0)
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    // Get the current radio operatiing mode
 | 
			
		||||
    pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode {
 | 
			
		||||
        self.operating_mode
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set/Update the current radio operating mode  This function is only required to reflect the current radio operating mode when processing interrupts.
 | 
			
		||||
    pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) {
 | 
			
		||||
        self.operating_mode = mode;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,732 +0,0 @@
 | 
			
		||||
#![allow(dead_code)]
 | 
			
		||||
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use embedded_hal::digital::v2::OutputPin;
 | 
			
		||||
use embedded_hal_async::digital::Wait;
 | 
			
		||||
use embedded_hal_async::spi::SpiBus;
 | 
			
		||||
 | 
			
		||||
mod board_specific;
 | 
			
		||||
pub mod mod_params;
 | 
			
		||||
mod subroutine;
 | 
			
		||||
 | 
			
		||||
use mod_params::RadioError::*;
 | 
			
		||||
use mod_params::*;
 | 
			
		||||
 | 
			
		||||
// Syncwords for public and private networks
 | 
			
		||||
const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444;
 | 
			
		||||
const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424;
 | 
			
		||||
 | 
			
		||||
// Maximum number of registers that can be added to the retention list
 | 
			
		||||
const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4;
 | 
			
		||||
 | 
			
		||||
// Possible LoRa bandwidths
 | 
			
		||||
const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz];
 | 
			
		||||
 | 
			
		||||
// Radio complete wakeup time with margin for temperature compensation [ms]
 | 
			
		||||
const RADIO_WAKEUP_TIME: u32 = 3;
 | 
			
		||||
 | 
			
		||||
/// Provides high-level access to Semtech SX126x-based boards
 | 
			
		||||
pub struct LoRa<SPI, CTRL, WAIT> {
 | 
			
		||||
    spi: SPI,
 | 
			
		||||
    cs: CTRL,
 | 
			
		||||
    reset: CTRL,
 | 
			
		||||
    antenna_rx: CTRL,
 | 
			
		||||
    antenna_tx: CTRL,
 | 
			
		||||
    dio1: WAIT,
 | 
			
		||||
    busy: WAIT,
 | 
			
		||||
    operating_mode: RadioMode,
 | 
			
		||||
    rx_continuous: bool,
 | 
			
		||||
    max_payload_length: u8,
 | 
			
		||||
    modulation_params: Option<ModulationParams>,
 | 
			
		||||
    packet_type: PacketType,
 | 
			
		||||
    packet_params: Option<PacketParams>,
 | 
			
		||||
    packet_status: Option<PacketStatus>,
 | 
			
		||||
    image_calibrated: bool,
 | 
			
		||||
    frequency_error: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = BUS>,
 | 
			
		||||
    CTRL: OutputPin,
 | 
			
		||||
    WAIT: Wait,
 | 
			
		||||
{
 | 
			
		||||
    /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time ()
 | 
			
		||||
    pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            spi,
 | 
			
		||||
            cs,
 | 
			
		||||
            reset,
 | 
			
		||||
            antenna_rx,
 | 
			
		||||
            antenna_tx,
 | 
			
		||||
            dio1,
 | 
			
		||||
            busy,
 | 
			
		||||
            operating_mode: RadioMode::Sleep,
 | 
			
		||||
            rx_continuous: false,
 | 
			
		||||
            max_payload_length: 0xFFu8,
 | 
			
		||||
            modulation_params: None,
 | 
			
		||||
            packet_type: PacketType::LoRa,
 | 
			
		||||
            packet_params: None,
 | 
			
		||||
            packet_status: None,
 | 
			
		||||
            image_calibrated: false,
 | 
			
		||||
            frequency_error: 0u32, // where is volatile FrequencyError modified ???
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Initialize the radio
 | 
			
		||||
    pub async fn init(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_init().await?;
 | 
			
		||||
        self.sub_set_standby(StandbyMode::RC).await?;
 | 
			
		||||
        self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?;
 | 
			
		||||
        self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?;
 | 
			
		||||
        self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?;
 | 
			
		||||
        self.sub_set_dio_irq_params(
 | 
			
		||||
            IrqMask::All.value(),
 | 
			
		||||
            IrqMask::All.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
        self.add_register_to_retention_list(Register::RxGain.addr()).await?;
 | 
			
		||||
        self.add_register_to_retention_list(Register::TxModulation.addr())
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return current radio state
 | 
			
		||||
    pub fn get_status(&mut self) -> RadioState {
 | 
			
		||||
        match self.brd_get_operating_mode() {
 | 
			
		||||
            RadioMode::Transmit => RadioState::TxRunning,
 | 
			
		||||
            RadioMode::Receive => RadioState::RxRunning,
 | 
			
		||||
            RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting,
 | 
			
		||||
            _ => RadioState::Idle,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired)
 | 
			
		||||
    pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_packet_type(PacketType::LoRa).await?;
 | 
			
		||||
        if enable_public_network {
 | 
			
		||||
            self.brd_write_registers(
 | 
			
		||||
                Register::LoRaSyncword,
 | 
			
		||||
                &[
 | 
			
		||||
                    ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8,
 | 
			
		||||
                    (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8,
 | 
			
		||||
                ],
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.brd_write_registers(
 | 
			
		||||
                Register::LoRaSyncword,
 | 
			
		||||
                &[
 | 
			
		||||
                    ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8,
 | 
			
		||||
                    (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8,
 | 
			
		||||
                ],
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the channel frequency
 | 
			
		||||
    pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_rf_frequency(frequency).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Checks if the channel is free for the given time.  This is currently not implemented until a substitute
 | 
			
		||||
        for switching to the FSK modem is found.
 | 
			
		||||
 | 
			
		||||
    pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool;
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts.   Ensure set_lora_modem() is called befrorehand.
 | 
			
		||||
    /// After calling this function either set_rx_config() or set_tx_config() must be called.
 | 
			
		||||
    pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_dio_irq_params(
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        let result = self.sub_get_random().await?;
 | 
			
		||||
        Ok(result)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the reception parameters for the LoRa modem (only).  Ensure set_lora_modem() is called befrorehand.
 | 
			
		||||
    ///   spreading_factor     [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
 | 
			
		||||
    ///   bandwidth            [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
 | 
			
		||||
    ///   coding_rate          [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
 | 
			
		||||
    ///   preamble_length      length in symbols (the hardware adds 4 more symbols)
 | 
			
		||||
    ///   symb_timeout         RxSingle timeout value in symbols
 | 
			
		||||
    ///   fixed_len            fixed length packets [0: variable, 1: fixed]
 | 
			
		||||
    ///   payload_len          payload length when fixed length is used
 | 
			
		||||
    ///   crc_on               [0: OFF, 1: ON]
 | 
			
		||||
    ///   freq_hop_on          intra-packet frequency hopping [0: OFF, 1: ON]
 | 
			
		||||
    ///   hop_period           number of symbols between each hop
 | 
			
		||||
    ///   iq_inverted          invert IQ signals [0: not inverted, 1: inverted]
 | 
			
		||||
    ///   rx_continuous        reception mode [false: single mode, true: continuous mode]
 | 
			
		||||
    pub async fn set_rx_config(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        spreading_factor: SpreadingFactor,
 | 
			
		||||
        bandwidth: Bandwidth,
 | 
			
		||||
        coding_rate: CodingRate,
 | 
			
		||||
        preamble_length: u16,
 | 
			
		||||
        symb_timeout: u16,
 | 
			
		||||
        fixed_len: bool,
 | 
			
		||||
        payload_len: u8,
 | 
			
		||||
        crc_on: bool,
 | 
			
		||||
        _freq_hop_on: bool,
 | 
			
		||||
        _hop_period: u8,
 | 
			
		||||
        iq_inverted: bool,
 | 
			
		||||
        rx_continuous: bool,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut symb_timeout_final = symb_timeout;
 | 
			
		||||
 | 
			
		||||
        self.rx_continuous = rx_continuous;
 | 
			
		||||
        if self.rx_continuous {
 | 
			
		||||
            symb_timeout_final = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if fixed_len {
 | 
			
		||||
            self.max_payload_length = payload_len;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.max_payload_length = 0xFFu8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.sub_set_stop_rx_timer_on_preamble_detect(false).await?;
 | 
			
		||||
 | 
			
		||||
        let mut low_data_rate_optimize = 0x00u8;
 | 
			
		||||
        if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
 | 
			
		||||
            && (bandwidth == Bandwidth::_125KHz))
 | 
			
		||||
            || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
 | 
			
		||||
        {
 | 
			
		||||
            low_data_rate_optimize = 0x01u8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let modulation_params = ModulationParams {
 | 
			
		||||
            spreading_factor: spreading_factor,
 | 
			
		||||
            bandwidth: bandwidth,
 | 
			
		||||
            coding_rate: coding_rate,
 | 
			
		||||
            low_data_rate_optimize: low_data_rate_optimize,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let mut preamble_length_final = preamble_length;
 | 
			
		||||
        if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
 | 
			
		||||
            && (preamble_length < 12)
 | 
			
		||||
        {
 | 
			
		||||
            preamble_length_final = 12;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let packet_params = PacketParams {
 | 
			
		||||
            preamble_length: preamble_length_final,
 | 
			
		||||
            implicit_header: fixed_len,
 | 
			
		||||
            payload_length: self.max_payload_length,
 | 
			
		||||
            crc_on: crc_on,
 | 
			
		||||
            iq_inverted: iq_inverted,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.modulation_params = Some(modulation_params);
 | 
			
		||||
        self.packet_params = Some(packet_params);
 | 
			
		||||
 | 
			
		||||
        self.standby().await?;
 | 
			
		||||
        self.sub_set_modulation_params().await?;
 | 
			
		||||
        self.sub_set_packet_params().await?;
 | 
			
		||||
        self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?;
 | 
			
		||||
 | 
			
		||||
        // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
 | 
			
		||||
        let mut iq_polarity = [0x00u8];
 | 
			
		||||
        self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?;
 | 
			
		||||
        if iq_inverted {
 | 
			
		||||
            self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))])
 | 
			
		||||
                .await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)])
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the transmission parameters for the LoRa modem (only).
 | 
			
		||||
    ///   power                output power [dBm]
 | 
			
		||||
    ///   spreading_factor     [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
 | 
			
		||||
    ///   bandwidth            [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
 | 
			
		||||
    ///   coding_rate          [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
 | 
			
		||||
    ///   preamble_length      length in symbols (the hardware adds 4 more symbols)
 | 
			
		||||
    ///   fixed_len            fixed length packets [0: variable, 1: fixed]
 | 
			
		||||
    ///   crc_on               [0: OFF, 1: ON]
 | 
			
		||||
    ///   freq_hop_on          intra-packet frequency hopping [0: OFF, 1: ON]
 | 
			
		||||
    ///   hop_period           number of symbols between each hop
 | 
			
		||||
    ///   iq_inverted          invert IQ signals [0: not inverted, 1: inverted]
 | 
			
		||||
    pub async fn set_tx_config(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        power: i8,
 | 
			
		||||
        spreading_factor: SpreadingFactor,
 | 
			
		||||
        bandwidth: Bandwidth,
 | 
			
		||||
        coding_rate: CodingRate,
 | 
			
		||||
        preamble_length: u16,
 | 
			
		||||
        fixed_len: bool,
 | 
			
		||||
        crc_on: bool,
 | 
			
		||||
        _freq_hop_on: bool,
 | 
			
		||||
        _hop_period: u8,
 | 
			
		||||
        iq_inverted: bool,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut low_data_rate_optimize = 0x00u8;
 | 
			
		||||
        if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
 | 
			
		||||
            && (bandwidth == Bandwidth::_125KHz))
 | 
			
		||||
            || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
 | 
			
		||||
        {
 | 
			
		||||
            low_data_rate_optimize = 0x01u8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let modulation_params = ModulationParams {
 | 
			
		||||
            spreading_factor: spreading_factor,
 | 
			
		||||
            bandwidth: bandwidth,
 | 
			
		||||
            coding_rate: coding_rate,
 | 
			
		||||
            low_data_rate_optimize: low_data_rate_optimize,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let mut preamble_length_final = preamble_length;
 | 
			
		||||
        if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
 | 
			
		||||
            && (preamble_length < 12)
 | 
			
		||||
        {
 | 
			
		||||
            preamble_length_final = 12;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let packet_params = PacketParams {
 | 
			
		||||
            preamble_length: preamble_length_final,
 | 
			
		||||
            implicit_header: fixed_len,
 | 
			
		||||
            payload_length: self.max_payload_length,
 | 
			
		||||
            crc_on: crc_on,
 | 
			
		||||
            iq_inverted: iq_inverted,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.modulation_params = Some(modulation_params);
 | 
			
		||||
        self.packet_params = Some(packet_params);
 | 
			
		||||
 | 
			
		||||
        self.standby().await?;
 | 
			
		||||
        self.sub_set_modulation_params().await?;
 | 
			
		||||
        self.sub_set_packet_params().await?;
 | 
			
		||||
 | 
			
		||||
        // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
 | 
			
		||||
 | 
			
		||||
        let mut tx_modulation = [0x00u8];
 | 
			
		||||
        self.brd_read_registers(Register::TxModulation, &mut tx_modulation)
 | 
			
		||||
            .await?;
 | 
			
		||||
        if bandwidth == Bandwidth::_500KHz {
 | 
			
		||||
            self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))])
 | 
			
		||||
                .await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)])
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.brd_set_rf_tx_power(power).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported]
 | 
			
		||||
    pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> {
 | 
			
		||||
        Ok(self.brd_check_rf_frequency(frequency).await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called)
 | 
			
		||||
    ///   spreading_factor     [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
 | 
			
		||||
    ///   bandwidth            [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
 | 
			
		||||
    ///   coding_rate          [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
 | 
			
		||||
    ///   preamble_length      length in symbols (the hardware adds 4 more symbols)
 | 
			
		||||
    ///   fixed_len            fixed length packets [0: variable, 1: fixed]
 | 
			
		||||
    ///   payload_len          sets payload length when fixed length is used
 | 
			
		||||
    ///   crc_on               [0: OFF, 1: ON]
 | 
			
		||||
    pub fn get_time_on_air(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        spreading_factor: SpreadingFactor,
 | 
			
		||||
        bandwidth: Bandwidth,
 | 
			
		||||
        coding_rate: CodingRate,
 | 
			
		||||
        preamble_length: u16,
 | 
			
		||||
        fixed_len: bool,
 | 
			
		||||
        payload_len: u8,
 | 
			
		||||
        crc_on: bool,
 | 
			
		||||
    ) -> Result<u32, RadioError<BUS>> {
 | 
			
		||||
        let numerator = 1000
 | 
			
		||||
            * Self::get_lora_time_on_air_numerator(
 | 
			
		||||
                spreading_factor,
 | 
			
		||||
                bandwidth,
 | 
			
		||||
                coding_rate,
 | 
			
		||||
                preamble_length,
 | 
			
		||||
                fixed_len,
 | 
			
		||||
                payload_len,
 | 
			
		||||
                crc_on,
 | 
			
		||||
            );
 | 
			
		||||
        let denominator = bandwidth.value_in_hz();
 | 
			
		||||
        if denominator == 0 {
 | 
			
		||||
            Err(RadioError::InvalidBandwidth)
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok((numerator + denominator - 1) / denominator)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms]
 | 
			
		||||
    pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        if self.packet_params.is_some() {
 | 
			
		||||
            self.sub_set_dio_irq_params(
 | 
			
		||||
                IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
 | 
			
		||||
                IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
 | 
			
		||||
                IrqMask::None.value(),
 | 
			
		||||
                IrqMask::None.value(),
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
            let mut packet_params = self.packet_params.as_mut().unwrap();
 | 
			
		||||
            packet_params.payload_length = buffer.len() as u8;
 | 
			
		||||
            self.sub_set_packet_params().await?;
 | 
			
		||||
            self.sub_send_payload(buffer, timeout).await?;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(RadioError::PacketParamsMissing)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the radio in sleep mode
 | 
			
		||||
    pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_sleep(SleepParams {
 | 
			
		||||
            wakeup_rtc: false,
 | 
			
		||||
            reset: false,
 | 
			
		||||
            warm_start: true,
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
        Timer::after(Duration::from_millis(2)).await;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the radio in standby mode
 | 
			
		||||
    pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_standby(StandbyMode::RC).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]       
 | 
			
		||||
    pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_dio_irq_params(
 | 
			
		||||
            IrqMask::All.value(),
 | 
			
		||||
            IrqMask::All.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        if self.rx_continuous {
 | 
			
		||||
            self.sub_set_rx(0xFFFFFF).await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.sub_set_rx(timeout << 6).await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Start a Channel Activity Detection
 | 
			
		||||
    pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_dio_irq_params(
 | 
			
		||||
            IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
 | 
			
		||||
            IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
        self.sub_set_cad().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the radio in continuous wave transmission mode
 | 
			
		||||
    ///   frequency    channel RF frequency
 | 
			
		||||
    ///   power        output power [dBm]
 | 
			
		||||
    ///   timeout      transmission mode timeout [s]
 | 
			
		||||
    pub async fn set_tx_continuous_wave(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        frequency: u32,
 | 
			
		||||
        power: i8,
 | 
			
		||||
        _timeout: u16,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_rf_frequency(frequency).await?;
 | 
			
		||||
        self.brd_set_rf_tx_power(power).await?;
 | 
			
		||||
        self.sub_set_tx_continuous_wave().await?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Read the current RSSI value for the LoRa modem (only) [dBm]
 | 
			
		||||
    pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> {
 | 
			
		||||
        let value = self.sub_get_rssi_inst().await?;
 | 
			
		||||
        Ok(value as i16)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Write one or more radio registers with a buffer of a given size, starting at the first register address
 | 
			
		||||
    pub async fn write_registers_from_buffer(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        start_register: Register,
 | 
			
		||||
        buffer: &[u8],
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_registers(start_register, buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Read one or more radio registers into a buffer of a given size, starting at the first register address
 | 
			
		||||
    pub async fn read_registers_into_buffer(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        start_register: Register,
 | 
			
		||||
        buffer: &mut [u8],
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_read_registers(start_register, buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the maximum payload length (in bytes) for a LoRa modem (only).
 | 
			
		||||
    pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        if self.packet_params.is_some() {
 | 
			
		||||
            let packet_params = self.packet_params.as_mut().unwrap();
 | 
			
		||||
            self.max_payload_length = max;
 | 
			
		||||
            packet_params.payload_length = max;
 | 
			
		||||
            self.sub_set_packet_params().await?;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(RadioError::PacketParamsMissing)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the time required for the board plus radio to get out of sleep [ms]
 | 
			
		||||
    pub fn get_wakeup_time(&mut self) -> u32 {
 | 
			
		||||
        self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Process the radio irq
 | 
			
		||||
    pub async fn process_irq(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        receiving_buffer: Option<&mut [u8]>,
 | 
			
		||||
        received_len: Option<&mut u8>,
 | 
			
		||||
        cad_activity_detected: Option<&mut bool>,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        loop {
 | 
			
		||||
            trace!("process_irq loop entered");
 | 
			
		||||
 | 
			
		||||
            let de = self.sub_get_device_errors().await?;
 | 
			
		||||
            trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}",
 | 
			
		||||
                               de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp);
 | 
			
		||||
            let st = self.sub_get_status().await?;
 | 
			
		||||
            trace!(
 | 
			
		||||
                "radio status: cmd_status: {:x}, chip_mode: {:x}",
 | 
			
		||||
                st.cmd_status,
 | 
			
		||||
                st.chip_mode
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
 | 
			
		||||
            let operating_mode = self.brd_get_operating_mode();
 | 
			
		||||
            let irq_flags = self.sub_get_irq_status().await?;
 | 
			
		||||
            self.sub_clear_irq_status(irq_flags).await?;
 | 
			
		||||
            trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags);
 | 
			
		||||
 | 
			
		||||
            // check for errors and unexpected interrupt masks (based on operation mode)
 | 
			
		||||
            if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() {
 | 
			
		||||
                if !self.rx_continuous {
 | 
			
		||||
                    self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
                }
 | 
			
		||||
                return Err(RadioError::HeaderError);
 | 
			
		||||
            } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
 | 
			
		||||
                if operating_mode == RadioMode::Receive {
 | 
			
		||||
                    if !self.rx_continuous {
 | 
			
		||||
                        self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
                    }
 | 
			
		||||
                    return Err(RadioError::CRCErrorOnReceive);
 | 
			
		||||
                } else {
 | 
			
		||||
                    return Err(RadioError::CRCErrorUnexpected);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() {
 | 
			
		||||
                if operating_mode == RadioMode::Transmit {
 | 
			
		||||
                    self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
                    return Err(RadioError::TransmitTimeout);
 | 
			
		||||
                } else if operating_mode == RadioMode::Receive {
 | 
			
		||||
                    self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
                    return Err(RadioError::ReceiveTimeout);
 | 
			
		||||
                } else {
 | 
			
		||||
                    return Err(RadioError::TimeoutUnexpected);
 | 
			
		||||
                }
 | 
			
		||||
            } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value())
 | 
			
		||||
                && (operating_mode != RadioMode::Transmit)
 | 
			
		||||
            {
 | 
			
		||||
                return Err(RadioError::TransmitDoneUnexpected);
 | 
			
		||||
            } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value())
 | 
			
		||||
                && (operating_mode != RadioMode::Receive)
 | 
			
		||||
            {
 | 
			
		||||
                return Err(RadioError::ReceiveDoneUnexpected);
 | 
			
		||||
            } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value())
 | 
			
		||||
                || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()))
 | 
			
		||||
                && (operating_mode != RadioMode::ChannelActivityDetection)
 | 
			
		||||
            {
 | 
			
		||||
                return Err(RadioError::CADUnexpected);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
 | 
			
		||||
                trace!("HeaderValid");
 | 
			
		||||
            } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() {
 | 
			
		||||
                trace!("PreambleDetected");
 | 
			
		||||
            } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() {
 | 
			
		||||
                trace!("SyncwordValid");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // handle completions
 | 
			
		||||
            if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
 | 
			
		||||
                self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
                return Ok(());
 | 
			
		||||
            } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
 | 
			
		||||
                if !self.rx_continuous {
 | 
			
		||||
                    self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
 | 
			
		||||
                    // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3)
 | 
			
		||||
                    self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?;
 | 
			
		||||
                    let mut evt_clr = [0x00u8];
 | 
			
		||||
                    self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?;
 | 
			
		||||
                    evt_clr[0] |= 1 << 1;
 | 
			
		||||
                    self.brd_write_registers(Register::EvtClr, &evt_clr).await?;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if receiving_buffer.is_some() && received_len.is_some() {
 | 
			
		||||
                    *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?;
 | 
			
		||||
                }
 | 
			
		||||
                self.packet_status = self.sub_get_packet_status().await?.into();
 | 
			
		||||
                return Ok(());
 | 
			
		||||
            } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() {
 | 
			
		||||
                if cad_activity_detected.is_some() {
 | 
			
		||||
                    *(cad_activity_detected.unwrap()) =
 | 
			
		||||
                        (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
 | 
			
		||||
                }
 | 
			
		||||
                self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
                return Ok(());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid
 | 
			
		||||
            // are in that category), loop to wait again
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // SX126x-specific functions
 | 
			
		||||
 | 
			
		||||
    /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms]
 | 
			
		||||
    pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_dio_irq_params(
 | 
			
		||||
            IrqMask::All.value(),
 | 
			
		||||
            IrqMask::All.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
            IrqMask::None.value(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        if self.rx_continuous {
 | 
			
		||||
            self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous
 | 
			
		||||
        } else {
 | 
			
		||||
            self.sub_set_rx_boosted(timeout << 6).await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the Rx duty cycle management parameters (SX126x radios only)
 | 
			
		||||
    ///   rx_time       structure describing reception timeout value
 | 
			
		||||
    ///   sleep_time    structure describing sleep timeout value
 | 
			
		||||
    pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> {
 | 
			
		||||
        self.packet_status
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Utilities
 | 
			
		||||
 | 
			
		||||
    async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize];
 | 
			
		||||
 | 
			
		||||
        // Read the address and registers already added to the list
 | 
			
		||||
        self.brd_read_registers(Register::RetentionList, &mut buffer).await?;
 | 
			
		||||
 | 
			
		||||
        let number_of_registers = buffer[0];
 | 
			
		||||
        for i in 0..number_of_registers {
 | 
			
		||||
            if register_address
 | 
			
		||||
                == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16)
 | 
			
		||||
            {
 | 
			
		||||
                return Ok(()); // register already in list
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION {
 | 
			
		||||
            buffer[0] += 1; // increment number of registers
 | 
			
		||||
 | 
			
		||||
            buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8;
 | 
			
		||||
            buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8;
 | 
			
		||||
            self.brd_write_registers(Register::RetentionList, &buffer).await?;
 | 
			
		||||
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(RadioError::RetentionListExceeded)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_lora_time_on_air_numerator(
 | 
			
		||||
        spreading_factor: SpreadingFactor,
 | 
			
		||||
        bandwidth: Bandwidth,
 | 
			
		||||
        coding_rate: CodingRate,
 | 
			
		||||
        preamble_length: u16,
 | 
			
		||||
        fixed_len: bool,
 | 
			
		||||
        payload_len: u8,
 | 
			
		||||
        crc_on: bool,
 | 
			
		||||
    ) -> u32 {
 | 
			
		||||
        let cell_denominator;
 | 
			
		||||
        let cr_denominator = (coding_rate.value() as i32) + 4;
 | 
			
		||||
 | 
			
		||||
        // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6
 | 
			
		||||
        let mut preamble_length_final = preamble_length;
 | 
			
		||||
        if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
 | 
			
		||||
            && (preamble_length < 12)
 | 
			
		||||
        {
 | 
			
		||||
            preamble_length_final = 12;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut low_data_rate_optimize = false;
 | 
			
		||||
        if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
 | 
			
		||||
            && (bandwidth == Bandwidth::_125KHz))
 | 
			
		||||
            || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
 | 
			
		||||
        {
 | 
			
		||||
            low_data_rate_optimize = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 })
 | 
			
		||||
            - (4 * spreading_factor.value() as i32)
 | 
			
		||||
            + (if fixed_len { 0 } else { 20 });
 | 
			
		||||
 | 
			
		||||
        if spreading_factor.value() <= 6 {
 | 
			
		||||
            cell_denominator = 4 * (spreading_factor.value() as i32);
 | 
			
		||||
        } else {
 | 
			
		||||
            cell_numerator += 8;
 | 
			
		||||
            if low_data_rate_optimize {
 | 
			
		||||
                cell_denominator = 4 * ((spreading_factor.value() as i32) - 2);
 | 
			
		||||
            } else {
 | 
			
		||||
                cell_denominator = 4 * (spreading_factor.value() as i32);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if cell_numerator < 0 {
 | 
			
		||||
            cell_numerator = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator)
 | 
			
		||||
            + (preamble_length_final as i32)
 | 
			
		||||
            + 12;
 | 
			
		||||
 | 
			
		||||
        if spreading_factor.value() <= 6 {
 | 
			
		||||
            intermediate = intermediate + 2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,469 +0,0 @@
 | 
			
		||||
use core::fmt::Debug;
 | 
			
		||||
 | 
			
		||||
use lorawan_device::async_device::radio as device;
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::upper_case_acronyms)]
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum RadioError<BUS> {
 | 
			
		||||
    SPI(BUS),
 | 
			
		||||
    CS,
 | 
			
		||||
    Reset,
 | 
			
		||||
    AntRx,
 | 
			
		||||
    AntTx,
 | 
			
		||||
    Busy,
 | 
			
		||||
    DIO1,
 | 
			
		||||
    PayloadSizeMismatch(usize, usize),
 | 
			
		||||
    RetentionListExceeded,
 | 
			
		||||
    InvalidBandwidth,
 | 
			
		||||
    ModulationParamsMissing,
 | 
			
		||||
    PacketParamsMissing,
 | 
			
		||||
    HeaderError,
 | 
			
		||||
    CRCErrorUnexpected,
 | 
			
		||||
    CRCErrorOnReceive,
 | 
			
		||||
    TransmitTimeout,
 | 
			
		||||
    ReceiveTimeout,
 | 
			
		||||
    TimeoutUnexpected,
 | 
			
		||||
    TransmitDoneUnexpected,
 | 
			
		||||
    ReceiveDoneUnexpected,
 | 
			
		||||
    CADUnexpected,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct RadioSystemError {
 | 
			
		||||
    pub rc_64khz_calibration: bool,
 | 
			
		||||
    pub rc_13mhz_calibration: bool,
 | 
			
		||||
    pub pll_calibration: bool,
 | 
			
		||||
    pub adc_calibration: bool,
 | 
			
		||||
    pub image_calibration: bool,
 | 
			
		||||
    pub xosc_start: bool,
 | 
			
		||||
    pub pll_lock: bool,
 | 
			
		||||
    pub pa_ramp: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum PacketType {
 | 
			
		||||
    GFSK = 0x00,
 | 
			
		||||
    LoRa = 0x01,
 | 
			
		||||
    None = 0x0F,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PacketType {
 | 
			
		||||
    pub const fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_enum(value: u8) -> Self {
 | 
			
		||||
        if value == 0x00 {
 | 
			
		||||
            PacketType::GFSK
 | 
			
		||||
        } else if value == 0x01 {
 | 
			
		||||
            PacketType::LoRa
 | 
			
		||||
        } else {
 | 
			
		||||
            PacketType::None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct PacketStatus {
 | 
			
		||||
    pub rssi: i8,
 | 
			
		||||
    pub snr: i8,
 | 
			
		||||
    pub signal_rssi: i8,
 | 
			
		||||
    pub freq_error: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum RadioType {
 | 
			
		||||
    SX1261,
 | 
			
		||||
    SX1262,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum RadioMode {
 | 
			
		||||
    Sleep = 0x00,                    // sleep mode
 | 
			
		||||
    StandbyRC = 0x01,                // standby mode with RC oscillator
 | 
			
		||||
    StandbyXOSC = 0x02,              // standby mode with XOSC oscillator
 | 
			
		||||
    FrequencySynthesis = 0x03,       // frequency synthesis mode
 | 
			
		||||
    Transmit = 0x04,                 // transmit mode
 | 
			
		||||
    Receive = 0x05,                  // receive mode
 | 
			
		||||
    ReceiveDutyCycle = 0x06,         // receive duty cycle mode
 | 
			
		||||
    ChannelActivityDetection = 0x07, // channel activity detection mode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RadioMode {
 | 
			
		||||
    /// Returns the value of the mode.
 | 
			
		||||
    pub const fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_enum(value: u8) -> Self {
 | 
			
		||||
        if value == 0x00 {
 | 
			
		||||
            RadioMode::Sleep
 | 
			
		||||
        } else if value == 0x01 {
 | 
			
		||||
            RadioMode::StandbyRC
 | 
			
		||||
        } else if value == 0x02 {
 | 
			
		||||
            RadioMode::StandbyXOSC
 | 
			
		||||
        } else if value == 0x03 {
 | 
			
		||||
            RadioMode::FrequencySynthesis
 | 
			
		||||
        } else if value == 0x04 {
 | 
			
		||||
            RadioMode::Transmit
 | 
			
		||||
        } else if value == 0x05 {
 | 
			
		||||
            RadioMode::Receive
 | 
			
		||||
        } else if value == 0x06 {
 | 
			
		||||
            RadioMode::ReceiveDutyCycle
 | 
			
		||||
        } else if value == 0x07 {
 | 
			
		||||
            RadioMode::ChannelActivityDetection
 | 
			
		||||
        } else {
 | 
			
		||||
            RadioMode::Sleep
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum RadioState {
 | 
			
		||||
    Idle = 0x00,
 | 
			
		||||
    RxRunning = 0x01,
 | 
			
		||||
    TxRunning = 0x02,
 | 
			
		||||
    ChannelActivityDetecting = 0x03,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RadioState {
 | 
			
		||||
    /// Returns the value of the state.
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct RadioStatus {
 | 
			
		||||
    pub cmd_status: u8,
 | 
			
		||||
    pub chip_mode: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RadioStatus {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        (self.chip_mode << 4) | (self.cmd_status << 1)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum IrqMask {
 | 
			
		||||
    None = 0x0000,
 | 
			
		||||
    TxDone = 0x0001,
 | 
			
		||||
    RxDone = 0x0002,
 | 
			
		||||
    PreambleDetected = 0x0004,
 | 
			
		||||
    SyncwordValid = 0x0008,
 | 
			
		||||
    HeaderValid = 0x0010,
 | 
			
		||||
    HeaderError = 0x0020,
 | 
			
		||||
    CRCError = 0x0040,
 | 
			
		||||
    CADDone = 0x0080,
 | 
			
		||||
    CADActivityDetected = 0x0100,
 | 
			
		||||
    RxTxTimeout = 0x0200,
 | 
			
		||||
    All = 0xFFFF,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IrqMask {
 | 
			
		||||
    pub fn value(self) -> u16 {
 | 
			
		||||
        self as u16
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    PacketParams = 0x0704,          // packet configuration
 | 
			
		||||
    PayloadLength = 0x0702,         // payload size
 | 
			
		||||
    SynchTimeout = 0x0706,          // recalculated number of symbols
 | 
			
		||||
    Syncword = 0x06C0,              // Syncword values
 | 
			
		||||
    LoRaSyncword = 0x0740,          // LoRa Syncword value
 | 
			
		||||
    GeneratedRandomNumber = 0x0819, //32-bit generated random number
 | 
			
		||||
    AnaLNA = 0x08E2,                // disable the LNA
 | 
			
		||||
    AnaMixer = 0x08E5,              // disable the mixer
 | 
			
		||||
    RxGain = 0x08AC,                // RX gain (0x94: power saving, 0x96: rx boosted)
 | 
			
		||||
    XTATrim = 0x0911,               // device internal trimming capacitor
 | 
			
		||||
    OCP = 0x08E7,                   // over current protection max value
 | 
			
		||||
    RetentionList = 0x029F,         // retention list
 | 
			
		||||
    IQPolarity = 0x0736,            // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
 | 
			
		||||
    TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
 | 
			
		||||
    TxClampCfg = 0x08D8,   // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
 | 
			
		||||
    RTCCtrl = 0x0902,      // RTC control
 | 
			
		||||
    EvtClr = 0x0944,       // event clear
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub fn addr(self) -> u16 {
 | 
			
		||||
        self as u16
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum OpCode {
 | 
			
		||||
    GetStatus = 0xC0,
 | 
			
		||||
    WriteRegister = 0x0D,
 | 
			
		||||
    ReadRegister = 0x1D,
 | 
			
		||||
    WriteBuffer = 0x0E,
 | 
			
		||||
    ReadBuffer = 0x1E,
 | 
			
		||||
    SetSleep = 0x84,
 | 
			
		||||
    SetStandby = 0x80,
 | 
			
		||||
    SetFS = 0xC1,
 | 
			
		||||
    SetTx = 0x83,
 | 
			
		||||
    SetRx = 0x82,
 | 
			
		||||
    SetRxDutyCycle = 0x94,
 | 
			
		||||
    SetCAD = 0xC5,
 | 
			
		||||
    SetTxContinuousWave = 0xD1,
 | 
			
		||||
    SetTxContinuousPremable = 0xD2,
 | 
			
		||||
    SetPacketType = 0x8A,
 | 
			
		||||
    GetPacketType = 0x11,
 | 
			
		||||
    SetRFFrequency = 0x86,
 | 
			
		||||
    SetTxParams = 0x8E,
 | 
			
		||||
    SetPAConfig = 0x95,
 | 
			
		||||
    SetCADParams = 0x88,
 | 
			
		||||
    SetBufferBaseAddress = 0x8F,
 | 
			
		||||
    SetModulationParams = 0x8B,
 | 
			
		||||
    SetPacketParams = 0x8C,
 | 
			
		||||
    GetRxBufferStatus = 0x13,
 | 
			
		||||
    GetPacketStatus = 0x14,
 | 
			
		||||
    GetRSSIInst = 0x15,
 | 
			
		||||
    GetStats = 0x10,
 | 
			
		||||
    ResetStats = 0x00,
 | 
			
		||||
    CfgDIOIrq = 0x08,
 | 
			
		||||
    GetIrqStatus = 0x12,
 | 
			
		||||
    ClrIrqStatus = 0x02,
 | 
			
		||||
    Calibrate = 0x89,
 | 
			
		||||
    CalibrateImage = 0x98,
 | 
			
		||||
    SetRegulatorMode = 0x96,
 | 
			
		||||
    GetErrors = 0x17,
 | 
			
		||||
    ClrErrors = 0x07,
 | 
			
		||||
    SetTCXOMode = 0x97,
 | 
			
		||||
    SetTxFallbackMode = 0x93,
 | 
			
		||||
    SetRFSwitchMode = 0x9D,
 | 
			
		||||
    SetStopRxTimerOnPreamble = 0x9F,
 | 
			
		||||
    SetLoRaSymbTimeout = 0xA0,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl OpCode {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct SleepParams {
 | 
			
		||||
    pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC
 | 
			
		||||
    pub reset: bool,
 | 
			
		||||
    pub warm_start: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SleepParams {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum StandbyMode {
 | 
			
		||||
    RC = 0x00,
 | 
			
		||||
    XOSC = 0x01,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StandbyMode {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum RegulatorMode {
 | 
			
		||||
    UseLDO = 0x00,
 | 
			
		||||
    UseDCDC = 0x01,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RegulatorMode {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct CalibrationParams {
 | 
			
		||||
    pub rc64k_enable: bool,     // calibrate RC64K clock
 | 
			
		||||
    pub rc13m_enable: bool,     // calibrate RC13M clock
 | 
			
		||||
    pub pll_enable: bool,       // calibrate PLL
 | 
			
		||||
    pub adc_pulse_enable: bool, // calibrate ADC Pulse
 | 
			
		||||
    pub adc_bulkn_enable: bool, // calibrate ADC bulkN
 | 
			
		||||
    pub adc_bulkp_enable: bool, // calibrate ADC bulkP
 | 
			
		||||
    pub img_enable: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CalibrationParams {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        ((self.img_enable as u8) << 6)
 | 
			
		||||
            | ((self.adc_bulkp_enable as u8) << 5)
 | 
			
		||||
            | ((self.adc_bulkn_enable as u8) << 4)
 | 
			
		||||
            | ((self.adc_pulse_enable as u8) << 3)
 | 
			
		||||
            | ((self.pll_enable as u8) << 2)
 | 
			
		||||
            | ((self.rc13m_enable as u8) << 1)
 | 
			
		||||
            | ((self.rc64k_enable as u8) << 0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum TcxoCtrlVoltage {
 | 
			
		||||
    Ctrl1V6 = 0x00,
 | 
			
		||||
    Ctrl1V7 = 0x01,
 | 
			
		||||
    Ctrl1V8 = 0x02,
 | 
			
		||||
    Ctrl2V2 = 0x03,
 | 
			
		||||
    Ctrl2V4 = 0x04,
 | 
			
		||||
    Ctrl2V7 = 0x05,
 | 
			
		||||
    Ctrl3V0 = 0x06,
 | 
			
		||||
    Ctrl3V3 = 0x07,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TcxoCtrlVoltage {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum RampTime {
 | 
			
		||||
    Ramp10Us = 0x00,
 | 
			
		||||
    Ramp20Us = 0x01,
 | 
			
		||||
    Ramp40Us = 0x02,
 | 
			
		||||
    Ramp80Us = 0x03,
 | 
			
		||||
    Ramp200Us = 0x04,
 | 
			
		||||
    Ramp800Us = 0x05,
 | 
			
		||||
    Ramp1700Us = 0x06,
 | 
			
		||||
    Ramp3400Us = 0x07,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RampTime {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum SpreadingFactor {
 | 
			
		||||
    _5 = 0x05,
 | 
			
		||||
    _6 = 0x06,
 | 
			
		||||
    _7 = 0x07,
 | 
			
		||||
    _8 = 0x08,
 | 
			
		||||
    _9 = 0x09,
 | 
			
		||||
    _10 = 0x0A,
 | 
			
		||||
    _11 = 0x0B,
 | 
			
		||||
    _12 = 0x0C,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SpreadingFactor {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<device::SpreadingFactor> for SpreadingFactor {
 | 
			
		||||
    fn from(sf: device::SpreadingFactor) -> Self {
 | 
			
		||||
        match sf {
 | 
			
		||||
            device::SpreadingFactor::_7 => SpreadingFactor::_7,
 | 
			
		||||
            device::SpreadingFactor::_8 => SpreadingFactor::_8,
 | 
			
		||||
            device::SpreadingFactor::_9 => SpreadingFactor::_9,
 | 
			
		||||
            device::SpreadingFactor::_10 => SpreadingFactor::_10,
 | 
			
		||||
            device::SpreadingFactor::_11 => SpreadingFactor::_11,
 | 
			
		||||
            device::SpreadingFactor::_12 => SpreadingFactor::_12,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum Bandwidth {
 | 
			
		||||
    _500KHz = 0x06,
 | 
			
		||||
    _250KHz = 0x05,
 | 
			
		||||
    _125KHz = 0x04,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Bandwidth {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn value_in_hz(self) -> u32 {
 | 
			
		||||
        match self {
 | 
			
		||||
            Bandwidth::_125KHz => 125000u32,
 | 
			
		||||
            Bandwidth::_250KHz => 250000u32,
 | 
			
		||||
            Bandwidth::_500KHz => 500000u32,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<device::Bandwidth> for Bandwidth {
 | 
			
		||||
    fn from(bw: device::Bandwidth) -> Self {
 | 
			
		||||
        match bw {
 | 
			
		||||
            device::Bandwidth::_500KHz => Bandwidth::_500KHz,
 | 
			
		||||
            device::Bandwidth::_250KHz => Bandwidth::_250KHz,
 | 
			
		||||
            device::Bandwidth::_125KHz => Bandwidth::_125KHz,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum CodingRate {
 | 
			
		||||
    _4_5 = 0x01,
 | 
			
		||||
    _4_6 = 0x02,
 | 
			
		||||
    _4_7 = 0x03,
 | 
			
		||||
    _4_8 = 0x04,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CodingRate {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<device::CodingRate> for CodingRate {
 | 
			
		||||
    fn from(cr: device::CodingRate) -> Self {
 | 
			
		||||
        match cr {
 | 
			
		||||
            device::CodingRate::_4_5 => CodingRate::_4_5,
 | 
			
		||||
            device::CodingRate::_4_6 => CodingRate::_4_6,
 | 
			
		||||
            device::CodingRate::_4_7 => CodingRate::_4_7,
 | 
			
		||||
            device::CodingRate::_4_8 => CodingRate::_4_8,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct ModulationParams {
 | 
			
		||||
    pub spreading_factor: SpreadingFactor,
 | 
			
		||||
    pub bandwidth: Bandwidth,
 | 
			
		||||
    pub coding_rate: CodingRate,
 | 
			
		||||
    pub low_data_rate_optimize: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct PacketParams {
 | 
			
		||||
    pub preamble_length: u16,  // number of LoRa symbols in the preamble
 | 
			
		||||
    pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length)
 | 
			
		||||
    pub payload_length: u8,
 | 
			
		||||
    pub crc_on: bool,
 | 
			
		||||
    pub iq_inverted: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum CADSymbols {
 | 
			
		||||
    _1 = 0x00,
 | 
			
		||||
    _2 = 0x01,
 | 
			
		||||
    _4 = 0x02,
 | 
			
		||||
    _8 = 0x03,
 | 
			
		||||
    _16 = 0x04,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CADSymbols {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum CADExitMode {
 | 
			
		||||
    CADOnly = 0x00,
 | 
			
		||||
    CADRx = 0x01,
 | 
			
		||||
    CADLBT = 0x10,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CADExitMode {
 | 
			
		||||
    pub fn value(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,674 +0,0 @@
 | 
			
		||||
use embedded_hal::digital::v2::OutputPin;
 | 
			
		||||
use embedded_hal_async::digital::Wait;
 | 
			
		||||
use embedded_hal_async::spi::SpiBus;
 | 
			
		||||
 | 
			
		||||
use super::mod_params::*;
 | 
			
		||||
use super::LoRa;
 | 
			
		||||
 | 
			
		||||
// Internal frequency of the radio
 | 
			
		||||
const SX126X_XTAL_FREQ: u32 = 32000000;
 | 
			
		||||
 | 
			
		||||
// Scaling factor used to perform fixed-point operations
 | 
			
		||||
const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14;
 | 
			
		||||
 | 
			
		||||
// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
 | 
			
		||||
const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT);
 | 
			
		||||
 | 
			
		||||
// Maximum value for parameter symbNum
 | 
			
		||||
const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248;
 | 
			
		||||
 | 
			
		||||
// Provides board-specific functionality for Semtech SX126x-based boards
 | 
			
		||||
 | 
			
		||||
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = BUS>,
 | 
			
		||||
    CTRL: OutputPin,
 | 
			
		||||
    WAIT: Wait,
 | 
			
		||||
{
 | 
			
		||||
    // Initialize the radio driver
 | 
			
		||||
    pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_reset().await?;
 | 
			
		||||
        self.brd_wakeup().await?;
 | 
			
		||||
        self.sub_set_standby(StandbyMode::RC).await?;
 | 
			
		||||
        self.brd_io_tcxo_init().await?;
 | 
			
		||||
        self.brd_io_rf_switch_init().await?;
 | 
			
		||||
        self.image_calibrated = false;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wakeup the radio if it is in Sleep mode and check that Busy is low
 | 
			
		||||
    pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let operating_mode = self.brd_get_operating_mode();
 | 
			
		||||
        if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle {
 | 
			
		||||
            self.brd_wakeup().await?;
 | 
			
		||||
        }
 | 
			
		||||
        self.brd_wait_on_busy().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Save the payload to be sent in the radio buffer
 | 
			
		||||
    pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_buffer(0x00, payload).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Read the payload received.
 | 
			
		||||
    pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
 | 
			
		||||
        let (size, offset) = self.sub_get_rx_buffer_status().await?;
 | 
			
		||||
        if (size as usize) > buffer.len() {
 | 
			
		||||
            Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len()))
 | 
			
		||||
        } else {
 | 
			
		||||
            self.brd_read_buffer(offset, buffer).await?;
 | 
			
		||||
            Ok(size)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Send a payload
 | 
			
		||||
    pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.sub_set_payload(payload).await?;
 | 
			
		||||
        self.sub_set_tx(timeout).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get a 32-bit random value generated by the radio.  A valid packet type must have been configured before using this command.
 | 
			
		||||
    //
 | 
			
		||||
    // The radio must be in reception mode before executing this function.  This code can potentially result in interrupt generation. It is the responsibility of
 | 
			
		||||
    // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts
 | 
			
		||||
    // generated during this process will not cause undesired side-effects in the software.
 | 
			
		||||
    //
 | 
			
		||||
    // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing.
 | 
			
		||||
    pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> {
 | 
			
		||||
        let mut reg_ana_lna_buffer_original = [0x00u8];
 | 
			
		||||
        let mut reg_ana_mixer_buffer_original = [0x00u8];
 | 
			
		||||
        let mut reg_ana_lna_buffer = [0x00u8];
 | 
			
		||||
        let mut reg_ana_mixer_buffer = [0x00u8];
 | 
			
		||||
        let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8];
 | 
			
		||||
 | 
			
		||||
        self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original)
 | 
			
		||||
            .await?;
 | 
			
		||||
        reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0));
 | 
			
		||||
        self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?;
 | 
			
		||||
 | 
			
		||||
        self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original)
 | 
			
		||||
            .await?;
 | 
			
		||||
        reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7));
 | 
			
		||||
        self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer)
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        // Set radio in continuous reception
 | 
			
		||||
        self.sub_set_rx(0xFFFFFFu32).await?;
 | 
			
		||||
 | 
			
		||||
        self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer)
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        self.sub_set_standby(StandbyMode::RC).await?;
 | 
			
		||||
 | 
			
		||||
        self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original)
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original)
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        Ok(Self::convert_u8_buffer_to_u32(&number_buffer))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in sleep mode
 | 
			
		||||
    pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_ant_sleep()?;
 | 
			
		||||
 | 
			
		||||
        if !sleep_config.warm_start {
 | 
			
		||||
            self.image_calibrated = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()])
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::Sleep);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in configuration mode
 | 
			
		||||
    pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?;
 | 
			
		||||
        if mode == StandbyMode::RC {
 | 
			
		||||
            self.brd_set_operating_mode(RadioMode::StandbyRC);
 | 
			
		||||
        } else {
 | 
			
		||||
            self.brd_set_operating_mode(RadioMode::StandbyXOSC);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.brd_ant_sleep()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in FS mode
 | 
			
		||||
    pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        // antenna settings ???
 | 
			
		||||
        self.brd_write_command(OpCode::SetFS, &[]).await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::FrequencySynthesis);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in transmission mode with timeout specified
 | 
			
		||||
    pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let buffer = [
 | 
			
		||||
            Self::timeout_1(timeout),
 | 
			
		||||
            Self::timeout_2(timeout),
 | 
			
		||||
            Self::timeout_3(timeout),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        self.brd_ant_set_tx()?;
 | 
			
		||||
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::Transmit);
 | 
			
		||||
        self.brd_write_command(OpCode::SetTx, &buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in reception mode with timeout specified
 | 
			
		||||
    pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let buffer = [
 | 
			
		||||
            Self::timeout_1(timeout),
 | 
			
		||||
            Self::timeout_2(timeout),
 | 
			
		||||
            Self::timeout_3(timeout),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        self.brd_ant_set_rx()?;
 | 
			
		||||
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::Receive);
 | 
			
		||||
        self.brd_write_registers(Register::RxGain, &[0x94u8]).await?;
 | 
			
		||||
        self.brd_write_command(OpCode::SetRx, &buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in reception mode with Boosted LNA gain and timeout specified
 | 
			
		||||
    pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let buffer = [
 | 
			
		||||
            Self::timeout_1(timeout),
 | 
			
		||||
            Self::timeout_2(timeout),
 | 
			
		||||
            Self::timeout_3(timeout),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        self.brd_ant_set_rx()?;
 | 
			
		||||
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::Receive);
 | 
			
		||||
        // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity
 | 
			
		||||
        self.brd_write_registers(Register::RxGain, &[0x96u8]).await?;
 | 
			
		||||
        self.brd_write_command(OpCode::SetRx, &buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the Rx duty cycle management parameters
 | 
			
		||||
    pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let buffer = [
 | 
			
		||||
            ((rx_time >> 16) & 0xFF) as u8,
 | 
			
		||||
            ((rx_time >> 8) & 0xFF) as u8,
 | 
			
		||||
            (rx_time & 0xFF) as u8,
 | 
			
		||||
            ((sleep_time >> 16) & 0xFF) as u8,
 | 
			
		||||
            ((sleep_time >> 8) & 0xFF) as u8,
 | 
			
		||||
            (sleep_time & 0xFF) as u8,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // antenna settings ???
 | 
			
		||||
 | 
			
		||||
        self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in CAD mode
 | 
			
		||||
    pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_ant_set_rx()?;
 | 
			
		||||
 | 
			
		||||
        self.brd_write_command(OpCode::SetCAD, &[]).await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in continuous wave transmission mode
 | 
			
		||||
    pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_ant_set_tx()?;
 | 
			
		||||
 | 
			
		||||
        self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::Transmit);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio in continuous preamble transmission mode
 | 
			
		||||
    pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_ant_set_tx()?;
 | 
			
		||||
 | 
			
		||||
        self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::Transmit);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Decide which interrupt will stop the internal radio rx timer.
 | 
			
		||||
    //   false     timer stop after header/syncword detection
 | 
			
		||||
    //   true      timer stop after preamble detection
 | 
			
		||||
    pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        enable: bool,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the number of symbols the radio will wait to validate a reception
 | 
			
		||||
    pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut exp = 0u8;
 | 
			
		||||
        let mut reg;
 | 
			
		||||
        let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1;
 | 
			
		||||
        while mant > 31 {
 | 
			
		||||
            mant = (mant + 3) >> 2;
 | 
			
		||||
            exp += 1;
 | 
			
		||||
        }
 | 
			
		||||
        reg = mant << ((2 * exp) + 1);
 | 
			
		||||
 | 
			
		||||
        self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?;
 | 
			
		||||
 | 
			
		||||
        if symb_num != 0 {
 | 
			
		||||
            reg = exp + (mant << 3);
 | 
			
		||||
            self.brd_write_registers(Register::SynchTimeout, &[reg]).await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the power regulators operating mode (LDO or DC_DC).  Using only LDO implies that the Rx or Tx current is doubled
 | 
			
		||||
    pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Calibrate the given radio block
 | 
			
		||||
    pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Calibrate the image rejection based on the given frequency
 | 
			
		||||
    pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut cal_freq = [0x00u8, 0x00u8];
 | 
			
		||||
 | 
			
		||||
        if freq > 900000000 {
 | 
			
		||||
            cal_freq[0] = 0xE1;
 | 
			
		||||
            cal_freq[1] = 0xE9;
 | 
			
		||||
        } else if freq > 850000000 {
 | 
			
		||||
            cal_freq[0] = 0xD7;
 | 
			
		||||
            cal_freq[1] = 0xDB;
 | 
			
		||||
        } else if freq > 770000000 {
 | 
			
		||||
            cal_freq[0] = 0xC1;
 | 
			
		||||
            cal_freq[1] = 0xC5;
 | 
			
		||||
        } else if freq > 460000000 {
 | 
			
		||||
            cal_freq[0] = 0x75;
 | 
			
		||||
            cal_freq[1] = 0x81;
 | 
			
		||||
        } else if freq > 425000000 {
 | 
			
		||||
            cal_freq[0] = 0x6B;
 | 
			
		||||
            cal_freq[1] = 0x6F;
 | 
			
		||||
        }
 | 
			
		||||
        self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Activate the extention of the timeout when a long preamble is used
 | 
			
		||||
    pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        Ok(()) // no operation currently
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the transmission parameters
 | 
			
		||||
    //   hp_max          0 for sx1261, 7 for sx1262
 | 
			
		||||
    //   device_sel      1 for sx1261, 0 for sx1262
 | 
			
		||||
    //   pa_lut          0 for 14dBm LUT, 1 for 22dBm LUT
 | 
			
		||||
    pub(super) async fn sub_set_pa_config(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        pa_duty_cycle: u8,
 | 
			
		||||
        hp_max: u8,
 | 
			
		||||
        device_sel: u8,
 | 
			
		||||
        pa_lut: u8,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Define into which mode the chip goes after a TX / RX done
 | 
			
		||||
    pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the IRQ mask and DIO masks
 | 
			
		||||
    pub(super) async fn sub_set_dio_irq_params(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        irq_mask: u16,
 | 
			
		||||
        dio1_mask: u16,
 | 
			
		||||
        dio2_mask: u16,
 | 
			
		||||
        dio3_mask: u16,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut buffer = [0x00u8; 8];
 | 
			
		||||
 | 
			
		||||
        buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8;
 | 
			
		||||
        buffer[1] = (irq_mask & 0x00FF) as u8;
 | 
			
		||||
        buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8;
 | 
			
		||||
        buffer[3] = (dio1_mask & 0x00FF) as u8;
 | 
			
		||||
        buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8;
 | 
			
		||||
        buffer[5] = (dio2_mask & 0x00FF) as u8;
 | 
			
		||||
        buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8;
 | 
			
		||||
        buffer[7] = (dio3_mask & 0x00FF) as u8;
 | 
			
		||||
        self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Return the current IRQ status
 | 
			
		||||
    pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> {
 | 
			
		||||
        let mut irq_status = [0x00u8, 0x00u8];
 | 
			
		||||
        self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?;
 | 
			
		||||
        Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Indicate if DIO2 is used to control an RF Switch
 | 
			
		||||
    pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Indicate if the radio main clock is supplied from a TCXO
 | 
			
		||||
    //   tcxo_voltage        voltage used to control the TCXO on/off from DIO3
 | 
			
		||||
    //   timeout             duration given to the TCXO to go to 32MHz
 | 
			
		||||
    pub(super) async fn sub_set_dio3_as_tcxo_ctrl(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        tcxo_voltage: TcxoCtrlVoltage,
 | 
			
		||||
        timeout: u32,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let buffer = [
 | 
			
		||||
            tcxo_voltage.value() & 0x07,
 | 
			
		||||
            Self::timeout_1(timeout),
 | 
			
		||||
            Self::timeout_2(timeout),
 | 
			
		||||
            Self::timeout_3(timeout),
 | 
			
		||||
        ];
 | 
			
		||||
        self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the RF frequency (Hz)
 | 
			
		||||
    pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut buffer = [0x00u8; 4];
 | 
			
		||||
 | 
			
		||||
        if !self.image_calibrated {
 | 
			
		||||
            self.sub_calibrate_image(frequency).await?;
 | 
			
		||||
            self.image_calibrated = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency);
 | 
			
		||||
 | 
			
		||||
        buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8;
 | 
			
		||||
        buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8;
 | 
			
		||||
        buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8;
 | 
			
		||||
        buffer[3] = (freq_in_pll_steps & 0xFF) as u8;
 | 
			
		||||
        self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the radio for the given protocol (LoRa or GFSK).  This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters.
 | 
			
		||||
    pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.packet_type = packet_type;
 | 
			
		||||
        self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the current radio protocol (LoRa or GFSK)
 | 
			
		||||
    pub(super) fn sub_get_packet_type(&mut self) -> PacketType {
 | 
			
		||||
        self.packet_type
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the transmission parameters
 | 
			
		||||
    //   power           RF output power [-18..13] dBm
 | 
			
		||||
    //   ramp_time       transmission ramp up time
 | 
			
		||||
    pub(super) async fn sub_set_tx_params(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        mut power: i8,
 | 
			
		||||
        ramp_time: RampTime,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        if self.brd_get_radio_type() == RadioType::SX1261 {
 | 
			
		||||
            if power == 15 {
 | 
			
		||||
                self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?;
 | 
			
		||||
            } else {
 | 
			
		||||
                self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if power >= 14 {
 | 
			
		||||
                power = 14;
 | 
			
		||||
            } else if power < -17 {
 | 
			
		||||
                power = -17;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
 | 
			
		||||
            let mut tx_clamp_cfg = [0x00u8];
 | 
			
		||||
            self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?;
 | 
			
		||||
            tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1);
 | 
			
		||||
            self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?;
 | 
			
		||||
 | 
			
		||||
            self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?;
 | 
			
		||||
 | 
			
		||||
            if power > 22 {
 | 
			
		||||
                power = 22;
 | 
			
		||||
            } else if power < -9 {
 | 
			
		||||
                power = -9;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // power conversion of negative number from i8 to u8 ???
 | 
			
		||||
        self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the modulation parameters
 | 
			
		||||
    pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        if self.modulation_params.is_some() {
 | 
			
		||||
            let mut buffer = [0x00u8; 4];
 | 
			
		||||
 | 
			
		||||
            // Since this driver only supports LoRa, ensure the packet type is set accordingly
 | 
			
		||||
            self.sub_set_packet_type(PacketType::LoRa).await?;
 | 
			
		||||
 | 
			
		||||
            let modulation_params = self.modulation_params.unwrap();
 | 
			
		||||
            buffer[0] = modulation_params.spreading_factor.value();
 | 
			
		||||
            buffer[1] = modulation_params.bandwidth.value();
 | 
			
		||||
            buffer[2] = modulation_params.coding_rate.value();
 | 
			
		||||
            buffer[3] = modulation_params.low_data_rate_optimize;
 | 
			
		||||
 | 
			
		||||
            self.brd_write_command(OpCode::SetModulationParams, &buffer).await?;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(RadioError::ModulationParamsMissing)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the packet parameters
 | 
			
		||||
    pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        if self.packet_params.is_some() {
 | 
			
		||||
            let mut buffer = [0x00u8; 6];
 | 
			
		||||
 | 
			
		||||
            // Since this driver only supports LoRa, ensure the packet type is set accordingly
 | 
			
		||||
            self.sub_set_packet_type(PacketType::LoRa).await?;
 | 
			
		||||
 | 
			
		||||
            let packet_params = self.packet_params.unwrap();
 | 
			
		||||
            buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8;
 | 
			
		||||
            buffer[1] = (packet_params.preamble_length & 0xFF) as u8;
 | 
			
		||||
            buffer[2] = packet_params.implicit_header as u8;
 | 
			
		||||
            buffer[3] = packet_params.payload_length;
 | 
			
		||||
            buffer[4] = packet_params.crc_on as u8;
 | 
			
		||||
            buffer[5] = packet_params.iq_inverted as u8;
 | 
			
		||||
 | 
			
		||||
            self.brd_write_command(OpCode::SetPacketParams, &buffer).await?;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(RadioError::PacketParamsMissing)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the channel activity detection (CAD) parameters
 | 
			
		||||
    //   symbols            number of symbols to use for CAD operations
 | 
			
		||||
    //   det_peak           limit for detection of SNR peak used in the CAD
 | 
			
		||||
    //   det_min            minimum symbol recognition for CAD
 | 
			
		||||
    //   exit_mode          operation to be done at the end of CAD action
 | 
			
		||||
    //   timeout            timeout value to abort the CAD activity
 | 
			
		||||
 | 
			
		||||
    pub(super) async fn sub_set_cad_params(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        symbols: CADSymbols,
 | 
			
		||||
        det_peak: u8,
 | 
			
		||||
        det_min: u8,
 | 
			
		||||
        exit_mode: CADExitMode,
 | 
			
		||||
        timeout: u32,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut buffer = [0x00u8; 7];
 | 
			
		||||
 | 
			
		||||
        buffer[0] = symbols.value();
 | 
			
		||||
        buffer[1] = det_peak;
 | 
			
		||||
        buffer[2] = det_min;
 | 
			
		||||
        buffer[3] = exit_mode.value();
 | 
			
		||||
        buffer[4] = Self::timeout_1(timeout);
 | 
			
		||||
        buffer[5] = Self::timeout_2(timeout);
 | 
			
		||||
        buffer[6] = Self::timeout_3(timeout);
 | 
			
		||||
 | 
			
		||||
        self.brd_write_command(OpCode::SetCADParams, &buffer).await?;
 | 
			
		||||
        self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the data buffer base address for transmission and reception
 | 
			
		||||
    pub(super) async fn sub_set_buffer_base_address(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        tx_base_address: u8,
 | 
			
		||||
        rx_base_address: u8,
 | 
			
		||||
    ) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address])
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the current radio status
 | 
			
		||||
    pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> {
 | 
			
		||||
        let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?;
 | 
			
		||||
        Ok(RadioStatus {
 | 
			
		||||
            cmd_status: (status & (0x07 << 1)) >> 1,
 | 
			
		||||
            chip_mode: (status & (0x07 << 4)) >> 4,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the instantaneous RSSI value for the last packet received
 | 
			
		||||
    pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> {
 | 
			
		||||
        let mut buffer = [0x00u8];
 | 
			
		||||
        self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?;
 | 
			
		||||
        let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ???
 | 
			
		||||
        Ok(rssi)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the last received packet buffer status
 | 
			
		||||
    pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> {
 | 
			
		||||
        if self.packet_params.is_some() {
 | 
			
		||||
            let mut status = [0x00u8; 2];
 | 
			
		||||
            let mut payload_length_buffer = [0x00u8];
 | 
			
		||||
 | 
			
		||||
            self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?;
 | 
			
		||||
            if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header {
 | 
			
		||||
                self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer)
 | 
			
		||||
                    .await?;
 | 
			
		||||
            } else {
 | 
			
		||||
                payload_length_buffer[0] = status[0];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let payload_length = payload_length_buffer[0];
 | 
			
		||||
            let offset = status[1];
 | 
			
		||||
 | 
			
		||||
            Ok((payload_length, offset))
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(RadioError::PacketParamsMissing)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the last received packet payload status
 | 
			
		||||
    pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> {
 | 
			
		||||
        let mut status = [0x00u8; 3];
 | 
			
		||||
        self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?;
 | 
			
		||||
 | 
			
		||||
        // check this ???
 | 
			
		||||
        let rssi = ((-(status[0] as i32)) >> 1) as i8;
 | 
			
		||||
        let snr = ((status[1] as i8) + 2) >> 2;
 | 
			
		||||
        let signal_rssi = ((-(status[2] as i32)) >> 1) as i8;
 | 
			
		||||
        let freq_error = self.frequency_error;
 | 
			
		||||
 | 
			
		||||
        Ok(PacketStatus {
 | 
			
		||||
            rssi,
 | 
			
		||||
            snr,
 | 
			
		||||
            signal_rssi,
 | 
			
		||||
            freq_error,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the possible system errors
 | 
			
		||||
    pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> {
 | 
			
		||||
        let mut errors = [0x00u8; 2];
 | 
			
		||||
        self.brd_read_command(OpCode::GetErrors, &mut errors).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(RadioSystemError {
 | 
			
		||||
            rc_64khz_calibration: (errors[1] & (1 << 0)) != 0,
 | 
			
		||||
            rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0,
 | 
			
		||||
            pll_calibration: (errors[1] & (1 << 2)) != 0,
 | 
			
		||||
            adc_calibration: (errors[1] & (1 << 3)) != 0,
 | 
			
		||||
            image_calibration: (errors[1] & (1 << 4)) != 0,
 | 
			
		||||
            xosc_start: (errors[1] & (1 << 5)) != 0,
 | 
			
		||||
            pll_lock: (errors[1] & (1 << 6)) != 0,
 | 
			
		||||
            pa_ramp: (errors[0] & (1 << 0)) != 0,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Clear all the errors in the device
 | 
			
		||||
    pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Clear the IRQs
 | 
			
		||||
    pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> {
 | 
			
		||||
        let mut buffer = [0x00u8, 0x00u8];
 | 
			
		||||
        buffer[0] = ((irq >> 8) & 0xFF) as u8;
 | 
			
		||||
        buffer[1] = (irq & 0xFF) as u8;
 | 
			
		||||
        self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Utility functions
 | 
			
		||||
 | 
			
		||||
    fn timeout_1(timeout: u32) -> u8 {
 | 
			
		||||
        ((timeout >> 16) & 0xFF) as u8
 | 
			
		||||
    }
 | 
			
		||||
    fn timeout_2(timeout: u32) -> u8 {
 | 
			
		||||
        ((timeout >> 8) & 0xFF) as u8
 | 
			
		||||
    }
 | 
			
		||||
    fn timeout_3(timeout: u32) -> u8 {
 | 
			
		||||
        (timeout & 0xFF) as u8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check this ???
 | 
			
		||||
    fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 {
 | 
			
		||||
        let b0 = buffer[0] as u32;
 | 
			
		||||
        let b1 = buffer[1] as u32;
 | 
			
		||||
        let b2 = buffer[2] as u32;
 | 
			
		||||
        let b3 = buffer[3] as u32;
 | 
			
		||||
        (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 {
 | 
			
		||||
        // Get integer and fractional parts of the frequency computed with a PLL step scaled value
 | 
			
		||||
        let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
 | 
			
		||||
        let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
 | 
			
		||||
 | 
			
		||||
        (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT)
 | 
			
		||||
            + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,192 +0,0 @@
 | 
			
		||||
use embedded_hal::digital::v2::OutputPin;
 | 
			
		||||
use embedded_hal_async::digital::Wait;
 | 
			
		||||
use embedded_hal_async::spi::*;
 | 
			
		||||
use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
 | 
			
		||||
use lorawan_device::async_device::Timings;
 | 
			
		||||
 | 
			
		||||
mod sx127x_lora;
 | 
			
		||||
use sx127x_lora::{Error as RadioError, LoRa, RadioMode, IRQ};
 | 
			
		||||
 | 
			
		||||
/// Trait representing a radio switch for boards using the Sx127x radio. One some
 | 
			
		||||
/// boards, this will be a dummy implementation that does nothing.
 | 
			
		||||
pub trait RadioSwitch {
 | 
			
		||||
    fn set_tx(&mut self);
 | 
			
		||||
    fn set_rx(&mut self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Semtech Sx127x radio peripheral
 | 
			
		||||
pub struct Sx127xRadio<SPI, CS, RESET, E, I, RFS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = E> + 'static,
 | 
			
		||||
    E: 'static,
 | 
			
		||||
    CS: OutputPin + 'static,
 | 
			
		||||
    RESET: OutputPin + 'static,
 | 
			
		||||
    I: Wait + 'static,
 | 
			
		||||
    RFS: RadioSwitch + 'static,
 | 
			
		||||
{
 | 
			
		||||
    radio: LoRa<SPI, CS, RESET>,
 | 
			
		||||
    rfs: RFS,
 | 
			
		||||
    irq: I,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum State {
 | 
			
		||||
    Idle,
 | 
			
		||||
    Txing,
 | 
			
		||||
    Rxing,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SPI, CS, RESET, E, I, RFS> Sx127xRadio<SPI, CS, RESET, E, I, RFS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = E> + 'static,
 | 
			
		||||
    CS: OutputPin + 'static,
 | 
			
		||||
    RESET: OutputPin + 'static,
 | 
			
		||||
    I: Wait + 'static,
 | 
			
		||||
    RFS: RadioSwitch + 'static,
 | 
			
		||||
    E: 'static,
 | 
			
		||||
{
 | 
			
		||||
    pub async fn new(
 | 
			
		||||
        spi: SPI,
 | 
			
		||||
        cs: CS,
 | 
			
		||||
        reset: RESET,
 | 
			
		||||
        irq: I,
 | 
			
		||||
        rfs: RFS,
 | 
			
		||||
    ) -> Result<Self, RadioError<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let mut radio = LoRa::new(spi, cs, reset);
 | 
			
		||||
        radio.reset().await?;
 | 
			
		||||
        Ok(Self { radio, irq, rfs })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SPI, CS, RESET, E, I, RFS> Timings for Sx127xRadio<SPI, CS, RESET, E, I, RFS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = E> + 'static,
 | 
			
		||||
    CS: OutputPin + 'static,
 | 
			
		||||
    RESET: OutputPin + 'static,
 | 
			
		||||
    I: Wait + 'static,
 | 
			
		||||
    RFS: RadioSwitch + 'static,
 | 
			
		||||
{
 | 
			
		||||
    fn get_rx_window_offset_ms(&self) -> i32 {
 | 
			
		||||
        -3
 | 
			
		||||
    }
 | 
			
		||||
    fn get_rx_window_duration_ms(&self) -> u32 {
 | 
			
		||||
        1003
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SPI, CS, RESET, E, I, RFS> PhyRxTx for Sx127xRadio<SPI, CS, RESET, E, I, RFS>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = E> + 'static,
 | 
			
		||||
    CS: OutputPin + 'static,
 | 
			
		||||
    E: 'static,
 | 
			
		||||
    RESET: OutputPin + 'static,
 | 
			
		||||
    I: Wait + 'static,
 | 
			
		||||
    RFS: RadioSwitch + 'static,
 | 
			
		||||
{
 | 
			
		||||
    type PhyError = Sx127xError;
 | 
			
		||||
 | 
			
		||||
    async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
 | 
			
		||||
        trace!("TX START");
 | 
			
		||||
        self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
 | 
			
		||||
        self.rfs.set_tx();
 | 
			
		||||
        self.radio.set_tx_power(14, 0).await?;
 | 
			
		||||
        self.radio.set_frequency(config.rf.frequency).await?;
 | 
			
		||||
        // TODO: Modify radio to support other coding rates
 | 
			
		||||
        self.radio.set_coding_rate_4(5).await?;
 | 
			
		||||
        self.radio
 | 
			
		||||
            .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth))
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.radio
 | 
			
		||||
            .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor))
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        self.radio.set_preamble_length(8).await?;
 | 
			
		||||
        self.radio.set_lora_pa_ramp().await?;
 | 
			
		||||
        self.radio.set_lora_sync_word().await?;
 | 
			
		||||
        self.radio.set_invert_iq(false).await?;
 | 
			
		||||
        self.radio.set_crc(true).await?;
 | 
			
		||||
 | 
			
		||||
        self.radio.set_dio0_tx_done().await?;
 | 
			
		||||
 | 
			
		||||
        self.radio.transmit_start(buf).await?;
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            self.irq.wait_for_rising_edge().await.unwrap();
 | 
			
		||||
            self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
 | 
			
		||||
            let irq = self.radio.clear_irq().await.ok().unwrap();
 | 
			
		||||
            if (irq & IRQ::IrqTxDoneMask.addr()) != 0 {
 | 
			
		||||
                trace!("TX DONE");
 | 
			
		||||
                return Ok(0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
 | 
			
		||||
        self.rfs.set_rx();
 | 
			
		||||
        self.radio.reset_payload_length().await?;
 | 
			
		||||
        self.radio.set_frequency(config.frequency).await?;
 | 
			
		||||
        // TODO: Modify radio to support other coding rates
 | 
			
		||||
        self.radio.set_coding_rate_4(5).await?;
 | 
			
		||||
        self.radio
 | 
			
		||||
            .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth))
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.radio
 | 
			
		||||
            .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor))
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        self.radio.set_preamble_length(8).await?;
 | 
			
		||||
        self.radio.set_lora_sync_word().await?;
 | 
			
		||||
        self.radio.set_invert_iq(true).await?;
 | 
			
		||||
        self.radio.set_crc(true).await?;
 | 
			
		||||
 | 
			
		||||
        self.radio.set_dio0_rx_done().await?;
 | 
			
		||||
        self.radio.set_mode(RadioMode::RxContinuous).await?;
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            self.irq.wait_for_rising_edge().await.unwrap();
 | 
			
		||||
            self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
 | 
			
		||||
            let irq = self.radio.clear_irq().await.ok().unwrap();
 | 
			
		||||
            if (irq & IRQ::IrqRxDoneMask.addr()) != 0 {
 | 
			
		||||
                let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16;
 | 
			
		||||
                let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8;
 | 
			
		||||
                let response = if let Ok(size) = self.radio.read_packet_size().await {
 | 
			
		||||
                    self.radio.read_packet(buf).await?;
 | 
			
		||||
                    Ok((size, RxQuality::new(rssi, snr)))
 | 
			
		||||
                } else {
 | 
			
		||||
                    Ok((0, RxQuality::new(rssi, snr)))
 | 
			
		||||
                };
 | 
			
		||||
                trace!("RX DONE");
 | 
			
		||||
                return response;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct Sx127xError;
 | 
			
		||||
 | 
			
		||||
impl<A, B, C> From<sx127x_lora::Error<A, B, C>> for Sx127xError {
 | 
			
		||||
    fn from(_: sx127x_lora::Error<A, B, C>) -> Self {
 | 
			
		||||
        Sx127xError
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn spreading_factor_to_u8(sf: SpreadingFactor) -> u8 {
 | 
			
		||||
    match sf {
 | 
			
		||||
        SpreadingFactor::_7 => 7,
 | 
			
		||||
        SpreadingFactor::_8 => 8,
 | 
			
		||||
        SpreadingFactor::_9 => 9,
 | 
			
		||||
        SpreadingFactor::_10 => 10,
 | 
			
		||||
        SpreadingFactor::_11 => 11,
 | 
			
		||||
        SpreadingFactor::_12 => 12,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn bandwidth_to_i64(bw: Bandwidth) -> i64 {
 | 
			
		||||
    match bw {
 | 
			
		||||
        Bandwidth::_125KHz => 125_000,
 | 
			
		||||
        Bandwidth::_250KHz => 250_000,
 | 
			
		||||
        Bandwidth::_500KHz => 500_000,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,539 +0,0 @@
 | 
			
		||||
// Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0
 | 
			
		||||
// license
 | 
			
		||||
//
 | 
			
		||||
// Modifications made to make the driver work with the rust-lorawan link layer.
 | 
			
		||||
 | 
			
		||||
#![allow(dead_code)]
 | 
			
		||||
 | 
			
		||||
use bit_field::BitField;
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use embedded_hal::digital::v2::OutputPin;
 | 
			
		||||
use embedded_hal_async::spi::SpiBus;
 | 
			
		||||
 | 
			
		||||
mod register;
 | 
			
		||||
pub use self::register::IRQ;
 | 
			
		||||
use self::register::{PaConfig, Register};
 | 
			
		||||
 | 
			
		||||
/// Provides high-level access to Semtech SX1276/77/78/79 based boards connected to a Raspberry Pi
 | 
			
		||||
pub struct LoRa<SPI, CS, RESET> {
 | 
			
		||||
    spi: SPI,
 | 
			
		||||
    cs: CS,
 | 
			
		||||
    reset: RESET,
 | 
			
		||||
    pub explicit_header: bool,
 | 
			
		||||
    pub mode: RadioMode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::upper_case_acronyms)]
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum Error<SPI, CS, RESET> {
 | 
			
		||||
    Uninformative,
 | 
			
		||||
    VersionMismatch(u8),
 | 
			
		||||
    CS(CS),
 | 
			
		||||
    Reset(RESET),
 | 
			
		||||
    SPI(SPI),
 | 
			
		||||
    Transmitting,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
use Error::*;
 | 
			
		||||
 | 
			
		||||
use super::sx127x_lora::register::{FskDataModulationShaping, FskRampUpRamDown};
 | 
			
		||||
 | 
			
		||||
#[cfg(not(feature = "version_0x09"))]
 | 
			
		||||
const VERSION_CHECK: u8 = 0x12;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "version_0x09")]
 | 
			
		||||
const VERSION_CHECK: u8 = 0x09;
 | 
			
		||||
 | 
			
		||||
impl<SPI, CS, RESET, E> LoRa<SPI, CS, RESET>
 | 
			
		||||
where
 | 
			
		||||
    SPI: SpiBus<u8, Error = E>,
 | 
			
		||||
    CS: OutputPin,
 | 
			
		||||
    RESET: OutputPin,
 | 
			
		||||
{
 | 
			
		||||
    /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time.
 | 
			
		||||
    /// This also preforms a hardware reset of the module and then puts it in standby.
 | 
			
		||||
    pub fn new(spi: SPI, cs: CS, reset: RESET) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            spi,
 | 
			
		||||
            cs,
 | 
			
		||||
            reset,
 | 
			
		||||
            explicit_header: true,
 | 
			
		||||
            mode: RadioMode::Sleep,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn reset(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.reset.set_low().map_err(Reset)?;
 | 
			
		||||
        Timer::after(Duration::from_millis(10)).await;
 | 
			
		||||
        self.reset.set_high().map_err(Reset)?;
 | 
			
		||||
        Timer::after(Duration::from_millis(10)).await;
 | 
			
		||||
        let version = self.read_register(Register::RegVersion.addr()).await?;
 | 
			
		||||
        if version == VERSION_CHECK {
 | 
			
		||||
            self.set_mode(RadioMode::Sleep).await?;
 | 
			
		||||
            self.write_register(Register::RegFifoTxBaseAddr.addr(), 0).await?;
 | 
			
		||||
            self.write_register(Register::RegFifoRxBaseAddr.addr(), 0).await?;
 | 
			
		||||
            let lna = self.read_register(Register::RegLna.addr()).await?;
 | 
			
		||||
            self.write_register(Register::RegLna.addr(), lna | 0x03).await?;
 | 
			
		||||
            self.write_register(Register::RegModemConfig3.addr(), 0x04).await?;
 | 
			
		||||
            self.set_tcxo(true).await?;
 | 
			
		||||
            self.set_mode(RadioMode::Stdby).await?;
 | 
			
		||||
            self.cs.set_high().map_err(CS)?;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(Error::VersionMismatch(version))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_dio0_tx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.write_register(Register::RegIrqFlagsMask.addr(), 0b1111_0111)
 | 
			
		||||
            .await?;
 | 
			
		||||
        let mapping = self.read_register(Register::RegDioMapping1.addr()).await?;
 | 
			
		||||
        self.write_register(Register::RegDioMapping1.addr(), (mapping & 0x3F) | 0x40)
 | 
			
		||||
            .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_dio0_rx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.write_register(Register::RegIrqFlagsMask.addr(), 0b0001_1111)
 | 
			
		||||
            .await?;
 | 
			
		||||
        let mapping = self.read_register(Register::RegDioMapping1.addr()).await?;
 | 
			
		||||
        self.write_register(Register::RegDioMapping1.addr(), mapping & 0x3F)
 | 
			
		||||
            .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn transmit_start(&mut self, buffer: &[u8]) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        assert!(buffer.len() < 255);
 | 
			
		||||
        if self.transmitting().await? {
 | 
			
		||||
            //trace!("ALREADY TRANSMNITTING");
 | 
			
		||||
            Err(Transmitting)
 | 
			
		||||
        } else {
 | 
			
		||||
            self.set_mode(RadioMode::Stdby).await?;
 | 
			
		||||
            if self.explicit_header {
 | 
			
		||||
                self.set_explicit_header_mode().await?;
 | 
			
		||||
            } else {
 | 
			
		||||
                self.set_implicit_header_mode().await?;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self.write_register(Register::RegIrqFlags.addr(), 0).await?;
 | 
			
		||||
            self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?;
 | 
			
		||||
            self.write_register(Register::RegPayloadLength.addr(), 0).await?;
 | 
			
		||||
            for byte in buffer.iter() {
 | 
			
		||||
                self.write_register(Register::RegFifo.addr(), *byte).await?;
 | 
			
		||||
            }
 | 
			
		||||
            self.write_register(Register::RegPayloadLength.addr(), buffer.len() as u8)
 | 
			
		||||
                .await?;
 | 
			
		||||
            self.set_mode(RadioMode::Tx).await?;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn packet_ready(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        Ok(self.read_register(Register::RegIrqFlags.addr()).await?.get_bit(6))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn irq_flags_mask(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        Ok(self.read_register(Register::RegIrqFlagsMask.addr()).await? as u8)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn irq_flags(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        Ok(self.read_register(Register::RegIrqFlags.addr()).await? as u8)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn read_packet_size(&mut self) -> Result<usize, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let size = self.read_register(Register::RegRxNbBytes.addr()).await?;
 | 
			
		||||
        Ok(size as usize)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the contents of the fifo as a fixed 255 u8 array. This should only be called is there is a
 | 
			
		||||
    /// new packet ready to be read.
 | 
			
		||||
    pub async fn read_packet(&mut self, buffer: &mut [u8]) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.clear_irq().await?;
 | 
			
		||||
        let size = self.read_register(Register::RegRxNbBytes.addr()).await?;
 | 
			
		||||
        assert!(size as usize <= buffer.len());
 | 
			
		||||
        let fifo_addr = self.read_register(Register::RegFifoRxCurrentAddr.addr()).await?;
 | 
			
		||||
        self.write_register(Register::RegFifoAddrPtr.addr(), fifo_addr).await?;
 | 
			
		||||
        for i in 0..size {
 | 
			
		||||
            let byte = self.read_register(Register::RegFifo.addr()).await?;
 | 
			
		||||
            buffer[i as usize] = byte;
 | 
			
		||||
        }
 | 
			
		||||
        self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the radio is currently transmitting a packet.
 | 
			
		||||
    pub async fn transmitting(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        if (self.read_register(Register::RegOpMode.addr()).await?) & RadioMode::Tx.addr() == RadioMode::Tx.addr() {
 | 
			
		||||
            Ok(true)
 | 
			
		||||
        } else {
 | 
			
		||||
            if (self.read_register(Register::RegIrqFlags.addr()).await? & IRQ::IrqTxDoneMask.addr()) == 1 {
 | 
			
		||||
                self.write_register(Register::RegIrqFlags.addr(), IRQ::IrqTxDoneMask.addr())
 | 
			
		||||
                    .await?;
 | 
			
		||||
            }
 | 
			
		||||
            Ok(false)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Clears the radio's IRQ registers.
 | 
			
		||||
    pub async fn clear_irq(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let irq_flags = self.read_register(Register::RegIrqFlags.addr()).await?;
 | 
			
		||||
        self.write_register(Register::RegIrqFlags.addr(), 0xFF).await?;
 | 
			
		||||
        Ok(irq_flags)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the transmit power and pin. Levels can range from 0-14 when the output
 | 
			
		||||
    /// pin = 0(RFO), and form 0-20 when output pin = 1(PaBoost). Power is in dB.
 | 
			
		||||
    /// Default value is `17`.
 | 
			
		||||
    pub async fn set_tx_power(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        mut level: i32,
 | 
			
		||||
        output_pin: u8,
 | 
			
		||||
    ) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        if PaConfig::PaOutputRfoPin.addr() == output_pin {
 | 
			
		||||
            // RFO
 | 
			
		||||
            if level < 0 {
 | 
			
		||||
                level = 0;
 | 
			
		||||
            } else if level > 14 {
 | 
			
		||||
                level = 14;
 | 
			
		||||
            }
 | 
			
		||||
            self.write_register(Register::RegPaConfig.addr(), (0x70 | level) as u8)
 | 
			
		||||
                .await
 | 
			
		||||
        } else {
 | 
			
		||||
            // PA BOOST
 | 
			
		||||
            if level > 17 {
 | 
			
		||||
                if level > 20 {
 | 
			
		||||
                    level = 20;
 | 
			
		||||
                }
 | 
			
		||||
                // subtract 3 from level, so 18 - 20 maps to 15 - 17
 | 
			
		||||
                level -= 3;
 | 
			
		||||
 | 
			
		||||
                // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
 | 
			
		||||
                self.write_register(Register::RegPaDac.addr(), 0x87).await?;
 | 
			
		||||
                self.set_ocp(140).await?;
 | 
			
		||||
            } else {
 | 
			
		||||
                if level < 2 {
 | 
			
		||||
                    level = 2;
 | 
			
		||||
                }
 | 
			
		||||
                //Default value PA_HF/LF or +17dBm
 | 
			
		||||
                self.write_register(Register::RegPaDac.addr(), 0x84).await?;
 | 
			
		||||
                self.set_ocp(100).await?;
 | 
			
		||||
            }
 | 
			
		||||
            level -= 2;
 | 
			
		||||
            self.write_register(Register::RegPaConfig.addr(), PaConfig::PaBoost.addr() | level as u8)
 | 
			
		||||
                .await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_modem_stat(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        Ok(self.read_register(Register::RegModemStat.addr()).await? as u8)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the over current protection on the radio(mA).
 | 
			
		||||
    pub async fn set_ocp(&mut self, ma: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let mut ocp_trim: u8 = 27;
 | 
			
		||||
 | 
			
		||||
        if ma <= 120 {
 | 
			
		||||
            ocp_trim = (ma - 45) / 5;
 | 
			
		||||
        } else if ma <= 240 {
 | 
			
		||||
            ocp_trim = (ma + 30) / 10;
 | 
			
		||||
        }
 | 
			
		||||
        self.write_register(Register::RegOcp.addr(), 0x20 | (0x1F & ocp_trim))
 | 
			
		||||
            .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the state of the radio. Default mode after initiation is `Standby`.
 | 
			
		||||
    pub async fn set_mode(&mut self, mode: RadioMode) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        if self.explicit_header {
 | 
			
		||||
            self.set_explicit_header_mode().await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.set_implicit_header_mode().await?;
 | 
			
		||||
        }
 | 
			
		||||
        self.write_register(
 | 
			
		||||
            Register::RegOpMode.addr(),
 | 
			
		||||
            RadioMode::LongRangeMode.addr() | mode.addr(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        self.mode = mode;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn reset_payload_length(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.write_register(Register::RegPayloadLength.addr(), 0xFF).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the frequency of the radio. Values are in megahertz.
 | 
			
		||||
    /// I.E. 915 MHz must be used for North America. Check regulation for your area.
 | 
			
		||||
    pub async fn set_frequency(&mut self, freq: u32) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        const FREQ_STEP: f64 = 61.03515625;
 | 
			
		||||
        // calculate register values
 | 
			
		||||
        let frf = (freq as f64 / FREQ_STEP) as u32;
 | 
			
		||||
        // write registers
 | 
			
		||||
        self.write_register(Register::RegFrfMsb.addr(), ((frf & 0x00FF_0000) >> 16) as u8)
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.write_register(Register::RegFrfMid.addr(), ((frf & 0x0000_FF00) >> 8) as u8)
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.write_register(Register::RegFrfLsb.addr(), (frf & 0x0000_00FF) as u8)
 | 
			
		||||
            .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the radio to use an explicit header. Default state is `ON`.
 | 
			
		||||
    async fn set_explicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
 | 
			
		||||
        self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0xfe)
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.explicit_header = true;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the radio to use an implicit header. Default state is `OFF`.
 | 
			
		||||
    async fn set_implicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
 | 
			
		||||
        self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0x01)
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.explicit_header = false;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the spreading factor of the radio. Supported values are between 6 and 12.
 | 
			
		||||
    /// If a spreading factor of 6 is set, implicit header mode must be used to transmit
 | 
			
		||||
    /// and receive packets. Default value is `7`.
 | 
			
		||||
    pub async fn set_spreading_factor(&mut self, mut sf: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        if sf < 6 {
 | 
			
		||||
            sf = 6;
 | 
			
		||||
        } else if sf > 12 {
 | 
			
		||||
            sf = 12;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if sf == 6 {
 | 
			
		||||
            self.write_register(Register::RegDetectionOptimize.addr(), 0xc5).await?;
 | 
			
		||||
            self.write_register(Register::RegDetectionThreshold.addr(), 0x0c)
 | 
			
		||||
                .await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.write_register(Register::RegDetectionOptimize.addr(), 0xc3).await?;
 | 
			
		||||
            self.write_register(Register::RegDetectionThreshold.addr(), 0x0a)
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
        let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?;
 | 
			
		||||
        self.write_register(
 | 
			
		||||
            Register::RegModemConfig2.addr(),
 | 
			
		||||
            (modem_config_2 & 0x0f) | ((sf << 4) & 0xf0),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
        self.set_ldo_flag().await?;
 | 
			
		||||
 | 
			
		||||
        self.write_register(Register::RegSymbTimeoutLsb.addr(), 0x05).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_tcxo(&mut self, external: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        if external {
 | 
			
		||||
            self.write_register(Register::RegTcxo.addr(), 0x10).await
 | 
			
		||||
        } else {
 | 
			
		||||
            self.write_register(Register::RegTcxo.addr(), 0x00).await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the signal bandwidth of the radio. Supported values are: `7800 Hz`, `10400 Hz`,
 | 
			
		||||
    /// `15600 Hz`, `20800 Hz`, `31250 Hz`,`41700 Hz` ,`62500 Hz`,`125000 Hz` and `250000 Hz`
 | 
			
		||||
    /// Default value is `125000 Hz`
 | 
			
		||||
    pub async fn set_signal_bandwidth(&mut self, sbw: i64) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let bw: i64 = match sbw {
 | 
			
		||||
            7_800 => 0,
 | 
			
		||||
            10_400 => 1,
 | 
			
		||||
            15_600 => 2,
 | 
			
		||||
            20_800 => 3,
 | 
			
		||||
            31_250 => 4,
 | 
			
		||||
            41_700 => 5,
 | 
			
		||||
            62_500 => 6,
 | 
			
		||||
            125_000 => 7,
 | 
			
		||||
            250_000 => 8,
 | 
			
		||||
            _ => 9,
 | 
			
		||||
        };
 | 
			
		||||
        let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
 | 
			
		||||
        self.write_register(
 | 
			
		||||
            Register::RegModemConfig1.addr(),
 | 
			
		||||
            (modem_config_1 & 0x0f) | ((bw << 4) as u8),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
        self.set_ldo_flag().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the coding rate of the radio with the numerator fixed at 4. Supported values
 | 
			
		||||
    /// are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`.
 | 
			
		||||
    /// Default value is `5`.
 | 
			
		||||
    pub async fn set_coding_rate_4(&mut self, mut denominator: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        if denominator < 5 {
 | 
			
		||||
            denominator = 5;
 | 
			
		||||
        } else if denominator > 8 {
 | 
			
		||||
            denominator = 8;
 | 
			
		||||
        }
 | 
			
		||||
        let cr = denominator - 4;
 | 
			
		||||
        let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
 | 
			
		||||
        self.write_register(Register::RegModemConfig1.addr(), (modem_config_1 & 0xf1) | (cr << 1))
 | 
			
		||||
            .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the preamble length of the radio. Values are between 6 and 65535.
 | 
			
		||||
    /// Default value is `8`.
 | 
			
		||||
    pub async fn set_preamble_length(&mut self, length: i64) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.write_register(Register::RegPreambleMsb.addr(), (length >> 8) as u8)
 | 
			
		||||
            .await?;
 | 
			
		||||
        self.write_register(Register::RegPreambleLsb.addr(), length as u8).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Enables are disables the radio's CRC check. Default value is `false`.
 | 
			
		||||
    pub async fn set_crc(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?;
 | 
			
		||||
        if value {
 | 
			
		||||
            self.write_register(Register::RegModemConfig2.addr(), modem_config_2 | 0x04)
 | 
			
		||||
                .await
 | 
			
		||||
        } else {
 | 
			
		||||
            self.write_register(Register::RegModemConfig2.addr(), modem_config_2 & 0xfb)
 | 
			
		||||
                .await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Inverts the radio's IQ signals. Default value is `false`.
 | 
			
		||||
    pub async fn set_invert_iq(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        if value {
 | 
			
		||||
            self.write_register(Register::RegInvertiq.addr(), 0x66).await?;
 | 
			
		||||
            self.write_register(Register::RegInvertiq2.addr(), 0x19).await
 | 
			
		||||
        } else {
 | 
			
		||||
            self.write_register(Register::RegInvertiq.addr(), 0x27).await?;
 | 
			
		||||
            self.write_register(Register::RegInvertiq2.addr(), 0x1d).await
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the spreading factor of the radio.
 | 
			
		||||
    pub async fn get_spreading_factor(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        Ok(self.read_register(Register::RegModemConfig2.addr()).await? >> 4)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the signal bandwidth of the radio.
 | 
			
		||||
    pub async fn get_signal_bandwidth(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let bw = self.read_register(Register::RegModemConfig1.addr()).await? >> 4;
 | 
			
		||||
        let bw = match bw {
 | 
			
		||||
            0 => 7_800,
 | 
			
		||||
            1 => 10_400,
 | 
			
		||||
            2 => 15_600,
 | 
			
		||||
            3 => 20_800,
 | 
			
		||||
            4 => 31_250,
 | 
			
		||||
            5 => 41_700,
 | 
			
		||||
            6 => 62_500,
 | 
			
		||||
            7 => 125_000,
 | 
			
		||||
            8 => 250_000,
 | 
			
		||||
            9 => 500_000,
 | 
			
		||||
            _ => -1,
 | 
			
		||||
        };
 | 
			
		||||
        Ok(bw)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the RSSI of the last received packet.
 | 
			
		||||
    pub async fn get_packet_rssi(&mut self) -> Result<i32, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        Ok(i32::from(self.read_register(Register::RegPktRssiValue.addr()).await?) - 157)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the signal to noise radio of the the last received packet.
 | 
			
		||||
    pub async fn get_packet_snr(&mut self) -> Result<f64, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        Ok(f64::from(self.read_register(Register::RegPktSnrValue.addr()).await?))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the frequency error of the last received packet in Hz.
 | 
			
		||||
    pub async fn get_packet_frequency_error(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let mut freq_error: i32;
 | 
			
		||||
        freq_error = i32::from(self.read_register(Register::RegFreqErrorMsb.addr()).await? & 0x7);
 | 
			
		||||
        freq_error <<= 8i64;
 | 
			
		||||
        freq_error += i32::from(self.read_register(Register::RegFreqErrorMid.addr()).await?);
 | 
			
		||||
        freq_error <<= 8i64;
 | 
			
		||||
        freq_error += i32::from(self.read_register(Register::RegFreqErrorLsb.addr()).await?);
 | 
			
		||||
 | 
			
		||||
        let f_xtal = 32_000_000; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
 | 
			
		||||
        let f_error = ((f64::from(freq_error) * (1i64 << 24) as f64) / f64::from(f_xtal))
 | 
			
		||||
            * (self.get_signal_bandwidth().await? as f64 / 500_000.0f64); // p. 37
 | 
			
		||||
        Ok(f_error as i64)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn set_ldo_flag(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let sw = self.get_signal_bandwidth().await?;
 | 
			
		||||
        // Section 4.1.1.5
 | 
			
		||||
        let symbol_duration = 1000 / (sw / ((1_i64) << self.get_spreading_factor().await?));
 | 
			
		||||
 | 
			
		||||
        // Section 4.1.1.6
 | 
			
		||||
        let ldo_on = symbol_duration > 16;
 | 
			
		||||
 | 
			
		||||
        let mut config_3 = self.read_register(Register::RegModemConfig3.addr()).await?;
 | 
			
		||||
        config_3.set_bit(3, ldo_on);
 | 
			
		||||
        //config_3.set_bit(2, true);
 | 
			
		||||
        self.write_register(Register::RegModemConfig3.addr(), config_3).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn read_register(&mut self, reg: u8) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let mut buffer = [reg & 0x7f, 0];
 | 
			
		||||
        self.cs.set_low().map_err(CS)?;
 | 
			
		||||
 | 
			
		||||
        let _ = self.spi.transfer(&mut buffer, &[reg & 0x7f, 0]).await.map_err(SPI)?;
 | 
			
		||||
 | 
			
		||||
        self.cs.set_high().map_err(CS)?;
 | 
			
		||||
        Ok(buffer[1])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn write_register(&mut self, reg: u8, byte: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.cs.set_low().map_err(CS)?;
 | 
			
		||||
        let buffer = [reg | 0x80, byte];
 | 
			
		||||
        self.spi.write(&buffer).await.map_err(SPI)?;
 | 
			
		||||
        self.cs.set_high().map_err(CS)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn put_in_fsk_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        // Put in FSK mode
 | 
			
		||||
        let mut op_mode = 0;
 | 
			
		||||
        op_mode
 | 
			
		||||
            .set_bit(7, false) // FSK mode
 | 
			
		||||
            .set_bits(5..6, 0x00) // FSK modulation
 | 
			
		||||
            .set_bit(3, false) //Low freq registers
 | 
			
		||||
            .set_bits(0..2, 0b011); // Mode
 | 
			
		||||
 | 
			
		||||
        self.write_register(Register::RegOpMode as u8, op_mode).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_fsk_pa_ramp(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        modulation_shaping: FskDataModulationShaping,
 | 
			
		||||
        ramp: FskRampUpRamDown,
 | 
			
		||||
    ) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        let mut pa_ramp = 0;
 | 
			
		||||
        pa_ramp
 | 
			
		||||
            .set_bits(5..6, modulation_shaping as u8)
 | 
			
		||||
            .set_bits(0..3, ramp as u8);
 | 
			
		||||
 | 
			
		||||
        self.write_register(Register::RegPaRamp as u8, pa_ramp).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_lora_pa_ramp(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.write_register(Register::RegPaRamp as u8, 0b1000).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn set_lora_sync_word(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
 | 
			
		||||
        self.write_register(Register::RegSyncWord as u8, 0x34).await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// Modes of the radio and their corresponding register values.
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum RadioMode {
 | 
			
		||||
    LongRangeMode = 0x80,
 | 
			
		||||
    Sleep = 0x00,
 | 
			
		||||
    Stdby = 0x01,
 | 
			
		||||
    Tx = 0x03,
 | 
			
		||||
    RxContinuous = 0x05,
 | 
			
		||||
    RxSingle = 0x06,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RadioMode {
 | 
			
		||||
    /// Returns the address of the mode.
 | 
			
		||||
    pub fn addr(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
// Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0
 | 
			
		||||
// license
 | 
			
		||||
//
 | 
			
		||||
// Modifications made to make the driver work with the rust-lorawan link layer.
 | 
			
		||||
#![allow(dead_code, clippy::enum_variant_names)]
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Register {
 | 
			
		||||
    RegFifo = 0x00,
 | 
			
		||||
    RegOpMode = 0x01,
 | 
			
		||||
    RegFrfMsb = 0x06,
 | 
			
		||||
    RegFrfMid = 0x07,
 | 
			
		||||
    RegFrfLsb = 0x08,
 | 
			
		||||
    RegPaConfig = 0x09,
 | 
			
		||||
    RegPaRamp = 0x0a,
 | 
			
		||||
    RegOcp = 0x0b,
 | 
			
		||||
    RegLna = 0x0c,
 | 
			
		||||
    RegFifoAddrPtr = 0x0d,
 | 
			
		||||
    RegFifoTxBaseAddr = 0x0e,
 | 
			
		||||
    RegFifoRxBaseAddr = 0x0f,
 | 
			
		||||
    RegFifoRxCurrentAddr = 0x10,
 | 
			
		||||
    RegIrqFlagsMask = 0x11,
 | 
			
		||||
    RegIrqFlags = 0x12,
 | 
			
		||||
    RegRxNbBytes = 0x13,
 | 
			
		||||
    RegPktSnrValue = 0x19,
 | 
			
		||||
    RegModemStat = 0x18,
 | 
			
		||||
    RegPktRssiValue = 0x1a,
 | 
			
		||||
    RegModemConfig1 = 0x1d,
 | 
			
		||||
    RegModemConfig2 = 0x1e,
 | 
			
		||||
    RegSymbTimeoutLsb = 0x1f,
 | 
			
		||||
    RegPreambleMsb = 0x20,
 | 
			
		||||
    RegPreambleLsb = 0x21,
 | 
			
		||||
    RegPayloadLength = 0x22,
 | 
			
		||||
    RegMaxPayloadLength = 0x23,
 | 
			
		||||
    RegModemConfig3 = 0x26,
 | 
			
		||||
    RegFreqErrorMsb = 0x28,
 | 
			
		||||
    RegFreqErrorMid = 0x29,
 | 
			
		||||
    RegFreqErrorLsb = 0x2a,
 | 
			
		||||
    RegRssiWideband = 0x2c,
 | 
			
		||||
    RegDetectionOptimize = 0x31,
 | 
			
		||||
    RegInvertiq = 0x33,
 | 
			
		||||
    RegDetectionThreshold = 0x37,
 | 
			
		||||
    RegSyncWord = 0x39,
 | 
			
		||||
    RegInvertiq2 = 0x3b,
 | 
			
		||||
    RegDioMapping1 = 0x40,
 | 
			
		||||
    RegVersion = 0x42,
 | 
			
		||||
    RegTcxo = 0x4b,
 | 
			
		||||
    RegPaDac = 0x4d,
 | 
			
		||||
}
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum PaConfig {
 | 
			
		||||
    PaBoost = 0x80,
 | 
			
		||||
    PaOutputRfoPin = 0,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum IRQ {
 | 
			
		||||
    IrqTxDoneMask = 0x08,
 | 
			
		||||
    IrqPayloadCrcErrorMask = 0x20,
 | 
			
		||||
    IrqRxDoneMask = 0x40,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Register {
 | 
			
		||||
    pub fn addr(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PaConfig {
 | 
			
		||||
    pub fn addr(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IRQ {
 | 
			
		||||
    pub fn addr(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum FskDataModulationShaping {
 | 
			
		||||
    None = 1,
 | 
			
		||||
    GaussianBt1d0 = 2,
 | 
			
		||||
    GaussianBt0d5 = 10,
 | 
			
		||||
    GaussianBt0d3 = 11,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum FskRampUpRamDown {
 | 
			
		||||
    _3d4ms = 0b000,
 | 
			
		||||
    _2ms = 0b0001,
 | 
			
		||||
    _1ms = 0b0010,
 | 
			
		||||
    _500us = 0b0011,
 | 
			
		||||
    _250us = 0b0100,
 | 
			
		||||
    _125us = 0b0101,
 | 
			
		||||
    _100us = 0b0110,
 | 
			
		||||
    _62us = 0b0111,
 | 
			
		||||
    _50us = 0b1000,
 | 
			
		||||
    _40us = 0b1001,
 | 
			
		||||
    _31us = 0b1010,
 | 
			
		||||
    _25us = 0b1011,
 | 
			
		||||
    _20us = 0b1100,
 | 
			
		||||
    _15us = 0b1101,
 | 
			
		||||
    _12us = 0b1110,
 | 
			
		||||
    _10us = 0b1111,
 | 
			
		||||
}
 | 
			
		||||
@@ -57,9 +57,6 @@ pub mod rtc;
 | 
			
		||||
pub mod sdmmc;
 | 
			
		||||
#[cfg(spi)]
 | 
			
		||||
pub mod spi;
 | 
			
		||||
#[cfg(stm32wl)]
 | 
			
		||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
 | 
			
		||||
pub mod subghz;
 | 
			
		||||
#[cfg(usart)]
 | 
			
		||||
pub mod usart;
 | 
			
		||||
#[cfg(all(usb, feature = "time"))]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,160 +0,0 @@
 | 
			
		||||
/// Bit synchronization.
 | 
			
		||||
///
 | 
			
		||||
/// This must be cleared to `0x00` (the reset value) when using packet types
 | 
			
		||||
/// other than LoRa.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_bit_sync`](crate::subghz::SubGhz::set_bit_sync).
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct BitSync {
 | 
			
		||||
    val: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BitSync {
 | 
			
		||||
    /// Bit synchronization register reset value.
 | 
			
		||||
    pub const RESET: BitSync = BitSync { val: 0x00 };
 | 
			
		||||
 | 
			
		||||
    /// Create a new [`BitSync`] structure from a raw value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Reserved bits will be masked.
 | 
			
		||||
    pub const fn from_raw(raw: u8) -> Self {
 | 
			
		||||
        Self { val: raw & 0x70 }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the raw value of the [`BitSync`] register.
 | 
			
		||||
    pub const fn as_bits(&self) -> u8 {
 | 
			
		||||
        self.val
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// LoRa simple bit synchronization enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Enable simple bit synchronization.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BitSync;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const BIT_SYNC: BitSync = BitSync::RESET.set_simple_bit_sync_en(true);
 | 
			
		||||
    /// # assert_eq!(u8::from(BIT_SYNC), 0x40u8);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_simple_bit_sync_en returns a modified BitSync"]
 | 
			
		||||
    pub const fn set_simple_bit_sync_en(mut self, en: bool) -> BitSync {
 | 
			
		||||
        if en {
 | 
			
		||||
            self.val |= 1 << 6;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !(1 << 6);
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if simple bit synchronization is enabled.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BitSync;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let bs: BitSync = BitSync::RESET;
 | 
			
		||||
    /// assert_eq!(bs.simple_bit_sync_en(), false);
 | 
			
		||||
    /// let bs: BitSync = bs.set_simple_bit_sync_en(true);
 | 
			
		||||
    /// assert_eq!(bs.simple_bit_sync_en(), true);
 | 
			
		||||
    /// let bs: BitSync = bs.set_simple_bit_sync_en(false);
 | 
			
		||||
    /// assert_eq!(bs.simple_bit_sync_en(), false);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn simple_bit_sync_en(&self) -> bool {
 | 
			
		||||
        self.val & (1 << 6) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// LoRa RX data inversion.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Invert receive data.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BitSync;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const BIT_SYNC: BitSync = BitSync::RESET.set_rx_data_inv(true);
 | 
			
		||||
    /// # assert_eq!(u8::from(BIT_SYNC), 0x20u8);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_rx_data_inv returns a modified BitSync"]
 | 
			
		||||
    pub const fn set_rx_data_inv(mut self, inv: bool) -> BitSync {
 | 
			
		||||
        if inv {
 | 
			
		||||
            self.val |= 1 << 5;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !(1 << 5);
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if LoRa RX data is inverted.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BitSync;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let bs: BitSync = BitSync::RESET;
 | 
			
		||||
    /// assert_eq!(bs.rx_data_inv(), false);
 | 
			
		||||
    /// let bs: BitSync = bs.set_rx_data_inv(true);
 | 
			
		||||
    /// assert_eq!(bs.rx_data_inv(), true);
 | 
			
		||||
    /// let bs: BitSync = bs.set_rx_data_inv(false);
 | 
			
		||||
    /// assert_eq!(bs.rx_data_inv(), false);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn rx_data_inv(&self) -> bool {
 | 
			
		||||
        self.val & (1 << 5) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// LoRa normal bit synchronization enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Enable normal bit synchronization.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BitSync;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const BIT_SYNC: BitSync = BitSync::RESET.set_norm_bit_sync_en(true);
 | 
			
		||||
    /// # assert_eq!(u8::from(BIT_SYNC), 0x10u8);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_norm_bit_sync_en returns a modified BitSync"]
 | 
			
		||||
    pub const fn set_norm_bit_sync_en(mut self, en: bool) -> BitSync {
 | 
			
		||||
        if en {
 | 
			
		||||
            self.val |= 1 << 4;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !(1 << 4);
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if normal bit synchronization is enabled.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BitSync;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let bs: BitSync = BitSync::RESET;
 | 
			
		||||
    /// assert_eq!(bs.norm_bit_sync_en(), false);
 | 
			
		||||
    /// let bs: BitSync = bs.set_norm_bit_sync_en(true);
 | 
			
		||||
    /// assert_eq!(bs.norm_bit_sync_en(), true);
 | 
			
		||||
    /// let bs: BitSync = bs.set_norm_bit_sync_en(false);
 | 
			
		||||
    /// assert_eq!(bs.norm_bit_sync_en(), false);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn norm_bit_sync_en(&self) -> bool {
 | 
			
		||||
        self.val & (1 << 4) != 0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<BitSync> for u8 {
 | 
			
		||||
    fn from(bs: BitSync) -> Self {
 | 
			
		||||
        bs.val
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for BitSync {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::RESET
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,230 +0,0 @@
 | 
			
		||||
use super::Timeout;
 | 
			
		||||
 | 
			
		||||
/// Number of symbols used for channel activity detection scans.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`CadParams::set_num_symbol`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum NbCadSymbol {
 | 
			
		||||
    /// 1 symbol.
 | 
			
		||||
    S1 = 0x0,
 | 
			
		||||
    /// 2 symbols.
 | 
			
		||||
    S2 = 0x1,
 | 
			
		||||
    /// 4 symbols.
 | 
			
		||||
    S4 = 0x2,
 | 
			
		||||
    /// 8 symbols.
 | 
			
		||||
    S8 = 0x3,
 | 
			
		||||
    /// 16 symbols.
 | 
			
		||||
    S16 = 0x4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Mode to enter after a channel activity detection scan is finished.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`CadParams::set_exit_mode`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum ExitMode {
 | 
			
		||||
    /// Standby with RC 13 MHz mode entry after CAD.
 | 
			
		||||
    Standby = 0,
 | 
			
		||||
    /// Standby with RC 13 MHz mode after CAD if no LoRa symbol is detected
 | 
			
		||||
    /// during the CAD scan.
 | 
			
		||||
    /// If a LoRa symbol is detected, the sub-GHz radio stays in RX mode
 | 
			
		||||
    /// until a packet is received or until the CAD timeout is reached.
 | 
			
		||||
    StandbyLoRa = 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Channel activity detection (CAD) parameters.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_cad_params`].
 | 
			
		||||
///
 | 
			
		||||
/// # Recommended CAD settings
 | 
			
		||||
///
 | 
			
		||||
/// This is taken directly from the datasheet.
 | 
			
		||||
///
 | 
			
		||||
/// "The correct values selected in the table below must be carefully tested to
 | 
			
		||||
/// ensure a good detection at sensitivity level and to limit the number of
 | 
			
		||||
/// false detections"
 | 
			
		||||
///
 | 
			
		||||
/// | SF (Spreading Factor) | [`set_det_peak`] | [`set_det_min`] |
 | 
			
		||||
/// |-----------------------|------------------|-----------------|
 | 
			
		||||
/// |                     5 |             0x18 |            0x10 |
 | 
			
		||||
/// |                     6 |             0x19 |            0x10 |
 | 
			
		||||
/// |                     7 |             0x20 |            0x10 |
 | 
			
		||||
/// |                     8 |             0x21 |            0x10 |
 | 
			
		||||
/// |                     9 |             0x22 |            0x10 |
 | 
			
		||||
/// |                    10 |             0x23 |            0x10 |
 | 
			
		||||
/// |                    11 |             0x24 |            0x10 |
 | 
			
		||||
/// |                    12 |             0x25 |            0x10 |
 | 
			
		||||
///
 | 
			
		||||
/// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params
 | 
			
		||||
/// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak
 | 
			
		||||
/// [`set_det_min`]: crate::subghz::CadParams::set_det_min
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct CadParams {
 | 
			
		||||
    buf: [u8; 8],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CadParams {
 | 
			
		||||
    /// Create a new `CadParams`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CadParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const CAD_PARAMS: CadParams = CadParams::new();
 | 
			
		||||
    /// assert_eq!(CAD_PARAMS, CadParams::default());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> CadParams {
 | 
			
		||||
        CadParams {
 | 
			
		||||
            buf: [super::OpCode::SetCadParams as u8, 0, 0, 0, 0, 0, 0, 0],
 | 
			
		||||
        }
 | 
			
		||||
        .set_num_symbol(NbCadSymbol::S1)
 | 
			
		||||
        .set_det_peak(0x18)
 | 
			
		||||
        .set_det_min(0x10)
 | 
			
		||||
        .set_exit_mode(ExitMode::Standby)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Number of symbols used for a CAD scan.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Set the number of symbols to 4.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CadParams, NbCadSymbol};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const CAD_PARAMS: CadParams = CadParams::new().set_num_symbol(NbCadSymbol::S4);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[1], 0x2);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_num_symbol returns a modified CadParams"]
 | 
			
		||||
    pub const fn set_num_symbol(mut self, nb: NbCadSymbol) -> CadParams {
 | 
			
		||||
        self.buf[1] = nb as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Used with [`set_det_min`] to correlate the LoRa symbol.
 | 
			
		||||
    ///
 | 
			
		||||
    /// See the table in [`CadParams`] docs for recommended values.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Setting the recommended value for a spreading factor of 7.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CadParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x20).set_det_min(0x10);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x20);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// [`set_det_min`]: crate::subghz::CadParams::set_det_min
 | 
			
		||||
    #[must_use = "set_det_peak returns a modified CadParams"]
 | 
			
		||||
    pub const fn set_det_peak(mut self, peak: u8) -> CadParams {
 | 
			
		||||
        self.buf[2] = peak;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Used with [`set_det_peak`] to correlate the LoRa symbol.
 | 
			
		||||
    ///
 | 
			
		||||
    /// See the table in [`CadParams`] docs for recommended values.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Setting the recommended value for a spreading factor of 6.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CadParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x18).set_det_min(0x10);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x18);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak
 | 
			
		||||
    #[must_use = "set_det_min returns a modified CadParams"]
 | 
			
		||||
    pub const fn set_det_min(mut self, min: u8) -> CadParams {
 | 
			
		||||
        self.buf[3] = min;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Mode to enter after a channel activity detection scan is finished.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CadParams, ExitMode};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const CAD_PARAMS: CadParams = CadParams::new().set_exit_mode(ExitMode::Standby);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x00);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.set_exit_mode(ExitMode::StandbyLoRa).as_slice()[4], 0x01);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_exit_mode returns a modified CadParams"]
 | 
			
		||||
    pub const fn set_exit_mode(mut self, mode: ExitMode) -> CadParams {
 | 
			
		||||
        self.buf[4] = mode as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the timeout.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is only used with [`ExitMode::StandbyLoRa`].
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, Timeout};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456);
 | 
			
		||||
    /// const CAD_PARAMS: CadParams = CadParams::new()
 | 
			
		||||
    ///     .set_exit_mode(ExitMode::StandbyLoRa)
 | 
			
		||||
    ///     .set_timeout(TIMEOUT);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x01);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[5], 0x12);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[6], 0x34);
 | 
			
		||||
    /// # assert_eq!(CAD_PARAMS.as_slice()[7], 0x56);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_timeout returns a modified CadParams"]
 | 
			
		||||
    pub const fn set_timeout(mut self, to: Timeout) -> CadParams {
 | 
			
		||||
        let to_bytes: [u8; 3] = to.as_bytes();
 | 
			
		||||
        self.buf[5] = to_bytes[0];
 | 
			
		||||
        self.buf[6] = to_bytes[1];
 | 
			
		||||
        self.buf[7] = to_bytes[2];
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, NbCadSymbol, Timeout};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456);
 | 
			
		||||
    /// const CAD_PARAMS: CadParams = CadParams::new()
 | 
			
		||||
    ///     .set_num_symbol(NbCadSymbol::S4)
 | 
			
		||||
    ///     .set_det_peak(0x18)
 | 
			
		||||
    ///     .set_det_min(0x10)
 | 
			
		||||
    ///     .set_exit_mode(ExitMode::StandbyLoRa)
 | 
			
		||||
    ///     .set_timeout(TIMEOUT);
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     CAD_PARAMS.as_slice(),
 | 
			
		||||
    ///     &[0x88, 0x02, 0x18, 0x10, 0x01, 0x12, 0x34, 0x56]
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for CadParams {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,122 +0,0 @@
 | 
			
		||||
/// Image calibration.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`calibrate_image`].
 | 
			
		||||
///
 | 
			
		||||
/// [`calibrate_image`]: crate::subghz::SubGhz::calibrate_image
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct CalibrateImage(pub(crate) u8, pub(crate) u8);
 | 
			
		||||
 | 
			
		||||
impl CalibrateImage {
 | 
			
		||||
    /// Image calibration for the 430 - 440 MHz ISM band.
 | 
			
		||||
    pub const ISM_430_440: CalibrateImage = CalibrateImage(0x6B, 0x6F);
 | 
			
		||||
 | 
			
		||||
    /// Image calibration for the 470 - 510 MHz ISM band.
 | 
			
		||||
    pub const ISM_470_510: CalibrateImage = CalibrateImage(0x75, 0x81);
 | 
			
		||||
 | 
			
		||||
    /// Image calibration for the 779 - 787 MHz ISM band.
 | 
			
		||||
    pub const ISM_779_787: CalibrateImage = CalibrateImage(0xC1, 0xC5);
 | 
			
		||||
 | 
			
		||||
    /// Image calibration for the 863 - 870 MHz ISM band.
 | 
			
		||||
    pub const ISM_863_870: CalibrateImage = CalibrateImage(0xD7, 0xDB);
 | 
			
		||||
 | 
			
		||||
    /// Image calibration for the 902 - 928 MHz ISM band.
 | 
			
		||||
    pub const ISM_902_928: CalibrateImage = CalibrateImage(0xE1, 0xE9);
 | 
			
		||||
 | 
			
		||||
    /// Create a new `CalibrateImage` structure from raw values.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CalibrateImage;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const CAL: CalibrateImage = CalibrateImage::new(0xE1, 0xE9);
 | 
			
		||||
    /// assert_eq!(CAL, CalibrateImage::ISM_902_928);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new(f1: u8, f2: u8) -> CalibrateImage {
 | 
			
		||||
        CalibrateImage(f1, f2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new `CalibrateImage` structure from two frequencies.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Arguments
 | 
			
		||||
    ///
 | 
			
		||||
    /// The units for `freq1` and `freq2` are in MHz.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Panics if `freq1` is less than `freq2`.
 | 
			
		||||
    /// * Panics if `freq1` or `freq2` is not a multiple of 4MHz.
 | 
			
		||||
    /// * Panics if `freq1` or `freq2` is greater than `1020`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Create an image calibration for the 430 - 440 MHz ISM band.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CalibrateImage;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let cal: CalibrateImage = CalibrateImage::from_freq(428, 444);
 | 
			
		||||
    /// assert_eq!(cal, CalibrateImage::ISM_430_440);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn from_freq(freq1: u16, freq2: u16) -> CalibrateImage {
 | 
			
		||||
        assert!(freq2 >= freq1);
 | 
			
		||||
        assert_eq!(freq1 % 4, 0);
 | 
			
		||||
        assert_eq!(freq2 % 4, 0);
 | 
			
		||||
        assert!(freq1 <= 1020);
 | 
			
		||||
        assert!(freq2 <= 1020);
 | 
			
		||||
        CalibrateImage((freq1 / 4) as u8, (freq2 / 4) as u8)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for CalibrateImage {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        CalibrateImage::new(0xE1, 0xE9)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Block calibration.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`calibrate`].
 | 
			
		||||
///
 | 
			
		||||
/// [`calibrate`]: crate::subghz::SubGhz::calibrate
 | 
			
		||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum Calibrate {
 | 
			
		||||
    /// Image calibration
 | 
			
		||||
    Image = 1 << 6,
 | 
			
		||||
    ///  RF-ADC bulk P calibration
 | 
			
		||||
    AdcBulkP = 1 << 5,
 | 
			
		||||
    /// RF-ADC bulk N calibration
 | 
			
		||||
    AdcBulkN = 1 << 4,
 | 
			
		||||
    /// RF-ADC pulse calibration
 | 
			
		||||
    AdcPulse = 1 << 3,
 | 
			
		||||
    /// RF-PLL calibration
 | 
			
		||||
    Pll = 1 << 2,
 | 
			
		||||
    /// Sub-GHz radio RC 13 MHz calibration
 | 
			
		||||
    Rc13M = 1 << 1,
 | 
			
		||||
    /// Sub-GHz radio RC 64 kHz calibration
 | 
			
		||||
    Rc64K = 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Calibrate {
 | 
			
		||||
    /// Get the bitmask for the block calibration.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Calibrate;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Calibrate::Image.mask(), 0b0100_0000);
 | 
			
		||||
    /// assert_eq!(Calibrate::AdcBulkP.mask(), 0b0010_0000);
 | 
			
		||||
    /// assert_eq!(Calibrate::AdcBulkN.mask(), 0b0001_0000);
 | 
			
		||||
    /// assert_eq!(Calibrate::AdcPulse.mask(), 0b0000_1000);
 | 
			
		||||
    /// assert_eq!(Calibrate::Pll.mask(), 0b0000_0100);
 | 
			
		||||
    /// assert_eq!(Calibrate::Rc13M.mask(), 0b0000_0010);
 | 
			
		||||
    /// assert_eq!(Calibrate::Rc64K.mask(), 0b0000_0001);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn mask(self) -> u8 {
 | 
			
		||||
        self as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
/// Fallback mode after successful packet transmission or packet reception.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_tx_rx_fallback_mode`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_tx_rx_fallback_mode`]: crate::subghz::SubGhz::set_tx_rx_fallback_mode.
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum FallbackMode {
 | 
			
		||||
    /// Standby mode entry.
 | 
			
		||||
    Standby = 0x20,
 | 
			
		||||
    /// Standby with HSE32 enabled.
 | 
			
		||||
    StandbyHse = 0x30,
 | 
			
		||||
    /// Frequency synthesizer entry.
 | 
			
		||||
    Fs = 0x40,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<FallbackMode> for u8 {
 | 
			
		||||
    fn from(fm: FallbackMode) -> Self {
 | 
			
		||||
        fm as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for FallbackMode {
 | 
			
		||||
    /// Default fallback mode after power-on reset.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::FallbackMode;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(FallbackMode::default(), FallbackMode::Standby);
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        FallbackMode::Standby
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
use super::ValueError;
 | 
			
		||||
 | 
			
		||||
/// HSE32 load capacitor trimming.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_hse_in_trim`] and [`set_hse_out_trim`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_hse_in_trim`]: crate::subghz::SubGhz::set_hse_in_trim
 | 
			
		||||
/// [`set_hse_out_trim`]: crate::subghz::SubGhz::set_hse_out_trim
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct HseTrim {
 | 
			
		||||
    val: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl HseTrim {
 | 
			
		||||
    /// Maximum capacitor value, ~33.4 pF
 | 
			
		||||
    pub const MAX: HseTrim = HseTrim::from_raw(0x2F);
 | 
			
		||||
 | 
			
		||||
    /// Minimum capacitor value, ~11.3 pF
 | 
			
		||||
    pub const MIN: HseTrim = HseTrim::from_raw(0x00);
 | 
			
		||||
 | 
			
		||||
    /// Power-on-reset capacitor value, ~20.3 pF
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::HseTrim;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(HseTrim::POR, HseTrim::default());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const POR: HseTrim = HseTrim::from_raw(0x12);
 | 
			
		||||
 | 
			
		||||
    /// Create a new [`HseTrim`] structure from a raw value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Values greater than the maximum of `0x2F` will be set to the maximum.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::HseTrim;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(HseTrim::from_raw(0xFF), HseTrim::MAX);
 | 
			
		||||
    /// assert_eq!(HseTrim::from_raw(0x2F), HseTrim::MAX);
 | 
			
		||||
    /// assert_eq!(HseTrim::from_raw(0x00), HseTrim::MIN);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw(raw: u8) -> HseTrim {
 | 
			
		||||
        if raw > 0x2F {
 | 
			
		||||
            HseTrim { val: 0x2F }
 | 
			
		||||
        } else {
 | 
			
		||||
            HseTrim { val: raw }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a HSE trim value from farads.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Values greater than the maximum of 33.4 pF will be set to the maximum.
 | 
			
		||||
    /// Values less than the minimum of 11.3 pF will be set to the minimum.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::HseTrim;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert!(HseTrim::from_farads(1.0).is_err());
 | 
			
		||||
    /// assert!(HseTrim::from_farads(1e-12).is_err());
 | 
			
		||||
    /// assert_eq!(HseTrim::from_farads(20.2e-12), Ok(HseTrim::default()));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn from_farads(farads: f32) -> Result<HseTrim, ValueError<f32>> {
 | 
			
		||||
        const MAX: f32 = 33.4E-12;
 | 
			
		||||
        const MIN: f32 = 11.3E-12;
 | 
			
		||||
        if farads > MAX {
 | 
			
		||||
            Err(ValueError::too_high(farads, MAX))
 | 
			
		||||
        } else if farads < MIN {
 | 
			
		||||
            Err(ValueError::too_low(farads, MIN))
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok(HseTrim::from_raw(((farads - 11.3e-12) / 0.47e-12) as u8))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the capacitance as farads.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::HseTrim;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!((HseTrim::MAX.as_farads() * 10e11) as u8, 33);
 | 
			
		||||
    /// assert_eq!((HseTrim::MIN.as_farads() * 10e11) as u8, 11);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn as_farads(&self) -> f32 {
 | 
			
		||||
        (self.val as f32) * 0.47E-12 + 11.3E-12
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<HseTrim> for u8 {
 | 
			
		||||
    fn from(ht: HseTrim) -> Self {
 | 
			
		||||
        ht.val
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for HseTrim {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::POR
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,292 +0,0 @@
 | 
			
		||||
/// Interrupt lines.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`CfgIrq::irq_enable`] and [`CfgIrq::irq_disable`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum IrqLine {
 | 
			
		||||
    /// Global interrupt.
 | 
			
		||||
    Global,
 | 
			
		||||
    /// Interrupt line 1.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will output to the [`RfIrq0`](crate::gpio::RfIrq0) pin.
 | 
			
		||||
    Line1,
 | 
			
		||||
    /// Interrupt line 2.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will output to the [`RfIrq1`](crate::gpio::RfIrq1) pin.
 | 
			
		||||
    Line2,
 | 
			
		||||
    /// Interrupt line 3.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will output to the [`RfIrq2`](crate::gpio::RfIrq2) pin.
 | 
			
		||||
    Line3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IrqLine {
 | 
			
		||||
    pub(super) const fn offset(&self) -> usize {
 | 
			
		||||
        match self {
 | 
			
		||||
            IrqLine::Global => 1,
 | 
			
		||||
            IrqLine::Line1 => 3,
 | 
			
		||||
            IrqLine::Line2 => 5,
 | 
			
		||||
            IrqLine::Line3 => 7,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// IRQ bit mapping
 | 
			
		||||
///
 | 
			
		||||
/// See table 37 "IRQ bit mapping and definition" in the reference manual for
 | 
			
		||||
/// more information.
 | 
			
		||||
#[repr(u16)]
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum Irq {
 | 
			
		||||
    /// Packet transmission finished.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa and GFSK
 | 
			
		||||
    /// * Operation: TX
 | 
			
		||||
    TxDone = (1 << 0),
 | 
			
		||||
    /// Packet reception finished.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa and GFSK
 | 
			
		||||
    /// * Operation: RX
 | 
			
		||||
    RxDone = (1 << 1),
 | 
			
		||||
    /// Preamble detected.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa and GFSK
 | 
			
		||||
    /// * Operation: RX
 | 
			
		||||
    PreambleDetected = (1 << 2),
 | 
			
		||||
    /// Synchronization word valid.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: GFSK
 | 
			
		||||
    /// * Operation: RX
 | 
			
		||||
    SyncDetected = (1 << 3),
 | 
			
		||||
    /// Header valid.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa
 | 
			
		||||
    /// * Operation: RX
 | 
			
		||||
    HeaderValid = (1 << 4),
 | 
			
		||||
    /// Header CRC error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa
 | 
			
		||||
    /// * Operation: RX
 | 
			
		||||
    HeaderErr = (1 << 5),
 | 
			
		||||
    /// Dual meaning error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// For GFSK RX this indicates a preamble, syncword, address, CRC, or length
 | 
			
		||||
    /// error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// For LoRa RX this indicates a CRC error.
 | 
			
		||||
    Err = (1 << 6),
 | 
			
		||||
    /// Channel activity detection finished.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa
 | 
			
		||||
    /// * Operation: CAD
 | 
			
		||||
    CadDone = (1 << 7),
 | 
			
		||||
    /// Channel activity detected.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa
 | 
			
		||||
    /// * Operation: CAD
 | 
			
		||||
    CadDetected = (1 << 8),
 | 
			
		||||
    /// RX or TX timeout.
 | 
			
		||||
    ///
 | 
			
		||||
    /// * Packet type: LoRa and GFSK
 | 
			
		||||
    /// * Operation: RX and TX
 | 
			
		||||
    Timeout = (1 << 9),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Irq {
 | 
			
		||||
    /// Get the bitmask for an IRQ.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Irq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Irq::TxDone.mask(), 0x0001);
 | 
			
		||||
    /// assert_eq!(Irq::Timeout.mask(), 0x0200);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn mask(self) -> u16 {
 | 
			
		||||
        self as u16
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Argument for [`set_irq_cfg`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_irq_cfg`]: crate::subghz::SubGhz::set_irq_cfg
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct CfgIrq {
 | 
			
		||||
    buf: [u8; 9],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CfgIrq {
 | 
			
		||||
    /// Create a new `CfgIrq`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The default value has all interrupts disabled on all lines.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CfgIrq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const IRQ_CFG: CfgIrq = CfgIrq::new();
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> CfgIrq {
 | 
			
		||||
        CfgIrq {
 | 
			
		||||
            buf: [
 | 
			
		||||
                super::OpCode::CfgDioIrq as u8,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x00,
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Enable an interrupt.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const IRQ_CFG: CfgIrq = CfgIrq::new()
 | 
			
		||||
    ///     .irq_enable(IrqLine::Global, Irq::TxDone)
 | 
			
		||||
    ///     .irq_enable(IrqLine::Global, Irq::Timeout);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "irq_enable returns a modified CfgIrq"]
 | 
			
		||||
    pub const fn irq_enable(mut self, line: IrqLine, irq: Irq) -> CfgIrq {
 | 
			
		||||
        let mask: u16 = irq as u16;
 | 
			
		||||
        let offset: usize = line.offset();
 | 
			
		||||
        self.buf[offset] |= ((mask >> 8) & 0xFF) as u8;
 | 
			
		||||
        self.buf[offset + 1] |= (mask & 0xFF) as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Enable an interrupt on all lines.
 | 
			
		||||
    ///
 | 
			
		||||
    /// As far as I can tell with empirical testing all IRQ lines need to be
 | 
			
		||||
    /// enabled for the internal interrupt to be pending in the NVIC.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CfgIrq, Irq};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const IRQ_CFG: CfgIrq = CfgIrq::new()
 | 
			
		||||
    ///     .irq_enable_all(Irq::TxDone)
 | 
			
		||||
    ///     .irq_enable_all(Irq::Timeout);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x02);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[4], 0x01);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[5], 0x02);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[6], 0x01);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[7], 0x02);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[8], 0x01);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "irq_enable_all returns a modified CfgIrq"]
 | 
			
		||||
    pub const fn irq_enable_all(mut self, irq: Irq) -> CfgIrq {
 | 
			
		||||
        let mask: [u8; 2] = irq.mask().to_be_bytes();
 | 
			
		||||
 | 
			
		||||
        self.buf[1] |= mask[0];
 | 
			
		||||
        self.buf[2] |= mask[1];
 | 
			
		||||
        self.buf[3] |= mask[0];
 | 
			
		||||
        self.buf[4] |= mask[1];
 | 
			
		||||
        self.buf[5] |= mask[0];
 | 
			
		||||
        self.buf[6] |= mask[1];
 | 
			
		||||
        self.buf[7] |= mask[0];
 | 
			
		||||
        self.buf[8] |= mask[1];
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Disable an interrupt.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const IRQ_CFG: CfgIrq = CfgIrq::new()
 | 
			
		||||
    ///     .irq_enable(IrqLine::Global, Irq::TxDone)
 | 
			
		||||
    ///     .irq_enable(IrqLine::Global, Irq::Timeout)
 | 
			
		||||
    ///     .irq_disable(IrqLine::Global, Irq::TxDone)
 | 
			
		||||
    ///     .irq_disable(IrqLine::Global, Irq::Timeout);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x00);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x00);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "irq_disable returns a modified CfgIrq"]
 | 
			
		||||
    pub const fn irq_disable(mut self, line: IrqLine, irq: Irq) -> CfgIrq {
 | 
			
		||||
        let mask: u16 = !(irq as u16);
 | 
			
		||||
        let offset: usize = line.offset();
 | 
			
		||||
        self.buf[offset] &= ((mask >> 8) & 0xFF) as u8;
 | 
			
		||||
        self.buf[offset + 1] &= (mask & 0xFF) as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Disable an interrupt on all lines.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CfgIrq, Irq};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const IRQ_CFG: CfgIrq = CfgIrq::new()
 | 
			
		||||
    ///     .irq_enable_all(Irq::TxDone)
 | 
			
		||||
    ///     .irq_enable_all(Irq::Timeout)
 | 
			
		||||
    ///     .irq_disable_all(Irq::TxDone)
 | 
			
		||||
    ///     .irq_disable_all(Irq::Timeout);
 | 
			
		||||
    /// # assert_eq!(IRQ_CFG, CfgIrq::new());
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "irq_disable_all returns a modified CfgIrq"]
 | 
			
		||||
    pub const fn irq_disable_all(mut self, irq: Irq) -> CfgIrq {
 | 
			
		||||
        let mask: [u8; 2] = (!irq.mask()).to_be_bytes();
 | 
			
		||||
 | 
			
		||||
        self.buf[1] &= mask[0];
 | 
			
		||||
        self.buf[2] &= mask[1];
 | 
			
		||||
        self.buf[3] &= mask[0];
 | 
			
		||||
        self.buf[4] &= mask[1];
 | 
			
		||||
        self.buf[5] &= mask[0];
 | 
			
		||||
        self.buf[6] &= mask[1];
 | 
			
		||||
        self.buf[7] &= mask[0];
 | 
			
		||||
        self.buf[8] &= mask[1];
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CfgIrq, Irq};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const IRQ_CFG: CfgIrq = CfgIrq::new()
 | 
			
		||||
    ///     .irq_enable_all(Irq::TxDone)
 | 
			
		||||
    ///     .irq_enable_all(Irq::Timeout);
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     IRQ_CFG.as_slice(),
 | 
			
		||||
    ///     &[0x08, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01]
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for CfgIrq {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
/// LoRa synchronization word.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_lora_sync_word`][crate::subghz::SubGhz::set_lora_sync_word].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum LoRaSyncWord {
 | 
			
		||||
    /// LoRa private network.
 | 
			
		||||
    Private,
 | 
			
		||||
    /// LoRa public network.
 | 
			
		||||
    Public,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LoRaSyncWord {
 | 
			
		||||
    pub(crate) const fn bytes(self) -> [u8; 2] {
 | 
			
		||||
        match self {
 | 
			
		||||
            LoRaSyncWord::Private => [0x14, 0x24],
 | 
			
		||||
            LoRaSyncWord::Public => [0x34, 0x44],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,14 +0,0 @@
 | 
			
		||||
/// Power amplifier over current protection.
 | 
			
		||||
///
 | 
			
		||||
/// Used by [`set_pa_ocp`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_pa_ocp`]: super::SubGhz::set_pa_ocp
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum Ocp {
 | 
			
		||||
    /// Maximum 60mA current for LP PA mode.
 | 
			
		||||
    Max60m = 0x18,
 | 
			
		||||
    /// Maximum 140mA for HP PA mode.
 | 
			
		||||
    Max140m = 0x38,
 | 
			
		||||
}
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
/// Operation Errors.
 | 
			
		||||
///
 | 
			
		||||
/// Returned by [`op_error`].
 | 
			
		||||
///
 | 
			
		||||
/// [`op_error`]: super::SubGhz::op_error
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum OpError {
 | 
			
		||||
    /// PA ramping failed
 | 
			
		||||
    PaRampError = 8,
 | 
			
		||||
    /// RF-PLL locking failed
 | 
			
		||||
    PllLockError = 6,
 | 
			
		||||
    /// HSE32 clock startup failed
 | 
			
		||||
    XoscStartError = 5,
 | 
			
		||||
    /// Image calibration failed
 | 
			
		||||
    ImageCalibrationError = 4,
 | 
			
		||||
    /// RF-ADC calibration failed
 | 
			
		||||
    AdcCalibrationError = 3,
 | 
			
		||||
    /// RF-PLL calibration failed
 | 
			
		||||
    PllCalibrationError = 2,
 | 
			
		||||
    /// Sub-GHz radio RC 13 MHz oscillator
 | 
			
		||||
    RC13MCalibrationError = 1,
 | 
			
		||||
    /// Sub-GHz radio RC 64 kHz oscillator
 | 
			
		||||
    RC64KCalibrationError = 0,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl OpError {
 | 
			
		||||
    /// Get the bitmask for the error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::OpError;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(OpError::PaRampError.mask(), 0b1_0000_0000);
 | 
			
		||||
    /// assert_eq!(OpError::PllLockError.mask(), 0b0_0100_0000);
 | 
			
		||||
    /// assert_eq!(OpError::XoscStartError.mask(), 0b0_0010_0000);
 | 
			
		||||
    /// assert_eq!(OpError::ImageCalibrationError.mask(), 0b0_0001_0000);
 | 
			
		||||
    /// assert_eq!(OpError::AdcCalibrationError.mask(), 0b0_0000_1000);
 | 
			
		||||
    /// assert_eq!(OpError::PllCalibrationError.mask(), 0b0_0000_0100);
 | 
			
		||||
    /// assert_eq!(OpError::RC13MCalibrationError.mask(), 0b0_0000_0010);
 | 
			
		||||
    /// assert_eq!(OpError::RC64KCalibrationError.mask(), 0b0_0000_0001);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn mask(self) -> u16 {
 | 
			
		||||
        1 << (self as u8)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,196 +0,0 @@
 | 
			
		||||
/// Power amplifier configuration parameters.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_pa_config`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_pa_config`]: super::SubGhz::set_pa_config
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct PaConfig {
 | 
			
		||||
    buf: [u8; 5],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PaConfig {
 | 
			
		||||
    /// Optimal settings for +15dBm output power with the low-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`TxParams::LP_15`](super::TxParams::LP_15).
 | 
			
		||||
    pub const LP_15: PaConfig = PaConfig::new().set_pa_duty_cycle(0x6).set_hp_max(0x0).set_pa(PaSel::Lp);
 | 
			
		||||
 | 
			
		||||
    /// Optimal settings for +14dBm output power with the low-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`TxParams::LP_14`](super::TxParams::LP_14).
 | 
			
		||||
    pub const LP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x0).set_pa(PaSel::Lp);
 | 
			
		||||
 | 
			
		||||
    /// Optimal settings for +10dBm output power with the low-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`TxParams::LP_10`](super::TxParams::LP_10).
 | 
			
		||||
    pub const LP_10: PaConfig = PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp);
 | 
			
		||||
 | 
			
		||||
    /// Optimal settings for +22dBm output power with the high-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`TxParams::HP`](super::TxParams::HP).
 | 
			
		||||
    pub const HP_22: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x7).set_pa(PaSel::Hp);
 | 
			
		||||
 | 
			
		||||
    /// Optimal settings for +20dBm output power with the high-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`TxParams::HP`](super::TxParams::HP).
 | 
			
		||||
    pub const HP_20: PaConfig = PaConfig::new().set_pa_duty_cycle(0x3).set_hp_max(0x5).set_pa(PaSel::Hp);
 | 
			
		||||
 | 
			
		||||
    /// Optimal settings for +17dBm output power with the high-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`TxParams::HP`](super::TxParams::HP).
 | 
			
		||||
    pub const HP_17: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x3).set_pa(PaSel::Hp);
 | 
			
		||||
 | 
			
		||||
    /// Optimal settings for +14dBm output power with the high-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`TxParams::HP`](super::TxParams::HP).
 | 
			
		||||
    pub const HP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x2).set_pa(PaSel::Hp);
 | 
			
		||||
 | 
			
		||||
    /// Create a new `PaConfig` struct.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PaConfig;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PA_CONFIG: PaConfig = PaConfig::new();
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> PaConfig {
 | 
			
		||||
        PaConfig {
 | 
			
		||||
            buf: [super::OpCode::SetPaConfig as u8, 0x01, 0x00, 0x01, 0x01],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the power amplifier duty cycle (conduit angle) control.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Note:** Only the first 3 bits of the `pa_duty_cycle` argument are used.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Duty cycle = 0.2 + 0.04 × bits
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Caution
 | 
			
		||||
    ///
 | 
			
		||||
    /// The following restrictions must be observed to avoid over-stress on the PA:
 | 
			
		||||
    /// * LP PA mode with synthesis frequency > 400 MHz, `pa_duty_cycle` must be < 0x7.
 | 
			
		||||
    /// * LP PA mode with synthesis frequency < 400 MHz, `pa_duty_cycle` must be < 0x4.
 | 
			
		||||
    /// * HP PA mode, `pa_duty_cycle` must be < 0x4
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Lp).set_pa_duty_cycle(0x4);
 | 
			
		||||
    /// # assert_eq!(PA_CONFIG.as_slice()[1], 0x04);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_pa_duty_cycle returns a modified PaConfig"]
 | 
			
		||||
    pub const fn set_pa_duty_cycle(mut self, pa_duty_cycle: u8) -> PaConfig {
 | 
			
		||||
        self.buf[1] = pa_duty_cycle & 0b111;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the high power amplifier output power.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Note:** Only the first 3 bits of the `hp_max` argument are used.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Hp).set_hp_max(0x2);
 | 
			
		||||
    /// # assert_eq!(PA_CONFIG.as_slice()[2], 0x02);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_hp_max returns a modified PaConfig"]
 | 
			
		||||
    pub const fn set_hp_max(mut self, hp_max: u8) -> PaConfig {
 | 
			
		||||
        self.buf[2] = hp_max & 0b111;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the power amplifier to use, low or high power.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PA_CONFIG_HP: PaConfig = PaConfig::new().set_pa(PaSel::Hp);
 | 
			
		||||
    /// const PA_CONFIG_LP: PaConfig = PaConfig::new().set_pa(PaSel::Lp);
 | 
			
		||||
    /// # assert_eq!(PA_CONFIG_HP.as_slice()[3], 0x00);
 | 
			
		||||
    /// # assert_eq!(PA_CONFIG_LP.as_slice()[3], 0x01);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_pa returns a modified PaConfig"]
 | 
			
		||||
    pub const fn set_pa(mut self, pa: PaSel) -> PaConfig {
 | 
			
		||||
        self.buf[3] = pa as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PA_CONFIG: PaConfig = PaConfig::new()
 | 
			
		||||
    ///     .set_pa(PaSel::Hp)
 | 
			
		||||
    ///     .set_pa_duty_cycle(0x2)
 | 
			
		||||
    ///     .set_hp_max(0x3);
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(PA_CONFIG.as_slice(), &[0x95, 0x2, 0x03, 0x00, 0x01]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PaConfig {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Power amplifier selection.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`PaConfig::set_pa`].
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
pub enum PaSel {
 | 
			
		||||
    /// High power amplifier.
 | 
			
		||||
    Hp = 0b0,
 | 
			
		||||
    /// Low power amplifier.
 | 
			
		||||
    Lp = 0b1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialOrd for PaSel {
 | 
			
		||||
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
 | 
			
		||||
        Some(self.cmp(other))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Ord for PaSel {
 | 
			
		||||
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
 | 
			
		||||
        match (self, other) {
 | 
			
		||||
            (PaSel::Hp, PaSel::Hp) | (PaSel::Lp, PaSel::Lp) => core::cmp::Ordering::Equal,
 | 
			
		||||
            (PaSel::Hp, PaSel::Lp) => core::cmp::Ordering::Greater,
 | 
			
		||||
            (PaSel::Lp, PaSel::Hp) => core::cmp::Ordering::Less,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PaSel {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        PaSel::Lp
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::PaSel;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn pa_sel_ord() {
 | 
			
		||||
        assert!(PaSel::Lp < PaSel::Hp);
 | 
			
		||||
        assert!(PaSel::Hp > PaSel::Lp);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,534 +0,0 @@
 | 
			
		||||
/// Preamble detection length for [`GenericPacketParams`].
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum PreambleDetection {
 | 
			
		||||
    /// Preamble detection disabled.
 | 
			
		||||
    Disabled = 0x0,
 | 
			
		||||
    /// 8-bit preamble detection.
 | 
			
		||||
    Bit8 = 0x4,
 | 
			
		||||
    /// 16-bit preamble detection.
 | 
			
		||||
    Bit16 = 0x5,
 | 
			
		||||
    /// 24-bit preamble detection.
 | 
			
		||||
    Bit24 = 0x6,
 | 
			
		||||
    /// 32-bit preamble detection.
 | 
			
		||||
    Bit32 = 0x7,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Address comparison/filtering for [`GenericPacketParams`].
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum AddrComp {
 | 
			
		||||
    /// Address comparison/filtering disabled.
 | 
			
		||||
    Disabled = 0x0,
 | 
			
		||||
    /// Address comparison/filtering on node address.
 | 
			
		||||
    Node = 0x1,
 | 
			
		||||
    /// Address comparison/filtering on node and broadcast addresses.
 | 
			
		||||
    Broadcast = 0x2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Packet header type.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`GenericPacketParams::set_header_type`] and
 | 
			
		||||
/// [`LoRaPacketParams::set_header_type`].
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum HeaderType {
 | 
			
		||||
    /// Fixed; payload length and header field not added to packet.
 | 
			
		||||
    Fixed,
 | 
			
		||||
    /// Variable; payload length and header field added to packet.
 | 
			
		||||
    Variable,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl HeaderType {
 | 
			
		||||
    pub(crate) const fn to_bits_generic(self) -> u8 {
 | 
			
		||||
        match self {
 | 
			
		||||
            HeaderType::Fixed => 0,
 | 
			
		||||
            HeaderType::Variable => 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) const fn to_bits_lora(self) -> u8 {
 | 
			
		||||
        match self {
 | 
			
		||||
            HeaderType::Fixed => 1,
 | 
			
		||||
            HeaderType::Variable => 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// CRC type definition for [`GenericPacketParams`].
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum CrcType {
 | 
			
		||||
    /// 1-byte CRC.
 | 
			
		||||
    Byte1 = 0x0,
 | 
			
		||||
    /// CRC disabled.
 | 
			
		||||
    Disabled = 0x1,
 | 
			
		||||
    /// 2-byte CRC.
 | 
			
		||||
    Byte2 = 0x2,
 | 
			
		||||
    /// 1-byte inverted CRC.
 | 
			
		||||
    Byte1Inverted = 0x4,
 | 
			
		||||
    /// 2-byte inverted CRC.
 | 
			
		||||
    Byte2Inverted = 0x6,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Packet parameters for [`set_packet_params`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_packet_params`]: super::SubGhz::set_packet_params
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct GenericPacketParams {
 | 
			
		||||
    buf: [u8; 10],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GenericPacketParams {
 | 
			
		||||
    /// Create a new `GenericPacketParams`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::GenericPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new();
 | 
			
		||||
    /// assert_eq!(PKT_PARAMS, GenericPacketParams::default());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> GenericPacketParams {
 | 
			
		||||
        const OPCODE: u8 = super::OpCode::SetPacketParams as u8;
 | 
			
		||||
        // const variable ensure the compile always optimizes the methods
 | 
			
		||||
        const NEW: GenericPacketParams = GenericPacketParams {
 | 
			
		||||
            buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
 | 
			
		||||
        }
 | 
			
		||||
        .set_preamble_len(1)
 | 
			
		||||
        .set_preamble_detection(PreambleDetection::Disabled)
 | 
			
		||||
        .set_sync_word_len(0)
 | 
			
		||||
        .set_addr_comp(AddrComp::Disabled)
 | 
			
		||||
        .set_header_type(HeaderType::Fixed)
 | 
			
		||||
        .set_payload_len(1);
 | 
			
		||||
 | 
			
		||||
        NEW
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Preamble length in number of symbols.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Values of zero are invalid, and will automatically be set to 1.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::GenericPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_preamble_len(0x1234);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "preamble_length returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_preamble_len(mut self, mut len: u16) -> GenericPacketParams {
 | 
			
		||||
        if len == 0 {
 | 
			
		||||
            len = 1
 | 
			
		||||
        }
 | 
			
		||||
        self.buf[1] = ((len >> 8) & 0xFF) as u8;
 | 
			
		||||
        self.buf[2] = (len & 0xFF) as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Preamble detection length in number of bit symbols.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{GenericPacketParams, PreambleDetection};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams =
 | 
			
		||||
    ///     GenericPacketParams::new().set_preamble_detection(PreambleDetection::Bit8);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x4);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_preamble_detection returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_preamble_detection(mut self, pb_det: PreambleDetection) -> GenericPacketParams {
 | 
			
		||||
        self.buf[3] = pb_det as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sync word length in number of bit symbols.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Valid values are `0x00` - `0x40` for 0 to 64-bits respectively.
 | 
			
		||||
    /// Values that exceed the maximum will saturate at `0x40`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Set the sync word length to 4 bytes (16 bits).
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::GenericPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_sync_word_len(16);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[4], 0x10);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_sync_word_len returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_sync_word_len(mut self, len: u8) -> GenericPacketParams {
 | 
			
		||||
        const MAX: u8 = 0x40;
 | 
			
		||||
        if len > MAX {
 | 
			
		||||
            self.buf[4] = MAX;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.buf[4] = len;
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Address comparison/filtering.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Enable address on the node address.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{AddrComp, GenericPacketParams};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams =
 | 
			
		||||
    ///     GenericPacketParams::new().set_addr_comp(AddrComp::Node);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x01);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_addr_comp returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_addr_comp(mut self, addr_comp: AddrComp) -> GenericPacketParams {
 | 
			
		||||
        self.buf[5] = addr_comp as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Header type definition.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Note:** The reference manual calls this packet type, but that results
 | 
			
		||||
    /// in a conflicting variable name for the modulation scheme, which the
 | 
			
		||||
    /// reference manual also calls packet type.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Set the header type to a variable length.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{GenericPacketParams, HeaderType};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams =
 | 
			
		||||
    ///     GenericPacketParams::new().set_header_type(HeaderType::Variable);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x01);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_header_type returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_header_type(mut self, header_type: HeaderType) -> GenericPacketParams {
 | 
			
		||||
        self.buf[6] = header_type.to_bits_generic();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the payload length in bytes.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::GenericPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_payload_len(12);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[7], 12);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_payload_len returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_payload_len(mut self, len: u8) -> GenericPacketParams {
 | 
			
		||||
        self.buf[7] = len;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// CRC type definition.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CrcType, GenericPacketParams};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams =
 | 
			
		||||
    ///     GenericPacketParams::new().set_crc_type(CrcType::Byte2Inverted);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[8], 0x6);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_payload_len returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_crc_type(mut self, crc_type: CrcType) -> GenericPacketParams {
 | 
			
		||||
        self.buf[8] = crc_type as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Whitening enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Enable whitening.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::GenericPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_whitening_enable(true);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[9], 1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_whitening_enable returns a modified GenericPacketParams"]
 | 
			
		||||
    pub const fn set_whitening_enable(mut self, en: bool) -> GenericPacketParams {
 | 
			
		||||
        self.buf[9] = en as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{
 | 
			
		||||
    ///     AddrComp, CrcType, GenericPacketParams, HeaderType, PreambleDetection,
 | 
			
		||||
    /// };
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new()
 | 
			
		||||
    ///     .set_preamble_len(8)
 | 
			
		||||
    ///     .set_preamble_detection(PreambleDetection::Disabled)
 | 
			
		||||
    ///     .set_sync_word_len(2)
 | 
			
		||||
    ///     .set_addr_comp(AddrComp::Disabled)
 | 
			
		||||
    ///     .set_header_type(HeaderType::Fixed)
 | 
			
		||||
    ///     .set_payload_len(128)
 | 
			
		||||
    ///     .set_crc_type(CrcType::Byte2)
 | 
			
		||||
    ///     .set_whitening_enable(true);
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     PKT_PARAMS.as_slice(),
 | 
			
		||||
    ///     &[0x8C, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x02, 0x01]
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for GenericPacketParams {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Packet parameters for [`set_lora_packet_params`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_lora_packet_params`]: super::SubGhz::set_lora_packet_params
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
pub struct LoRaPacketParams {
 | 
			
		||||
    buf: [u8; 7],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LoRaPacketParams {
 | 
			
		||||
    /// Create a new `LoRaPacketParams`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::LoRaPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new();
 | 
			
		||||
    /// assert_eq!(PKT_PARAMS, LoRaPacketParams::default());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> LoRaPacketParams {
 | 
			
		||||
        const OPCODE: u8 = super::OpCode::SetPacketParams as u8;
 | 
			
		||||
        // const variable ensure the compile always optimizes the methods
 | 
			
		||||
        const NEW: LoRaPacketParams = LoRaPacketParams {
 | 
			
		||||
            buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
 | 
			
		||||
        }
 | 
			
		||||
        .set_preamble_len(1)
 | 
			
		||||
        .set_header_type(HeaderType::Fixed)
 | 
			
		||||
        .set_payload_len(1)
 | 
			
		||||
        .set_crc_en(true)
 | 
			
		||||
        .set_invert_iq(false);
 | 
			
		||||
 | 
			
		||||
        NEW
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Preamble length in number of symbols.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Values of zero are invalid, and will automatically be set to 1.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::LoRaPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_preamble_len(0x1234);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "preamble_length returns a modified LoRaPacketParams"]
 | 
			
		||||
    pub const fn set_preamble_len(mut self, mut len: u16) -> LoRaPacketParams {
 | 
			
		||||
        if len == 0 {
 | 
			
		||||
            len = 1
 | 
			
		||||
        }
 | 
			
		||||
        self.buf[1] = ((len >> 8) & 0xFF) as u8;
 | 
			
		||||
        self.buf[2] = (len & 0xFF) as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Header type (fixed or variable).
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Set the payload type to a fixed length.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_header_type(HeaderType::Fixed);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x01);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_header_type returns a modified LoRaPacketParams"]
 | 
			
		||||
    pub const fn set_header_type(mut self, header_type: HeaderType) -> LoRaPacketParams {
 | 
			
		||||
        self.buf[3] = header_type.to_bits_lora();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the payload length in bytes.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::LoRaPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_payload_len(12);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[4], 12);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_payload_len returns a modified LoRaPacketParams"]
 | 
			
		||||
    pub const fn set_payload_len(mut self, len: u8) -> LoRaPacketParams {
 | 
			
		||||
        self.buf[4] = len;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// CRC enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Enable CRC.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::LoRaPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_crc_en(true);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_crc_en returns a modified LoRaPacketParams"]
 | 
			
		||||
    pub const fn set_crc_en(mut self, en: bool) -> LoRaPacketParams {
 | 
			
		||||
        self.buf[5] = en as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// IQ setup.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Use an inverted IQ setup.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::LoRaPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_invert_iq(true);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_invert_iq returns a modified LoRaPacketParams"]
 | 
			
		||||
    pub const fn set_invert_iq(mut self, invert: bool) -> LoRaPacketParams {
 | 
			
		||||
        self.buf[6] = invert as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new()
 | 
			
		||||
    ///     .set_preamble_len(5 * 8)
 | 
			
		||||
    ///     .set_header_type(HeaderType::Fixed)
 | 
			
		||||
    ///     .set_payload_len(64)
 | 
			
		||||
    ///     .set_crc_en(true)
 | 
			
		||||
    ///     .set_invert_iq(true);
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     PKT_PARAMS.as_slice(),
 | 
			
		||||
    ///     &[0x8C, 0x00, 0x28, 0x01, 0x40, 0x01, 0x01]
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for LoRaPacketParams {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Packet parameters for [`set_bpsk_packet_params`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_bpsk_packet_params`]: super::SubGhz::set_bpsk_packet_params
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct BpskPacketParams {
 | 
			
		||||
    buf: [u8; 2],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BpskPacketParams {
 | 
			
		||||
    /// Create a new `BpskPacketParams`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BpskPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new();
 | 
			
		||||
    /// assert_eq!(PKT_PARAMS, BpskPacketParams::default());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> BpskPacketParams {
 | 
			
		||||
        BpskPacketParams {
 | 
			
		||||
            buf: [super::OpCode::SetPacketParams as u8, 0x00],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the payload length in bytes.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The length includes preamble, sync word, device ID, and CRC.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::BpskPacketParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(12);
 | 
			
		||||
    /// # assert_eq!(PKT_PARAMS.as_slice()[1], 12);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_payload_len returns a modified BpskPacketParams"]
 | 
			
		||||
    pub const fn set_payload_len(mut self, len: u8) -> BpskPacketParams {
 | 
			
		||||
        self.buf[1] = len;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{BpskPacketParams, HeaderType};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(24);
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(PKT_PARAMS.as_slice(), &[0x8C, 24]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for BpskPacketParams {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,282 +0,0 @@
 | 
			
		||||
use super::{Ratio, Status};
 | 
			
		||||
 | 
			
		||||
/// (G)FSK packet status.
 | 
			
		||||
///
 | 
			
		||||
/// Returned by [`fsk_packet_status`].
 | 
			
		||||
///
 | 
			
		||||
/// [`fsk_packet_status`]: super::SubGhz::fsk_packet_status
 | 
			
		||||
#[derive(Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
pub struct FskPacketStatus {
 | 
			
		||||
    buf: [u8; 4],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<[u8; 4]> for FskPacketStatus {
 | 
			
		||||
    fn from(buf: [u8; 4]) -> Self {
 | 
			
		||||
        FskPacketStatus { buf }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FskPacketStatus {
 | 
			
		||||
    /// Get the status.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CmdStatus, FskPacketStatus, Status, StatusMode};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0];
 | 
			
		||||
    /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio);
 | 
			
		||||
    /// let status: Status = pkt_status.status();
 | 
			
		||||
    /// assert_eq!(status.mode(), Ok(StatusMode::Rx));
 | 
			
		||||
    /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn status(&self) -> Status {
 | 
			
		||||
        Status::from_raw(self.buf[0])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if a preamble error occurred.
 | 
			
		||||
    pub const fn preamble_err(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & (1 << 7)) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if a synchronization error occurred.
 | 
			
		||||
    pub const fn sync_err(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & (1 << 6)) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if an address error occurred.
 | 
			
		||||
    pub const fn addr_err(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & (1 << 5)) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if an CRC error occurred.
 | 
			
		||||
    pub const fn crc_err(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & (1 << 4)) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if a length error occurred.
 | 
			
		||||
    pub const fn length_err(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & (1 << 3)) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if an abort error occurred.
 | 
			
		||||
    pub const fn abort_err(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & (1 << 2)) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if a packet is received.
 | 
			
		||||
    pub const fn pkt_received(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & (1 << 1)) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` when a packet has been sent.
 | 
			
		||||
    pub const fn pkt_sent(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & 1) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if any error occurred.
 | 
			
		||||
    pub const fn any_err(&self) -> bool {
 | 
			
		||||
        (self.buf[1] & 0xFC) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// RSSI level when the synchronization address is detected.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Units are in dBm.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 4] = [0, 0, 80, 0];
 | 
			
		||||
    /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(pkt_status.rssi_sync().to_integer(), -40);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn rssi_sync(&self) -> Ratio<i16> {
 | 
			
		||||
        Ratio::new_raw(i16::from(self.buf[2]), -2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the RSSI level over the received packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Units are in dBm.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 100];
 | 
			
		||||
    /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(pkt_status.rssi_avg().to_integer(), -50);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn rssi_avg(&self) -> Ratio<i16> {
 | 
			
		||||
        Ratio::new_raw(i16::from(self.buf[3]), -2)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "defmt")]
 | 
			
		||||
impl defmt::Format for FskPacketStatus {
 | 
			
		||||
    fn format(&self, fmt: defmt::Formatter) {
 | 
			
		||||
        defmt::write!(
 | 
			
		||||
            fmt,
 | 
			
		||||
            r#"FskPacketStatus {{
 | 
			
		||||
    status: {},
 | 
			
		||||
    preamble_err: {},
 | 
			
		||||
    sync_err: {},
 | 
			
		||||
    addr_err: {},
 | 
			
		||||
    crc_err: {},
 | 
			
		||||
    length_err: {},
 | 
			
		||||
    abort_err: {},
 | 
			
		||||
    pkt_received: {},
 | 
			
		||||
    pkt_sent: {},
 | 
			
		||||
    rssi_sync: {},
 | 
			
		||||
    rssi_avg: {},
 | 
			
		||||
}}"#,
 | 
			
		||||
            self.status(),
 | 
			
		||||
            self.preamble_err(),
 | 
			
		||||
            self.sync_err(),
 | 
			
		||||
            self.addr_err(),
 | 
			
		||||
            self.crc_err(),
 | 
			
		||||
            self.length_err(),
 | 
			
		||||
            self.abort_err(),
 | 
			
		||||
            self.pkt_received(),
 | 
			
		||||
            self.pkt_sent(),
 | 
			
		||||
            self.rssi_sync().to_integer(),
 | 
			
		||||
            self.rssi_avg().to_integer()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl core::fmt::Debug for FskPacketStatus {
 | 
			
		||||
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
			
		||||
        f.debug_struct("FskPacketStatus")
 | 
			
		||||
            .field("status", &self.status())
 | 
			
		||||
            .field("preamble_err", &self.preamble_err())
 | 
			
		||||
            .field("sync_err", &self.sync_err())
 | 
			
		||||
            .field("addr_err", &self.addr_err())
 | 
			
		||||
            .field("crc_err", &self.crc_err())
 | 
			
		||||
            .field("length_err", &self.length_err())
 | 
			
		||||
            .field("abort_err", &self.abort_err())
 | 
			
		||||
            .field("pkt_received", &self.pkt_received())
 | 
			
		||||
            .field("pkt_sent", &self.pkt_sent())
 | 
			
		||||
            .field("rssi_sync", &self.rssi_sync().to_integer())
 | 
			
		||||
            .field("rssi_avg", &self.rssi_avg().to_integer())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// (G)FSK packet status.
 | 
			
		||||
///
 | 
			
		||||
/// Returned by [`lora_packet_status`].
 | 
			
		||||
///
 | 
			
		||||
/// [`lora_packet_status`]: super::SubGhz::lora_packet_status
 | 
			
		||||
#[derive(Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
pub struct LoRaPacketStatus {
 | 
			
		||||
    buf: [u8; 4],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<[u8; 4]> for LoRaPacketStatus {
 | 
			
		||||
    fn from(buf: [u8; 4]) -> Self {
 | 
			
		||||
        LoRaPacketStatus { buf }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LoRaPacketStatus {
 | 
			
		||||
    /// Get the status.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CmdStatus, LoRaPacketStatus, Status, StatusMode};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0];
 | 
			
		||||
    /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
 | 
			
		||||
    /// let status: Status = pkt_status.status();
 | 
			
		||||
    /// assert_eq!(status.mode(), Ok(StatusMode::Rx));
 | 
			
		||||
    /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn status(&self) -> Status {
 | 
			
		||||
        Status::from_raw(self.buf[0])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Average RSSI level over the received packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Units are in dBm.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 4] = [0, 80, 0, 0];
 | 
			
		||||
    /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(pkt_status.rssi_pkt().to_integer(), -40);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn rssi_pkt(&self) -> Ratio<i16> {
 | 
			
		||||
        Ratio::new_raw(i16::from(self.buf[1]), -2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Estimation of SNR over the received packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Units are in dB.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 4] = [0, 0, 40, 0];
 | 
			
		||||
    /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(pkt_status.snr_pkt().to_integer(), 10);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn snr_pkt(&self) -> Ratio<i16> {
 | 
			
		||||
        Ratio::new_raw(i16::from(self.buf[2]), 4)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Estimation of RSSI level of the LoRa signal after despreading.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Units are in dBm.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 80];
 | 
			
		||||
    /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(pkt_status.signal_rssi_pkt().to_integer(), -40);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn signal_rssi_pkt(&self) -> Ratio<i16> {
 | 
			
		||||
        Ratio::new_raw(i16::from(self.buf[3]), -2)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "defmt")]
 | 
			
		||||
impl defmt::Format for LoRaPacketStatus {
 | 
			
		||||
    fn format(&self, fmt: defmt::Formatter) {
 | 
			
		||||
        defmt::write!(
 | 
			
		||||
            fmt,
 | 
			
		||||
            r#"LoRaPacketStatus {{
 | 
			
		||||
    status: {},
 | 
			
		||||
    rssi_pkt: {},
 | 
			
		||||
    snr_pkt: {},
 | 
			
		||||
    signal_rssi_pkt: {},
 | 
			
		||||
}}"#,
 | 
			
		||||
            self.status(),
 | 
			
		||||
            self.rssi_pkt().to_integer(),
 | 
			
		||||
            self.snr_pkt().to_integer(),
 | 
			
		||||
            self.signal_rssi_pkt().to_integer(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl core::fmt::Debug for LoRaPacketStatus {
 | 
			
		||||
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
			
		||||
        f.debug_struct("LoRaPacketStatus")
 | 
			
		||||
            .field("status", &self.status())
 | 
			
		||||
            .field("rssi_pkt", &self.rssi_pkt().to_integer())
 | 
			
		||||
            .field("snr_pkt", &self.snr_pkt().to_integer())
 | 
			
		||||
            .field("signal_rssi_pkt", &self.signal_rssi_pkt().to_integer())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
/// Packet type definition.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_packet_type`]
 | 
			
		||||
///
 | 
			
		||||
/// [`set_packet_type`]: super::SubGhz::set_packet_type
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum PacketType {
 | 
			
		||||
    /// FSK (frequency shift keying) generic packet type.
 | 
			
		||||
    Fsk = 0,
 | 
			
		||||
    /// LoRa (long range) packet type.
 | 
			
		||||
    LoRa = 1,
 | 
			
		||||
    /// BPSK (binary phase shift keying) packet type.
 | 
			
		||||
    Bpsk = 2,
 | 
			
		||||
    /// MSK (minimum shift keying) generic packet type.
 | 
			
		||||
    Msk = 3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PacketType {
 | 
			
		||||
    /// Create a new `PacketType` from bits.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PacketType;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(PacketType::from_raw(0), Ok(PacketType::Fsk));
 | 
			
		||||
    /// assert_eq!(PacketType::from_raw(1), Ok(PacketType::LoRa));
 | 
			
		||||
    /// assert_eq!(PacketType::from_raw(2), Ok(PacketType::Bpsk));
 | 
			
		||||
    /// assert_eq!(PacketType::from_raw(3), Ok(PacketType::Msk));
 | 
			
		||||
    /// // Other values are reserved
 | 
			
		||||
    /// assert_eq!(PacketType::from_raw(4), Err(4));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw(bits: u8) -> Result<PacketType, u8> {
 | 
			
		||||
        match bits {
 | 
			
		||||
            0 => Ok(PacketType::Fsk),
 | 
			
		||||
            1 => Ok(PacketType::LoRa),
 | 
			
		||||
            2 => Ok(PacketType::Bpsk),
 | 
			
		||||
            3 => Ok(PacketType::Msk),
 | 
			
		||||
            _ => Err(bits),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,247 +0,0 @@
 | 
			
		||||
/// Generic packet infinite sequence selection.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`PktCtrl::set_inf_seq_sel`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum InfSeqSel {
 | 
			
		||||
    /// Preamble `0x5555`.
 | 
			
		||||
    Five = 0b00,
 | 
			
		||||
    /// Preamble `0x0000`.
 | 
			
		||||
    Zero = 0b01,
 | 
			
		||||
    /// Preamble `0xFFFF`.
 | 
			
		||||
    One = 0b10,
 | 
			
		||||
    /// PRBS9.
 | 
			
		||||
    Prbs9 = 0b11,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for InfSeqSel {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        InfSeqSel::Five
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generic packet control.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_pkt_ctrl`](super::SubGhz::set_pkt_ctrl).
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct PktCtrl {
 | 
			
		||||
    val: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PktCtrl {
 | 
			
		||||
    /// Reset value of the packet control register.
 | 
			
		||||
    pub const RESET: PktCtrl = PktCtrl { val: 0x21 };
 | 
			
		||||
 | 
			
		||||
    /// Create a new [`PktCtrl`] structure from a raw value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Reserved bits will be masked.
 | 
			
		||||
    pub const fn from_raw(raw: u8) -> Self {
 | 
			
		||||
        Self { val: raw & 0x3F }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the raw value of the [`PktCtrl`] register.
 | 
			
		||||
    pub const fn as_bits(&self) -> u8 {
 | 
			
		||||
        self.val
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Generic packet synchronization word detection enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_sync_det_en(true);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_sync_det_en returns a modified PktCtrl"]
 | 
			
		||||
    pub const fn set_sync_det_en(mut self, en: bool) -> PktCtrl {
 | 
			
		||||
        if en {
 | 
			
		||||
            self.val |= 1 << 5;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !(1 << 5);
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if generic packet synchronization word detection is
 | 
			
		||||
    /// enabled.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = PktCtrl::RESET;
 | 
			
		||||
    /// assert_eq!(pc.sync_det_en(), true);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_sync_det_en(false);
 | 
			
		||||
    /// assert_eq!(pc.sync_det_en(), false);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_sync_det_en(true);
 | 
			
		||||
    /// assert_eq!(pc.sync_det_en(), true);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn sync_det_en(&self) -> bool {
 | 
			
		||||
        self.val & (1 << 5) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Generic packet continuous transmit enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_cont_tx_en(true);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_cont_tx_en returns a modified PktCtrl"]
 | 
			
		||||
    pub const fn set_cont_tx_en(mut self, en: bool) -> PktCtrl {
 | 
			
		||||
        if en {
 | 
			
		||||
            self.val |= 1 << 4;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !(1 << 4);
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if generic packet continuous transmit is enabled.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = PktCtrl::RESET;
 | 
			
		||||
    /// assert_eq!(pc.cont_tx_en(), false);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_cont_tx_en(true);
 | 
			
		||||
    /// assert_eq!(pc.cont_tx_en(), true);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_cont_tx_en(false);
 | 
			
		||||
    /// assert_eq!(pc.cont_tx_en(), false);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn cont_tx_en(&self) -> bool {
 | 
			
		||||
        self.val & (1 << 4) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the continuous sequence type.
 | 
			
		||||
    #[must_use = "set_inf_seq_sel returns a modified PktCtrl"]
 | 
			
		||||
    pub const fn set_inf_seq_sel(mut self, sel: InfSeqSel) -> PktCtrl {
 | 
			
		||||
        self.val &= !(0b11 << 2);
 | 
			
		||||
        self.val |= (sel as u8) << 2;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the continuous sequence type.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{InfSeqSel, PktCtrl};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = PktCtrl::RESET;
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Zero);
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Zero);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::One);
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::One);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Prbs9);
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Prbs9);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Five);
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn inf_seq_sel(&self) -> InfSeqSel {
 | 
			
		||||
        match (self.val >> 2) & 0b11 {
 | 
			
		||||
            0b00 => InfSeqSel::Five,
 | 
			
		||||
            0b01 => InfSeqSel::Zero,
 | 
			
		||||
            0b10 => InfSeqSel::One,
 | 
			
		||||
            _ => InfSeqSel::Prbs9,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Enable infinite sequence generation.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_inf_seq_en(true);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_inf_seq_en returns a modified PktCtrl"]
 | 
			
		||||
    pub const fn set_inf_seq_en(mut self, en: bool) -> PktCtrl {
 | 
			
		||||
        if en {
 | 
			
		||||
            self.val |= 1 << 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !(1 << 1);
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if infinite sequence generation is enabled.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = PktCtrl::RESET;
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_en(), false);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_inf_seq_en(true);
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_en(), true);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_inf_seq_en(false);
 | 
			
		||||
    /// assert_eq!(pc.inf_seq_en(), false);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn inf_seq_en(&self) -> bool {
 | 
			
		||||
        self.val & (1 << 1) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the value of bit-8 (9th bit) for generic packet whitening.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_whitening_init(true);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_whitening_init returns a modified PktCtrl"]
 | 
			
		||||
    pub const fn set_whitening_init(mut self, val: bool) -> PktCtrl {
 | 
			
		||||
        if val {
 | 
			
		||||
            self.val |= 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !1;
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if bit-8 of the generic packet whitening is set.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PktCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PktCtrl = PktCtrl::RESET;
 | 
			
		||||
    /// assert_eq!(pc.whitening_init(), true);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_whitening_init(false);
 | 
			
		||||
    /// assert_eq!(pc.whitening_init(), false);
 | 
			
		||||
    /// let pc: PktCtrl = pc.set_whitening_init(true);
 | 
			
		||||
    /// assert_eq!(pc.whitening_init(), true);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn whitening_init(&self) -> bool {
 | 
			
		||||
        self.val & 0b1 != 0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<PktCtrl> for u8 {
 | 
			
		||||
    fn from(pc: PktCtrl) -> Self {
 | 
			
		||||
        pc.val
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PktCtrl {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::RESET
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
/// RX gain power modes.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_rx_gain`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_rx_gain`]: super::SubGhz::set_rx_gain
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum PMode {
 | 
			
		||||
    /// Power saving mode.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Reduces sensitivity.
 | 
			
		||||
    #[allow(clippy::identity_op)]
 | 
			
		||||
    PowerSaving = (0x25 << 2) | 0b00,
 | 
			
		||||
    /// Boost mode level 1.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Improves sensitivity at detriment of power consumption.
 | 
			
		||||
    Boost1 = (0x25 << 2) | 0b01,
 | 
			
		||||
    /// Boost mode level 2.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Improves a set further sensitivity at detriment of power consumption.
 | 
			
		||||
    Boost2 = (0x25 << 2) | 0b10,
 | 
			
		||||
    /// Boost mode.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Best receiver sensitivity.
 | 
			
		||||
    Boost = (0x25 << 2) | 0b11,
 | 
			
		||||
}
 | 
			
		||||
@@ -1,160 +0,0 @@
 | 
			
		||||
/// Power-supply current limit.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`PwrCtrl::set_current_lim`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum CurrentLim {
 | 
			
		||||
    /// 25 mA
 | 
			
		||||
    Milli25 = 0x0,
 | 
			
		||||
    /// 50 mA (default)
 | 
			
		||||
    Milli50 = 0x1,
 | 
			
		||||
    /// 100 mA
 | 
			
		||||
    Milli100 = 0x2,
 | 
			
		||||
    /// 200 mA
 | 
			
		||||
    Milli200 = 0x3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CurrentLim {
 | 
			
		||||
    /// Get the SMPS drive value as milliamps.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CurrentLim;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(CurrentLim::Milli25.as_milliamps(), 25);
 | 
			
		||||
    /// assert_eq!(CurrentLim::Milli50.as_milliamps(), 50);
 | 
			
		||||
    /// assert_eq!(CurrentLim::Milli100.as_milliamps(), 100);
 | 
			
		||||
    /// assert_eq!(CurrentLim::Milli200.as_milliamps(), 200);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_milliamps(&self) -> u8 {
 | 
			
		||||
        match self {
 | 
			
		||||
            CurrentLim::Milli25 => 25,
 | 
			
		||||
            CurrentLim::Milli50 => 50,
 | 
			
		||||
            CurrentLim::Milli100 => 100,
 | 
			
		||||
            CurrentLim::Milli200 => 200,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for CurrentLim {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        CurrentLim::Milli50
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Power control.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_bit_sync`](super::SubGhz::set_bit_sync).
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct PwrCtrl {
 | 
			
		||||
    val: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PwrCtrl {
 | 
			
		||||
    /// Power control register reset value.
 | 
			
		||||
    pub const RESET: PwrCtrl = PwrCtrl { val: 0x50 };
 | 
			
		||||
 | 
			
		||||
    /// Create a new [`PwrCtrl`] structure from a raw value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Reserved bits will be masked.
 | 
			
		||||
    pub const fn from_raw(raw: u8) -> Self {
 | 
			
		||||
        Self { val: raw & 0x70 }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the raw value of the [`PwrCtrl`] register.
 | 
			
		||||
    pub const fn as_bits(&self) -> u8 {
 | 
			
		||||
        self.val
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the current limiter enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PwrCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const PWR_CTRL: PwrCtrl = PwrCtrl::RESET.set_current_lim_en(true);
 | 
			
		||||
    /// # assert_eq!(u8::from(PWR_CTRL), 0x50u8);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_current_lim_en returns a modified PwrCtrl"]
 | 
			
		||||
    pub const fn set_current_lim_en(mut self, en: bool) -> PwrCtrl {
 | 
			
		||||
        if en {
 | 
			
		||||
            self.val |= 1 << 6;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.val &= !(1 << 6);
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if current limiting is enabled
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::PwrCtrl;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PwrCtrl = PwrCtrl::RESET;
 | 
			
		||||
    /// assert_eq!(pc.current_limit_en(), true);
 | 
			
		||||
    /// let pc: PwrCtrl = pc.set_current_lim_en(false);
 | 
			
		||||
    /// assert_eq!(pc.current_limit_en(), false);
 | 
			
		||||
    /// let pc: PwrCtrl = pc.set_current_lim_en(true);
 | 
			
		||||
    /// assert_eq!(pc.current_limit_en(), true);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn current_limit_en(&self) -> bool {
 | 
			
		||||
        self.val & (1 << 6) != 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the current limit.
 | 
			
		||||
    #[must_use = "set_current_lim returns a modified PwrCtrl"]
 | 
			
		||||
    pub const fn set_current_lim(mut self, lim: CurrentLim) -> PwrCtrl {
 | 
			
		||||
        self.val &= !(0x30);
 | 
			
		||||
        self.val |= (lim as u8) << 4;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the current limit.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CurrentLim, PwrCtrl};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PwrCtrl = PwrCtrl::RESET;
 | 
			
		||||
    /// assert_eq!(pc.current_lim(), CurrentLim::Milli50);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli25);
 | 
			
		||||
    /// assert_eq!(pc.current_lim(), CurrentLim::Milli25);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli50);
 | 
			
		||||
    /// assert_eq!(pc.current_lim(), CurrentLim::Milli50);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli100);
 | 
			
		||||
    /// assert_eq!(pc.current_lim(), CurrentLim::Milli100);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli200);
 | 
			
		||||
    /// assert_eq!(pc.current_lim(), CurrentLim::Milli200);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn current_lim(&self) -> CurrentLim {
 | 
			
		||||
        match (self.val >> 4) & 0b11 {
 | 
			
		||||
            0x0 => CurrentLim::Milli25,
 | 
			
		||||
            0x1 => CurrentLim::Milli50,
 | 
			
		||||
            0x2 => CurrentLim::Milli100,
 | 
			
		||||
            _ => CurrentLim::Milli200,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<PwrCtrl> for u8 {
 | 
			
		||||
    fn from(bs: PwrCtrl) -> Self {
 | 
			
		||||
        bs.val
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PwrCtrl {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::RESET
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
/// Radio power supply selection.
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum RegMode {
 | 
			
		||||
    /// Linear dropout regulator
 | 
			
		||||
    Ldo = 0b0,
 | 
			
		||||
    /// Switch mode power supply.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Used in standby with HSE32, FS, RX, and TX modes.
 | 
			
		||||
    Smps = 0b1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for RegMode {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        RegMode::Ldo
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,135 +0,0 @@
 | 
			
		||||
/// RF frequency structure.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_rf_frequency`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_rf_frequency`]: super::SubGhz::set_rf_frequency
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct RfFreq {
 | 
			
		||||
    buf: [u8; 5],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RfFreq {
 | 
			
		||||
    /// 915MHz, often used in Australia and North America.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::RfFreq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(RfFreq::F915.freq(), 915_000_000);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const F915: RfFreq = RfFreq::from_raw(0x39_30_00_00);
 | 
			
		||||
 | 
			
		||||
    /// 868MHz, often used in Europe.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::RfFreq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(RfFreq::F868.freq(), 868_000_000);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const F868: RfFreq = RfFreq::from_raw(0x36_40_00_00);
 | 
			
		||||
 | 
			
		||||
    /// 433MHz, often used in Europe.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::RfFreq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(RfFreq::F433.freq(), 433_000_000);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const F433: RfFreq = RfFreq::from_raw(0x1B_10_00_00);
 | 
			
		||||
 | 
			
		||||
    /// Create a new `RfFreq` from a raw bit value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The equation used to get the PLL frequency from the raw bits is:
 | 
			
		||||
    ///
 | 
			
		||||
    /// RF<sub>PLL</sub> = 32e6 × bits / 2<sup>25</sup>
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::RfFreq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const FREQ: RfFreq = RfFreq::from_raw(0x39300000);
 | 
			
		||||
    /// assert_eq!(FREQ, RfFreq::F915);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw(bits: u32) -> RfFreq {
 | 
			
		||||
        RfFreq {
 | 
			
		||||
            buf: [
 | 
			
		||||
                super::OpCode::SetRfFrequency as u8,
 | 
			
		||||
                ((bits >> 24) & 0xFF) as u8,
 | 
			
		||||
                ((bits >> 16) & 0xFF) as u8,
 | 
			
		||||
                ((bits >> 8) & 0xFF) as u8,
 | 
			
		||||
                (bits & 0xFF) as u8,
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new `RfFreq` from a PLL frequency.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The equation used to get the raw bits from the PLL frequency is:
 | 
			
		||||
    ///
 | 
			
		||||
    /// bits = RF<sub>PLL</sub> * 2<sup>25</sup> / 32e6
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::RfFreq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const FREQ: RfFreq = RfFreq::from_frequency(915_000_000);
 | 
			
		||||
    /// assert_eq!(FREQ, RfFreq::F915);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_frequency(freq: u32) -> RfFreq {
 | 
			
		||||
        Self::from_raw((((freq as u64) * (1 << 25)) / 32_000_000) as u32)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the frequency bit value.
 | 
			
		||||
    const fn as_bits(&self) -> u32 {
 | 
			
		||||
        ((self.buf[1] as u32) << 24) | ((self.buf[2] as u32) << 16) | ((self.buf[3] as u32) << 8) | (self.buf[4] as u32)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the actual frequency.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::RfFreq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(RfFreq::from_raw(0x39300000).freq(), 915_000_000);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn freq(&self) -> u32 {
 | 
			
		||||
        (32_000_000 * (self.as_bits() as u64) / (1 << 25)) as u32
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::RfFreq;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(RfFreq::F915.as_slice(), &[0x86, 0x39, 0x30, 0x00, 0x00]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::RfFreq;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn max() {
 | 
			
		||||
        assert_eq!(RfFreq::from_raw(u32::MAX).freq(), 4_095_999_999);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn min() {
 | 
			
		||||
        assert_eq!(RfFreq::from_raw(u32::MIN).freq(), 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
/// Receiver event which stops the RX timeout timer.
 | 
			
		||||
///
 | 
			
		||||
/// Used by [`set_rx_timeout_stop`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_rx_timeout_stop`]: super::SubGhz::set_rx_timeout_stop
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum RxTimeoutStop {
 | 
			
		||||
    /// Receive timeout stopped on synchronization word detection in generic
 | 
			
		||||
    /// packet mode or header detection in LoRa packet mode.
 | 
			
		||||
    Sync = 0b0,
 | 
			
		||||
    /// Receive timeout stopped on preamble detection.
 | 
			
		||||
    Preamble = 0b1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<RxTimeoutStop> for u8 {
 | 
			
		||||
    fn from(rx_ts: RxTimeoutStop) -> Self {
 | 
			
		||||
        rx_ts as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
/// Startup configurations when exiting sleep mode.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`SleepCfg::set_startup`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum Startup {
 | 
			
		||||
    /// Cold startup when exiting Sleep mode, configuration registers reset.
 | 
			
		||||
    Cold = 0,
 | 
			
		||||
    /// Warm startup when exiting Sleep mode,
 | 
			
		||||
    /// configuration registers kept in retention.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Note:** Only the configuration of the activated modem,
 | 
			
		||||
    /// before going to sleep mode, is retained.
 | 
			
		||||
    /// The configuration of the other modes is lost and must be re-configured
 | 
			
		||||
    /// when exiting sleep mode.
 | 
			
		||||
    Warm = 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Startup {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Startup::Warm
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Sleep configuration.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_sleep`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_sleep`]: super::SubGhz::set_sleep
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct SleepCfg(u8);
 | 
			
		||||
 | 
			
		||||
impl SleepCfg {
 | 
			
		||||
    /// Create a new `SleepCfg` structure.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The defaults are a warm startup, with RTC wakeup enabled.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::SleepCfg;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const SLEEP_CFG: SleepCfg = SleepCfg::new();
 | 
			
		||||
    /// assert_eq!(SLEEP_CFG, SleepCfg::default());
 | 
			
		||||
    /// # assert_eq!(u8::from(SLEEP_CFG), 0b101);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> SleepCfg {
 | 
			
		||||
        SleepCfg(0).set_startup(Startup::Warm).set_rtc_wakeup_en(true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the startup mode.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{SleepCfg, Startup};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_startup(Startup::Cold);
 | 
			
		||||
    /// # assert_eq!(u8::from(SLEEP_CFG), 0b001);
 | 
			
		||||
    /// # assert_eq!(u8::from(SLEEP_CFG.set_startup(Startup::Warm)), 0b101);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn set_startup(mut self, startup: Startup) -> SleepCfg {
 | 
			
		||||
        if startup as u8 == 1 {
 | 
			
		||||
            self.0 |= 1 << 2
 | 
			
		||||
        } else {
 | 
			
		||||
            self.0 &= !(1 << 2)
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the RTC wakeup enable.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::SleepCfg;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_rtc_wakeup_en(false);
 | 
			
		||||
    /// # assert_eq!(u8::from(SLEEP_CFG), 0b100);
 | 
			
		||||
    /// # assert_eq!(u8::from(SLEEP_CFG.set_rtc_wakeup_en(true)), 0b101);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_rtc_wakeup_en returns a modified SleepCfg"]
 | 
			
		||||
    pub const fn set_rtc_wakeup_en(mut self, en: bool) -> SleepCfg {
 | 
			
		||||
        if en {
 | 
			
		||||
            self.0 |= 0b1
 | 
			
		||||
        } else {
 | 
			
		||||
            self.0 &= !0b1
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<SleepCfg> for u8 {
 | 
			
		||||
    fn from(sc: SleepCfg) -> Self {
 | 
			
		||||
        sc.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for SleepCfg {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
/// SMPS maximum drive capability.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_smps_drv`](super::SubGhz::set_smps_drv).
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum SmpsDrv {
 | 
			
		||||
    /// 20 mA
 | 
			
		||||
    Milli20 = 0x0,
 | 
			
		||||
    /// 40 mA
 | 
			
		||||
    Milli40 = 0x1,
 | 
			
		||||
    /// 60 mA
 | 
			
		||||
    Milli60 = 0x2,
 | 
			
		||||
    /// 100 mA (default)
 | 
			
		||||
    Milli100 = 0x3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SmpsDrv {
 | 
			
		||||
    /// Get the SMPS drive value as milliamps.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::SmpsDrv;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(SmpsDrv::Milli20.as_milliamps(), 20);
 | 
			
		||||
    /// assert_eq!(SmpsDrv::Milli40.as_milliamps(), 40);
 | 
			
		||||
    /// assert_eq!(SmpsDrv::Milli60.as_milliamps(), 60);
 | 
			
		||||
    /// assert_eq!(SmpsDrv::Milli100.as_milliamps(), 100);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_milliamps(&self) -> u8 {
 | 
			
		||||
        match self {
 | 
			
		||||
            SmpsDrv::Milli20 => 20,
 | 
			
		||||
            SmpsDrv::Milli40 => 40,
 | 
			
		||||
            SmpsDrv::Milli60 => 60,
 | 
			
		||||
            SmpsDrv::Milli100 => 100,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for SmpsDrv {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        SmpsDrv::Milli100
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
/// Clock in standby mode.
 | 
			
		||||
///
 | 
			
		||||
/// Used by [`set_standby`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_standby`]: super::SubGhz::set_standby
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum StandbyClk {
 | 
			
		||||
    /// RC 13 MHz used in standby mode.
 | 
			
		||||
    Rc = 0b0,
 | 
			
		||||
    /// HSE32 used in standby mode.
 | 
			
		||||
    Hse = 0b1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<StandbyClk> for u8 {
 | 
			
		||||
    fn from(sc: StandbyClk) -> Self {
 | 
			
		||||
        sc as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,184 +0,0 @@
 | 
			
		||||
use super::Status;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct LoRaStats;
 | 
			
		||||
 | 
			
		||||
impl LoRaStats {
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self {}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct FskStats;
 | 
			
		||||
 | 
			
		||||
impl FskStats {
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self {}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Packet statistics.
 | 
			
		||||
///
 | 
			
		||||
/// Returned by [`fsk_stats`] and [`lora_stats`].
 | 
			
		||||
///
 | 
			
		||||
/// [`fsk_stats`]: super::SubGhz::fsk_stats
 | 
			
		||||
/// [`lora_stats`]: super::SubGhz::lora_stats
 | 
			
		||||
#[derive(Eq, PartialEq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct Stats<ModType> {
 | 
			
		||||
    status: Status,
 | 
			
		||||
    pkt_rx: u16,
 | 
			
		||||
    pkt_crc: u16,
 | 
			
		||||
    pkt_len_or_hdr_err: u16,
 | 
			
		||||
    ty: ModType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<ModType> Stats<ModType> {
 | 
			
		||||
    const fn from_buf(buf: [u8; 7], ty: ModType) -> Stats<ModType> {
 | 
			
		||||
        Stats {
 | 
			
		||||
            status: Status::from_raw(buf[0]),
 | 
			
		||||
            pkt_rx: u16::from_be_bytes([buf[1], buf[2]]),
 | 
			
		||||
            pkt_crc: u16::from_be_bytes([buf[3], buf[4]]),
 | 
			
		||||
            pkt_len_or_hdr_err: u16::from_be_bytes([buf[5], buf[6]]),
 | 
			
		||||
            ty,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the radio status returned with the packet statistics.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CmdStatus, FskStats, Stats, StatusMode};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0];
 | 
			
		||||
    /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(stats.status().mode(), Ok(StatusMode::Rx));
 | 
			
		||||
    /// assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn status(&self) -> Status {
 | 
			
		||||
        self.status
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Number of packets received.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{FskStats, Stats};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 7] = [0x54, 0, 3, 0, 0, 0, 0];
 | 
			
		||||
    /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(stats.pkt_rx(), 3);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn pkt_rx(&self) -> u16 {
 | 
			
		||||
        self.pkt_rx
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Number of packets received with a payload CRC error
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{LoRaStats, Stats};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 1, 0, 0];
 | 
			
		||||
    /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(stats.pkt_crc(), 1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn pkt_crc(&self) -> u16 {
 | 
			
		||||
        self.pkt_crc
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Stats<FskStats> {
 | 
			
		||||
    /// Create a new FSK packet statistics structure from a raw buffer.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{FskStats, Stats};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0];
 | 
			
		||||
    /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw_fsk(buf: [u8; 7]) -> Stats<FskStats> {
 | 
			
		||||
        Self::from_buf(buf, FskStats::new())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Number of packets received with a payload length error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{FskStats, Stats};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1];
 | 
			
		||||
    /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(stats.pkt_len_err(), 1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn pkt_len_err(&self) -> u16 {
 | 
			
		||||
        self.pkt_len_or_hdr_err
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Stats<LoRaStats> {
 | 
			
		||||
    /// Create a new LoRa packet statistics structure from a raw buffer.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{LoRaStats, Stats};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0];
 | 
			
		||||
    /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw_lora(buf: [u8; 7]) -> Stats<LoRaStats> {
 | 
			
		||||
        Self::from_buf(buf, LoRaStats::new())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Number of packets received with a header CRC error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{LoRaStats, Stats};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1];
 | 
			
		||||
    /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
 | 
			
		||||
    /// assert_eq!(stats.pkt_hdr_err(), 1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn pkt_hdr_err(&self) -> u16 {
 | 
			
		||||
        self.pkt_len_or_hdr_err
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl core::fmt::Debug for Stats<FskStats> {
 | 
			
		||||
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
			
		||||
        f.debug_struct("Stats")
 | 
			
		||||
            .field("status", &self.status())
 | 
			
		||||
            .field("pkt_rx", &self.pkt_rx())
 | 
			
		||||
            .field("pkt_crc", &self.pkt_crc())
 | 
			
		||||
            .field("pkt_len_err", &self.pkt_len_err())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::super::{CmdStatus, LoRaStats, Stats, StatusMode};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn mixed() {
 | 
			
		||||
        let example_data_from_radio: [u8; 7] = [0x54, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
 | 
			
		||||
        let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
 | 
			
		||||
        assert_eq!(stats.status().mode(), Ok(StatusMode::Rx));
 | 
			
		||||
        assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable));
 | 
			
		||||
        assert_eq!(stats.pkt_rx(), 0x0102);
 | 
			
		||||
        assert_eq!(stats.pkt_crc(), 0x0304);
 | 
			
		||||
        assert_eq!(stats.pkt_hdr_err(), 0x0506);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,197 +0,0 @@
 | 
			
		||||
/// sub-GHz radio operating mode.
 | 
			
		||||
///
 | 
			
		||||
/// See `Get_Status` under section 5.8.5 "Communication status information commands"
 | 
			
		||||
/// in the reference manual.
 | 
			
		||||
///
 | 
			
		||||
/// This is returned by [`Status::mode`].
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum StatusMode {
 | 
			
		||||
    /// Standby mode with RC 13MHz.
 | 
			
		||||
    StandbyRc = 0x2,
 | 
			
		||||
    /// Standby mode with HSE32.
 | 
			
		||||
    StandbyHse = 0x3,
 | 
			
		||||
    /// Frequency Synthesis mode.
 | 
			
		||||
    Fs = 0x4,
 | 
			
		||||
    /// Receive mode.
 | 
			
		||||
    Rx = 0x5,
 | 
			
		||||
    /// Transmit mode.
 | 
			
		||||
    Tx = 0x6,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StatusMode {
 | 
			
		||||
    /// Create a new `StatusMode` from bits.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::StatusMode;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(StatusMode::from_raw(0x2), Ok(StatusMode::StandbyRc));
 | 
			
		||||
    /// assert_eq!(StatusMode::from_raw(0x3), Ok(StatusMode::StandbyHse));
 | 
			
		||||
    /// assert_eq!(StatusMode::from_raw(0x4), Ok(StatusMode::Fs));
 | 
			
		||||
    /// assert_eq!(StatusMode::from_raw(0x5), Ok(StatusMode::Rx));
 | 
			
		||||
    /// assert_eq!(StatusMode::from_raw(0x6), Ok(StatusMode::Tx));
 | 
			
		||||
    /// // Other values are reserved
 | 
			
		||||
    /// assert_eq!(StatusMode::from_raw(0), Err(0));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw(bits: u8) -> Result<Self, u8> {
 | 
			
		||||
        match bits {
 | 
			
		||||
            0x2 => Ok(StatusMode::StandbyRc),
 | 
			
		||||
            0x3 => Ok(StatusMode::StandbyHse),
 | 
			
		||||
            0x4 => Ok(StatusMode::Fs),
 | 
			
		||||
            0x5 => Ok(StatusMode::Rx),
 | 
			
		||||
            0x6 => Ok(StatusMode::Tx),
 | 
			
		||||
            _ => Err(bits),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Command status.
 | 
			
		||||
///
 | 
			
		||||
/// See `Get_Status` under section 5.8.5 "Communication status information commands"
 | 
			
		||||
/// in the reference manual.
 | 
			
		||||
///
 | 
			
		||||
/// This is returned by [`Status::cmd`].
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum CmdStatus {
 | 
			
		||||
    /// Data available to host.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Packet received successfully and data can be retrieved.
 | 
			
		||||
    Avaliable = 0x2,
 | 
			
		||||
    /// Command time out.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Command took too long to complete triggering a sub-GHz radio watchdog
 | 
			
		||||
    /// timeout.
 | 
			
		||||
    Timeout = 0x3,
 | 
			
		||||
    /// Command processing error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Invalid opcode or incorrect number of parameters.
 | 
			
		||||
    ProcessingError = 0x4,
 | 
			
		||||
    /// Command execution failure.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Command successfully received but cannot be executed at this time,
 | 
			
		||||
    /// requested operating mode cannot be entered or requested data cannot be
 | 
			
		||||
    /// sent.
 | 
			
		||||
    ExecutionFailure = 0x5,
 | 
			
		||||
    /// Transmit command completed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Current packet transmission completed.
 | 
			
		||||
    Complete = 0x6,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CmdStatus {
 | 
			
		||||
    /// Create a new `CmdStatus` from bits.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::CmdStatus;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(CmdStatus::from_raw(0x2), Ok(CmdStatus::Avaliable));
 | 
			
		||||
    /// assert_eq!(CmdStatus::from_raw(0x3), Ok(CmdStatus::Timeout));
 | 
			
		||||
    /// assert_eq!(CmdStatus::from_raw(0x4), Ok(CmdStatus::ProcessingError));
 | 
			
		||||
    /// assert_eq!(CmdStatus::from_raw(0x5), Ok(CmdStatus::ExecutionFailure));
 | 
			
		||||
    /// assert_eq!(CmdStatus::from_raw(0x6), Ok(CmdStatus::Complete));
 | 
			
		||||
    /// // Other values are reserved
 | 
			
		||||
    /// assert_eq!(CmdStatus::from_raw(0), Err(0));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw(bits: u8) -> Result<Self, u8> {
 | 
			
		||||
        match bits {
 | 
			
		||||
            0x2 => Ok(CmdStatus::Avaliable),
 | 
			
		||||
            0x3 => Ok(CmdStatus::Timeout),
 | 
			
		||||
            0x4 => Ok(CmdStatus::ProcessingError),
 | 
			
		||||
            0x5 => Ok(CmdStatus::ExecutionFailure),
 | 
			
		||||
            0x6 => Ok(CmdStatus::Complete),
 | 
			
		||||
            _ => Err(bits),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Radio status.
 | 
			
		||||
///
 | 
			
		||||
/// This is returned by [`status`].
 | 
			
		||||
///
 | 
			
		||||
/// [`status`]: super::SubGhz::status
 | 
			
		||||
#[derive(PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
pub struct Status(u8);
 | 
			
		||||
 | 
			
		||||
impl From<u8> for Status {
 | 
			
		||||
    fn from(x: u8) -> Self {
 | 
			
		||||
        Status(x)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl From<Status> for u8 {
 | 
			
		||||
    fn from(x: Status) -> Self {
 | 
			
		||||
        x.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Status {
 | 
			
		||||
    /// Create a new `Status` from a raw `u8` value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `Status::from(u8)`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CmdStatus, Status, StatusMode};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const STATUS: Status = Status::from_raw(0x54_u8);
 | 
			
		||||
    /// assert_eq!(STATUS.mode(), Ok(StatusMode::Rx));
 | 
			
		||||
    /// assert_eq!(STATUS.cmd(), Ok(CmdStatus::Avaliable));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw(value: u8) -> Status {
 | 
			
		||||
        Status(value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// sub-GHz radio operating mode.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{Status, StatusMode};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let status: Status = 0xACu8.into();
 | 
			
		||||
    /// assert_eq!(status.mode(), Ok(StatusMode::StandbyRc));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn mode(&self) -> Result<StatusMode, u8> {
 | 
			
		||||
        StatusMode::from_raw((self.0 >> 4) & 0b111)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Command status.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This method frequently returns reserved values such as `Err(1)`.
 | 
			
		||||
    /// ST support has confirmed that this is normal and should be ignored.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{CmdStatus, Status};
 | 
			
		||||
    ///
 | 
			
		||||
    /// let status: Status = 0xACu8.into();
 | 
			
		||||
    /// assert_eq!(status.cmd(), Ok(CmdStatus::Complete));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn cmd(&self) -> Result<CmdStatus, u8> {
 | 
			
		||||
        CmdStatus::from_raw((self.0 >> 1) & 0b111)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl core::fmt::Debug for Status {
 | 
			
		||||
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
			
		||||
        f.debug_struct("Status")
 | 
			
		||||
            .field("mode", &self.mode())
 | 
			
		||||
            .field("cmd", &self.cmd())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "defmt")]
 | 
			
		||||
impl defmt::Format for Status {
 | 
			
		||||
    fn format(&self, fmt: defmt::Formatter) {
 | 
			
		||||
        defmt::write!(fmt, "Status {{ mode: {}, cmd: {} }}", self.mode(), self.cmd())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,170 +0,0 @@
 | 
			
		||||
use super::Timeout;
 | 
			
		||||
 | 
			
		||||
/// TCXO trim.
 | 
			
		||||
///
 | 
			
		||||
/// **Note:** To use V<sub>DDTCXO</sub>, the V<sub>DDRF</sub> supply must be at
 | 
			
		||||
/// least + 200 mV higher than the selected `TcxoTrim` voltage level.
 | 
			
		||||
///
 | 
			
		||||
/// Used by [`TcxoMode`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum TcxoTrim {
 | 
			
		||||
    /// 1.6V
 | 
			
		||||
    Volts1pt6 = 0x0,
 | 
			
		||||
    /// 1.7V
 | 
			
		||||
    Volts1pt7 = 0x1,
 | 
			
		||||
    /// 1.8V
 | 
			
		||||
    Volts1pt8 = 0x2,
 | 
			
		||||
    /// 2.2V
 | 
			
		||||
    Volts2pt2 = 0x3,
 | 
			
		||||
    /// 2.4V
 | 
			
		||||
    Volts2pt4 = 0x4,
 | 
			
		||||
    /// 2.7V
 | 
			
		||||
    Volts2pt7 = 0x5,
 | 
			
		||||
    /// 3.0V
 | 
			
		||||
    Volts3pt0 = 0x6,
 | 
			
		||||
    /// 3.3V
 | 
			
		||||
    Volts3pt3 = 0x7,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl core::fmt::Display for TcxoTrim {
 | 
			
		||||
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            TcxoTrim::Volts1pt6 => write!(f, "1.6V"),
 | 
			
		||||
            TcxoTrim::Volts1pt7 => write!(f, "1.7V"),
 | 
			
		||||
            TcxoTrim::Volts1pt8 => write!(f, "1.8V"),
 | 
			
		||||
            TcxoTrim::Volts2pt2 => write!(f, "2.2V"),
 | 
			
		||||
            TcxoTrim::Volts2pt4 => write!(f, "2.4V"),
 | 
			
		||||
            TcxoTrim::Volts2pt7 => write!(f, "2.7V"),
 | 
			
		||||
            TcxoTrim::Volts3pt0 => write!(f, "3.0V"),
 | 
			
		||||
            TcxoTrim::Volts3pt3 => write!(f, "3.3V"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TcxoTrim {
 | 
			
		||||
    /// Get the value of the TXCO trim in millivolts.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::TcxoTrim;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts1pt6.as_millivolts(), 1600);
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts1pt7.as_millivolts(), 1700);
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts1pt8.as_millivolts(), 1800);
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts2pt2.as_millivolts(), 2200);
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts2pt4.as_millivolts(), 2400);
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts2pt7.as_millivolts(), 2700);
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts3pt0.as_millivolts(), 3000);
 | 
			
		||||
    /// assert_eq!(TcxoTrim::Volts3pt3.as_millivolts(), 3300);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_millivolts(&self) -> u16 {
 | 
			
		||||
        match self {
 | 
			
		||||
            TcxoTrim::Volts1pt6 => 1600,
 | 
			
		||||
            TcxoTrim::Volts1pt7 => 1700,
 | 
			
		||||
            TcxoTrim::Volts1pt8 => 1800,
 | 
			
		||||
            TcxoTrim::Volts2pt2 => 2200,
 | 
			
		||||
            TcxoTrim::Volts2pt4 => 2400,
 | 
			
		||||
            TcxoTrim::Volts2pt7 => 2700,
 | 
			
		||||
            TcxoTrim::Volts3pt0 => 3000,
 | 
			
		||||
            TcxoTrim::Volts3pt3 => 3300,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// TCXO trim and HSE32 ready timeout.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_tcxo_mode`].
 | 
			
		||||
///
 | 
			
		||||
/// [`set_tcxo_mode`]: super::SubGhz::set_tcxo_mode
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct TcxoMode {
 | 
			
		||||
    buf: [u8; 5],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TcxoMode {
 | 
			
		||||
    /// Create a new `TcxoMode` struct.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::TcxoMode;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TCXO_MODE: TcxoMode = TcxoMode::new();
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> TcxoMode {
 | 
			
		||||
        TcxoMode {
 | 
			
		||||
            buf: [super::OpCode::SetTcxoMode as u8, 0x00, 0x00, 0x00, 0x00],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the TCXO trim.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Note:** To use V<sub>DDTCXO</sub>, the V<sub>DDRF</sub> supply must be
 | 
			
		||||
    /// at least + 200 mV higher than the selected `TcxoTrim` voltage level.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_txco_trim(TcxoTrim::Volts1pt6);
 | 
			
		||||
    /// # assert_eq!(TCXO_MODE.as_slice()[1], 0x00);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_txco_trim returns a modified TcxoMode"]
 | 
			
		||||
    pub const fn set_txco_trim(mut self, tcxo_trim: TcxoTrim) -> TcxoMode {
 | 
			
		||||
        self.buf[1] = tcxo_trim as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the ready timeout duration.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{TcxoMode, Timeout};
 | 
			
		||||
    ///
 | 
			
		||||
    /// // 15.625 ms timeout
 | 
			
		||||
    /// const TIMEOUT: Timeout = Timeout::from_duration_sat(Duration::from_millis(15_625));
 | 
			
		||||
    /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_timeout(TIMEOUT);
 | 
			
		||||
    /// # assert_eq!(TCXO_MODE.as_slice()[2], 0x0F);
 | 
			
		||||
    /// # assert_eq!(TCXO_MODE.as_slice()[3], 0x42);
 | 
			
		||||
    /// # assert_eq!(TCXO_MODE.as_slice()[4], 0x40);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_timeout returns a modified TcxoMode"]
 | 
			
		||||
    pub const fn set_timeout(mut self, timeout: Timeout) -> TcxoMode {
 | 
			
		||||
        let timeout_bits: u32 = timeout.into_bits();
 | 
			
		||||
        self.buf[2] = ((timeout_bits >> 16) & 0xFF) as u8;
 | 
			
		||||
        self.buf[3] = ((timeout_bits >> 8) & 0xFF) as u8;
 | 
			
		||||
        self.buf[4] = (timeout_bits & 0xFF) as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim, Timeout};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TCXO_MODE: TcxoMode = TcxoMode::new()
 | 
			
		||||
    ///     .set_txco_trim(TcxoTrim::Volts1pt7)
 | 
			
		||||
    ///     .set_timeout(Timeout::from_raw(0x123456));
 | 
			
		||||
    /// assert_eq!(TCXO_MODE.as_slice(), &[0x97, 0x1, 0x12, 0x34, 0x56]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for TcxoMode {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,492 +0,0 @@
 | 
			
		||||
use core::time::Duration;
 | 
			
		||||
 | 
			
		||||
use super::ValueError;
 | 
			
		||||
 | 
			
		||||
const fn abs_diff(a: u64, b: u64) -> u64 {
 | 
			
		||||
    if a > b {
 | 
			
		||||
        a - b
 | 
			
		||||
    } else {
 | 
			
		||||
        b - a
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Timeout argument.
 | 
			
		||||
///
 | 
			
		||||
/// This is used by:
 | 
			
		||||
/// * [`set_rx`]
 | 
			
		||||
/// * [`set_tx`]
 | 
			
		||||
/// * [`TcxoMode`]
 | 
			
		||||
///
 | 
			
		||||
/// Each timeout has 3 bytes, with a resolution of 15.625µs per bit, giving a
 | 
			
		||||
/// range of 0s to 262.143984375s.
 | 
			
		||||
///
 | 
			
		||||
/// [`set_rx`]: super::SubGhz::set_rx
 | 
			
		||||
/// [`set_tx`]: super::SubGhz::set_tx
 | 
			
		||||
/// [`TcxoMode`]: super::TcxoMode
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct Timeout {
 | 
			
		||||
    bits: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Timeout {
 | 
			
		||||
    const BITS_PER_MILLI: u32 = 64; // 1e-3 / 15.625e-6
 | 
			
		||||
    const BITS_PER_SEC: u32 = 64_000; // 1 / 15.625e-6
 | 
			
		||||
 | 
			
		||||
    /// Disable the timeout (0s timeout).
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TIMEOUT: Timeout = Timeout::DISABLED;
 | 
			
		||||
    /// assert_eq!(TIMEOUT.as_duration(), Duration::from_secs(0));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const DISABLED: Timeout = Timeout { bits: 0x0 };
 | 
			
		||||
 | 
			
		||||
    /// Minimum timeout, 15.625µs.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TIMEOUT: Timeout = Timeout::MIN;
 | 
			
		||||
    /// assert_eq!(TIMEOUT.into_bits(), 1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const MIN: Timeout = Timeout { bits: 1 };
 | 
			
		||||
 | 
			
		||||
    /// Maximum timeout, 262.143984375s.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TIMEOUT: Timeout = Timeout::MAX;
 | 
			
		||||
    /// assert_eq!(TIMEOUT.as_duration(), Duration::from_nanos(262_143_984_375));
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const MAX: Timeout = Timeout { bits: 0x00FF_FFFF };
 | 
			
		||||
 | 
			
		||||
    /// Timeout resolution in nanoseconds, 15.625µs.
 | 
			
		||||
    pub const RESOLUTION_NANOS: u16 = 15_625;
 | 
			
		||||
 | 
			
		||||
    /// Timeout resolution, 15.625µs.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::RESOLUTION.as_nanos(),
 | 
			
		||||
    ///     Timeout::RESOLUTION_NANOS as u128
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const RESOLUTION: Duration = Duration::from_nanos(Self::RESOLUTION_NANOS as u64);
 | 
			
		||||
 | 
			
		||||
    /// Create a new timeout from a [`Duration`].
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will return the nearest timeout value possible, or a
 | 
			
		||||
    /// [`ValueError`] if the value is out of bounds.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout
 | 
			
		||||
    /// construction.
 | 
			
		||||
    /// This is not _that_ useful right now, it is simply future proofing for a
 | 
			
		||||
    /// time when `Result::unwrap` is available for `const fn`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Value within bounds:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{Timeout, ValueError};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const MIN: Duration = Timeout::RESOLUTION;
 | 
			
		||||
    /// assert_eq!(Timeout::from_duration(MIN).unwrap(), Timeout::MIN);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// Value too low:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{Timeout, ValueError};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const LOWER_LIMIT_NANOS: u128 = 7813;
 | 
			
		||||
    /// const TOO_LOW_NANOS: u128 = LOWER_LIMIT_NANOS - 1;
 | 
			
		||||
    /// const TOO_LOW_DURATION: Duration = Duration::from_nanos(TOO_LOW_NANOS as u64);
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::from_duration(TOO_LOW_DURATION),
 | 
			
		||||
    ///     Err(ValueError::too_low(TOO_LOW_NANOS, LOWER_LIMIT_NANOS))
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// Value too high:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{Timeout, ValueError};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const UPPER_LIMIT_NANOS: u128 = Timeout::MAX.as_nanos() as u128 + 7812;
 | 
			
		||||
    /// const TOO_HIGH_NANOS: u128 = UPPER_LIMIT_NANOS + 1;
 | 
			
		||||
    /// const TOO_HIGH_DURATION: Duration = Duration::from_nanos(TOO_HIGH_NANOS as u64);
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::from_duration(TOO_HIGH_DURATION),
 | 
			
		||||
    ///     Err(ValueError::too_high(TOO_HIGH_NANOS, UPPER_LIMIT_NANOS))
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_duration(duration: Duration) -> Result<Timeout, ValueError<u128>> {
 | 
			
		||||
        // at the time of development many methods in
 | 
			
		||||
        // `core::Duration` were not `const fn`, which leads to the hacks
 | 
			
		||||
        // you see here.
 | 
			
		||||
        let nanos: u128 = duration.as_nanos();
 | 
			
		||||
        const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128 + (Timeout::RESOLUTION_NANOS as u128) / 2;
 | 
			
		||||
        const LOWER_LIMIT: u128 = (((Timeout::RESOLUTION_NANOS as u128) + 1) / 2) as u128;
 | 
			
		||||
 | 
			
		||||
        if nanos > UPPER_LIMIT {
 | 
			
		||||
            Err(ValueError::too_high(nanos, UPPER_LIMIT))
 | 
			
		||||
        } else if nanos < LOWER_LIMIT {
 | 
			
		||||
            Err(ValueError::too_low(nanos, LOWER_LIMIT))
 | 
			
		||||
        } else {
 | 
			
		||||
            // safe to truncate here because of previous bounds check.
 | 
			
		||||
            let duration_nanos: u64 = nanos as u64;
 | 
			
		||||
 | 
			
		||||
            let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64);
 | 
			
		||||
            let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64);
 | 
			
		||||
 | 
			
		||||
            let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32);
 | 
			
		||||
            let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32);
 | 
			
		||||
 | 
			
		||||
            let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos);
 | 
			
		||||
            let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos);
 | 
			
		||||
 | 
			
		||||
            if error_ceil < error_floor {
 | 
			
		||||
                Ok(timeout_ceil)
 | 
			
		||||
            } else {
 | 
			
		||||
                Ok(timeout_floor)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new timeout from a [`Duration`].
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will return the nearest timeout value possible, saturating at the
 | 
			
		||||
    /// limits.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is an expensive function to call outside of `const` contexts.
 | 
			
		||||
    /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout
 | 
			
		||||
    /// construction.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const DURATION_MAX_NS: u64 = 262_143_984_376;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::from_duration_sat(Duration::from_millis(0)),
 | 
			
		||||
    ///     Timeout::MIN
 | 
			
		||||
    /// );
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::from_duration_sat(Duration::from_nanos(DURATION_MAX_NS)),
 | 
			
		||||
    ///     Timeout::MAX
 | 
			
		||||
    /// );
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::from_duration_sat(Timeout::RESOLUTION).into_bits(),
 | 
			
		||||
    ///     1
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_duration_sat(duration: Duration) -> Timeout {
 | 
			
		||||
        // at the time of development many methods in
 | 
			
		||||
        // `core::Duration` were not `const fn`, which leads to the hacks
 | 
			
		||||
        // you see here.
 | 
			
		||||
        let nanos: u128 = duration.as_nanos();
 | 
			
		||||
        const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128;
 | 
			
		||||
 | 
			
		||||
        if nanos > UPPER_LIMIT {
 | 
			
		||||
            Timeout::MAX
 | 
			
		||||
        } else if nanos < (Timeout::RESOLUTION_NANOS as u128) {
 | 
			
		||||
            Timeout::from_raw(1)
 | 
			
		||||
        } else {
 | 
			
		||||
            // safe to truncate here because of previous bounds check.
 | 
			
		||||
            let duration_nanos: u64 = duration.as_nanos() as u64;
 | 
			
		||||
 | 
			
		||||
            let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64);
 | 
			
		||||
            let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64);
 | 
			
		||||
 | 
			
		||||
            let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32);
 | 
			
		||||
            let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32);
 | 
			
		||||
 | 
			
		||||
            let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos);
 | 
			
		||||
            let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos);
 | 
			
		||||
 | 
			
		||||
            if error_ceil < error_floor {
 | 
			
		||||
                timeout_ceil
 | 
			
		||||
            } else {
 | 
			
		||||
                timeout_floor
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new timeout from a milliseconds value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will round towards zero and saturate at the limits.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the preferred method to call when you need to generate a
 | 
			
		||||
    /// timeout value at runtime.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::from_millis_sat(0), Timeout::MIN);
 | 
			
		||||
    /// assert_eq!(Timeout::from_millis_sat(262_144), Timeout::MAX);
 | 
			
		||||
    /// assert_eq!(Timeout::from_millis_sat(1).into_bits(), 64);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_millis_sat(millis: u32) -> Timeout {
 | 
			
		||||
        if millis == 0 {
 | 
			
		||||
            Timeout::MIN
 | 
			
		||||
        } else if millis >= 262_144 {
 | 
			
		||||
            Timeout::MAX
 | 
			
		||||
        } else {
 | 
			
		||||
            Timeout::from_raw(millis * Self::BITS_PER_MILLI)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a timeout from raw bits, where each bit has the resolution of
 | 
			
		||||
    /// [`Timeout::RESOLUTION`].
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Note:** Only the first 24 bits of the `u32` are used, the `bits`
 | 
			
		||||
    /// argument will be masked.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(u32::MAX), Timeout::MAX);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(0x00_FF_FF_FF), Timeout::MAX);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(0), Timeout::DISABLED);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn from_raw(bits: u32) -> Timeout {
 | 
			
		||||
        Timeout {
 | 
			
		||||
            bits: bits & 0x00FF_FFFF,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the timeout as nanoseconds.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::MAX.as_nanos(), 262_143_984_375);
 | 
			
		||||
    /// assert_eq!(Timeout::DISABLED.as_nanos(), 0);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).as_nanos(), 15_625);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(64_000).as_nanos(), 1_000_000_000);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_nanos(&self) -> u64 {
 | 
			
		||||
        (self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the timeout as microseconds, rounding towards zero.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::MAX.as_micros(), 262_143_984);
 | 
			
		||||
    /// assert_eq!(Timeout::DISABLED.as_micros(), 0);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).as_micros(), 15);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(64_000).as_micros(), 1_000_000);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_micros(&self) -> u32 {
 | 
			
		||||
        (self.as_nanos() / 1_000) as u32
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the timeout as milliseconds, rounding towards zero.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::MAX.as_millis(), 262_143);
 | 
			
		||||
    /// assert_eq!(Timeout::DISABLED.as_millis(), 0);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).as_millis(), 0);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(64_000).as_millis(), 1_000);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_millis(&self) -> u32 {
 | 
			
		||||
        self.into_bits() / Self::BITS_PER_MILLI
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the timeout as seconds, rounding towards zero.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::MAX.as_secs(), 262);
 | 
			
		||||
    /// assert_eq!(Timeout::DISABLED.as_secs(), 0);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).as_secs(), 0);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(64_000).as_secs(), 1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_secs(&self) -> u16 {
 | 
			
		||||
        (self.into_bits() / Self::BITS_PER_SEC) as u16
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the timeout as a [`Duration`].
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use core::time::Duration;
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::MAX.as_duration(),
 | 
			
		||||
    ///     Duration::from_nanos(262_143_984_375)
 | 
			
		||||
    /// );
 | 
			
		||||
    /// assert_eq!(Timeout::DISABLED.as_duration(), Duration::from_nanos(0));
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_duration(&self) -> Duration {
 | 
			
		||||
        Duration::from_nanos((self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the bit value for the timeout.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(u32::MAX).into_bits(), 0x00FF_FFFF);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).into_bits(), 1);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn into_bits(self) -> u32 {
 | 
			
		||||
        self.bits
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the byte value for the timeout.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(u32::MAX).as_bytes(), [0xFF, 0xFF, 0xFF]);
 | 
			
		||||
    /// assert_eq!(Timeout::from_raw(1).as_bytes(), [0, 0, 1]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_bytes(self) -> [u8; 3] {
 | 
			
		||||
        [
 | 
			
		||||
            ((self.bits >> 16) & 0xFF) as u8,
 | 
			
		||||
            ((self.bits >> 8) & 0xFF) as u8,
 | 
			
		||||
            (self.bits & 0xFF) as u8,
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Saturating timeout addition.  Computes `self + rhs`, saturating at the
 | 
			
		||||
    /// numeric bounds instead of overflowing.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::Timeout;
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::from_raw(0xFF_FF_F0).saturating_add(Timeout::from_raw(0xFF)),
 | 
			
		||||
    ///     Timeout::from_raw(0xFF_FF_FF)
 | 
			
		||||
    /// );
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     Timeout::from_raw(100).saturating_add(Timeout::from_raw(23)),
 | 
			
		||||
    ///     Timeout::from_raw(123)
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "saturating_add returns a new Timeout"]
 | 
			
		||||
    pub const fn saturating_add(self, rhs: Self) -> Self {
 | 
			
		||||
        // TODO: use core::cmp::min when it is const
 | 
			
		||||
        let bits: u32 = self.bits.saturating_add(rhs.bits);
 | 
			
		||||
        if bits > Self::MAX.bits {
 | 
			
		||||
            Self::MAX
 | 
			
		||||
        } else {
 | 
			
		||||
            Self { bits }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Timeout> for Duration {
 | 
			
		||||
    fn from(to: Timeout) -> Self {
 | 
			
		||||
        to.as_duration()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Timeout> for [u8; 3] {
 | 
			
		||||
    fn from(to: Timeout) -> Self {
 | 
			
		||||
        to.as_bytes()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "time")]
 | 
			
		||||
impl From<Timeout> for embassy_time::Duration {
 | 
			
		||||
    fn from(to: Timeout) -> Self {
 | 
			
		||||
        embassy_time::Duration::from_micros(to.as_micros().into())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use core::time::Duration;
 | 
			
		||||
 | 
			
		||||
    use super::{Timeout, ValueError};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn saturate() {
 | 
			
		||||
        assert_eq!(Timeout::from_duration_sat(Duration::from_secs(u64::MAX)), Timeout::MAX);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn rounding() {
 | 
			
		||||
        const NANO1: Duration = Duration::from_nanos(1);
 | 
			
		||||
        let res_sub_1_ns: Duration = Timeout::RESOLUTION - NANO1;
 | 
			
		||||
        let res_add_1_ns: Duration = Timeout::RESOLUTION + NANO1;
 | 
			
		||||
        assert_eq!(Timeout::from_duration_sat(res_sub_1_ns).into_bits(), 1);
 | 
			
		||||
        assert_eq!(Timeout::from_duration_sat(res_add_1_ns).into_bits(), 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn lower_limit() {
 | 
			
		||||
        let low: Duration = (Timeout::RESOLUTION + Duration::from_nanos(1)) / 2;
 | 
			
		||||
        assert_eq!(Timeout::from_duration(low), Ok(Timeout::from_raw(1)));
 | 
			
		||||
 | 
			
		||||
        let too_low: Duration = low - Duration::from_nanos(1);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Timeout::from_duration(too_low),
 | 
			
		||||
            Err(ValueError::too_low(too_low.as_nanos(), low.as_nanos()))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn upper_limit() {
 | 
			
		||||
        let high: Duration = Timeout::MAX.as_duration() + Timeout::RESOLUTION / 2;
 | 
			
		||||
        assert_eq!(Timeout::from_duration(high), Ok(Timeout::from_raw(0xFFFFFF)));
 | 
			
		||||
 | 
			
		||||
        let too_high: Duration = high + Duration::from_nanos(1);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Timeout::from_duration(too_high),
 | 
			
		||||
            Err(ValueError::too_high(too_high.as_nanos(), high.as_nanos()))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,192 +0,0 @@
 | 
			
		||||
/// Power amplifier ramp time for FSK, MSK, and LoRa modulation.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_ramp_time`][`super::TxParams::set_ramp_time`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
pub enum RampTime {
 | 
			
		||||
    /// 10µs
 | 
			
		||||
    Micros10 = 0x00,
 | 
			
		||||
    /// 20µs
 | 
			
		||||
    Micros20 = 0x01,
 | 
			
		||||
    /// 40µs
 | 
			
		||||
    Micros40 = 0x02,
 | 
			
		||||
    /// 80µs
 | 
			
		||||
    Micros80 = 0x03,
 | 
			
		||||
    /// 200µs
 | 
			
		||||
    Micros200 = 0x04,
 | 
			
		||||
    /// 800µs
 | 
			
		||||
    Micros800 = 0x05,
 | 
			
		||||
    /// 1.7ms
 | 
			
		||||
    Micros1700 = 0x06,
 | 
			
		||||
    /// 3.4ms
 | 
			
		||||
    Micros3400 = 0x07,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<RampTime> for u8 {
 | 
			
		||||
    fn from(rt: RampTime) -> Self {
 | 
			
		||||
        rt as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<RampTime> for core::time::Duration {
 | 
			
		||||
    fn from(rt: RampTime) -> Self {
 | 
			
		||||
        match rt {
 | 
			
		||||
            RampTime::Micros10 => core::time::Duration::from_micros(10),
 | 
			
		||||
            RampTime::Micros20 => core::time::Duration::from_micros(20),
 | 
			
		||||
            RampTime::Micros40 => core::time::Duration::from_micros(40),
 | 
			
		||||
            RampTime::Micros80 => core::time::Duration::from_micros(80),
 | 
			
		||||
            RampTime::Micros200 => core::time::Duration::from_micros(200),
 | 
			
		||||
            RampTime::Micros800 => core::time::Duration::from_micros(800),
 | 
			
		||||
            RampTime::Micros1700 => core::time::Duration::from_micros(1700),
 | 
			
		||||
            RampTime::Micros3400 => core::time::Duration::from_micros(3400),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "time")]
 | 
			
		||||
impl From<RampTime> for embassy_time::Duration {
 | 
			
		||||
    fn from(rt: RampTime) -> Self {
 | 
			
		||||
        match rt {
 | 
			
		||||
            RampTime::Micros10 => embassy_time::Duration::from_micros(10),
 | 
			
		||||
            RampTime::Micros20 => embassy_time::Duration::from_micros(20),
 | 
			
		||||
            RampTime::Micros40 => embassy_time::Duration::from_micros(40),
 | 
			
		||||
            RampTime::Micros80 => embassy_time::Duration::from_micros(80),
 | 
			
		||||
            RampTime::Micros200 => embassy_time::Duration::from_micros(200),
 | 
			
		||||
            RampTime::Micros800 => embassy_time::Duration::from_micros(800),
 | 
			
		||||
            RampTime::Micros1700 => embassy_time::Duration::from_micros(1700),
 | 
			
		||||
            RampTime::Micros3400 => embassy_time::Duration::from_micros(3400),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// Transmit parameters, output power and power amplifier ramp up time.
 | 
			
		||||
///
 | 
			
		||||
/// Argument of [`set_tx_params`][`super::SubGhz::set_tx_params`].
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct TxParams {
 | 
			
		||||
    buf: [u8; 3],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TxParams {
 | 
			
		||||
    /// Optimal power setting for +15dBm output power with the low-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`PaConfig::LP_15`](super::PaConfig::LP_15).
 | 
			
		||||
    pub const LP_15: TxParams = TxParams::new().set_power(0x0E);
 | 
			
		||||
 | 
			
		||||
    /// Optimal power setting for +14dBm output power with the low-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`PaConfig::LP_14`](super::PaConfig::LP_14).
 | 
			
		||||
    pub const LP_14: TxParams = TxParams::new().set_power(0x0E);
 | 
			
		||||
 | 
			
		||||
    /// Optimal power setting for +10dBm output power with the low-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with [`PaConfig::LP_10`](super::PaConfig::LP_10).
 | 
			
		||||
    pub const LP_10: TxParams = TxParams::new().set_power(0x0D);
 | 
			
		||||
 | 
			
		||||
    /// Optimal power setting for the high-power PA.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This must be used with one of:
 | 
			
		||||
    ///
 | 
			
		||||
    /// * [`PaConfig::HP_22`](super::PaConfig::HP_22)
 | 
			
		||||
    /// * [`PaConfig::HP_20`](super::PaConfig::HP_20)
 | 
			
		||||
    /// * [`PaConfig::HP_17`](super::PaConfig::HP_17)
 | 
			
		||||
    /// * [`PaConfig::HP_14`](super::PaConfig::HP_14)
 | 
			
		||||
    pub const HP: TxParams = TxParams::new().set_power(0x16);
 | 
			
		||||
 | 
			
		||||
    /// Create a new `TxParams` struct.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is the same as `default`, but in a `const` function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::TxParams;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TX_PARAMS: TxParams = TxParams::new();
 | 
			
		||||
    /// assert_eq!(TX_PARAMS, TxParams::default());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn new() -> TxParams {
 | 
			
		||||
        TxParams {
 | 
			
		||||
            buf: [super::OpCode::SetTxParams as u8, 0x00, 0x00],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the output power.
 | 
			
		||||
    ///
 | 
			
		||||
    /// For low power selected in [`set_pa_config`]:
 | 
			
		||||
    ///
 | 
			
		||||
    /// * 0x0E: +14 dB
 | 
			
		||||
    /// * ...
 | 
			
		||||
    /// * 0x00: 0 dB
 | 
			
		||||
    /// * ...
 | 
			
		||||
    /// * 0xEF: -17 dB
 | 
			
		||||
    /// * Others: reserved
 | 
			
		||||
    ///
 | 
			
		||||
    /// For high power selected in [`set_pa_config`]:
 | 
			
		||||
    ///
 | 
			
		||||
    /// * 0x16: +22 dB
 | 
			
		||||
    /// * ...
 | 
			
		||||
    /// * 0x00: 0 dB
 | 
			
		||||
    /// * ...
 | 
			
		||||
    /// * 0xF7: -9 dB
 | 
			
		||||
    /// * Others: reserved
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Set the output power to 0 dB.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{RampTime, TxParams};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TX_PARAMS: TxParams = TxParams::new().set_power(0x00);
 | 
			
		||||
    /// # assert_eq!(TX_PARAMS.as_slice()[1], 0x00);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// [`set_pa_config`]: super::SubGhz::set_pa_config
 | 
			
		||||
    #[must_use = "set_power returns a modified TxParams"]
 | 
			
		||||
    pub const fn set_power(mut self, power: u8) -> TxParams {
 | 
			
		||||
        self.buf[1] = power;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the Power amplifier ramp time for FSK, MSK, and LoRa modulation.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// Set the ramp time to 200 microseconds.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{RampTime, TxParams};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TX_PARAMS: TxParams = TxParams::new().set_ramp_time(RampTime::Micros200);
 | 
			
		||||
    /// # assert_eq!(TX_PARAMS.as_slice()[2], 0x04);
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[must_use = "set_ramp_time returns a modified TxParams"]
 | 
			
		||||
    pub const fn set_ramp_time(mut self, rt: RampTime) -> TxParams {
 | 
			
		||||
        self.buf[2] = rt as u8;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Extracts a slice containing the packet.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::{RampTime, TxParams};
 | 
			
		||||
    ///
 | 
			
		||||
    /// const TX_PARAMS: TxParams = TxParams::new()
 | 
			
		||||
    ///     .set_ramp_time(RampTime::Micros80)
 | 
			
		||||
    ///     .set_power(0x0E);
 | 
			
		||||
    /// assert_eq!(TX_PARAMS.as_slice(), &[0x8E, 0x0E, 0x03]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn as_slice(&self) -> &[u8] {
 | 
			
		||||
        &self.buf
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for TxParams {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,129 +0,0 @@
 | 
			
		||||
/// Error for a value that is out-of-bounds.
 | 
			
		||||
///
 | 
			
		||||
/// Used by [`Timeout::from_duration`].
 | 
			
		||||
///
 | 
			
		||||
/// [`Timeout::from_duration`]: super::Timeout::from_duration
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct ValueError<T> {
 | 
			
		||||
    value: T,
 | 
			
		||||
    limit: T,
 | 
			
		||||
    over: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> ValueError<T> {
 | 
			
		||||
    /// Create a new `ValueError` for a value that exceeded an upper bound.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Unfortunately panic is not available in `const fn`, so there are no
 | 
			
		||||
    /// guarantees on the value being greater than the limit.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::ValueError;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
 | 
			
		||||
    /// assert!(ERROR.over());
 | 
			
		||||
    /// assert!(!ERROR.under());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn too_high(value: T, limit: T) -> ValueError<T> {
 | 
			
		||||
        ValueError {
 | 
			
		||||
            value,
 | 
			
		||||
            limit,
 | 
			
		||||
            over: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new `ValueError` for a value that exceeded a lower bound.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Unfortunately panic is not available in `const fn`, so there are no
 | 
			
		||||
    /// guarantees on the value being less than the limit.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::ValueError;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const ERROR: ValueError<u8> = ValueError::too_low(200u8, 201u8);
 | 
			
		||||
    /// assert!(ERROR.under());
 | 
			
		||||
    /// assert!(!ERROR.over());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn too_low(value: T, limit: T) -> ValueError<T> {
 | 
			
		||||
        ValueError {
 | 
			
		||||
            value,
 | 
			
		||||
            limit,
 | 
			
		||||
            over: false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the value that caused the error.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::ValueError;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
 | 
			
		||||
    /// assert_eq!(ERROR.value(), &101u8);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn value(&self) -> &T {
 | 
			
		||||
        &self.value
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the limit for the value.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::ValueError;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
 | 
			
		||||
    /// assert_eq!(ERROR.limit(), &100u8);
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn limit(&self) -> &T {
 | 
			
		||||
        &self.limit
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if the value was over the limit.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::ValueError;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
 | 
			
		||||
    /// assert!(ERROR.over());
 | 
			
		||||
    /// assert!(!ERROR.under());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn over(&self) -> bool {
 | 
			
		||||
        self.over
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if the value was under the limit.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use stm32wlxx_hal::subghz::ValueError;
 | 
			
		||||
    ///
 | 
			
		||||
    /// const ERROR: ValueError<u8> = ValueError::too_low(200u8, 201u8);
 | 
			
		||||
    /// assert!(ERROR.under());
 | 
			
		||||
    /// assert!(!ERROR.over());
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub const fn under(&self) -> bool {
 | 
			
		||||
        !self.over
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> core::fmt::Display for ValueError<T>
 | 
			
		||||
where
 | 
			
		||||
    T: core::fmt::Display,
 | 
			
		||||
{
 | 
			
		||||
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
			
		||||
        if self.over {
 | 
			
		||||
            write!(f, "Value is too high {} > {}", self.value, self.limit)
 | 
			
		||||
        } else {
 | 
			
		||||
            write!(f, "Value is too low {} < {}", self.value, self.limit)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm
 | 
			
		||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
 | 
			
		||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true }
 | 
			
		||||
embedded-io = "0.4.0"
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true }
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true }
 | 
			
		||||
lora-phy = { version = "1", optional = true }
 | 
			
		||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
 | 
			
		||||
lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
 | 
			
		||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
 | 
			
		||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 | 
			
		||||
embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"] }
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] }
 | 
			
		||||
lora-phy = { version = "1" }
 | 
			
		||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] }
 | 
			
		||||
lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de
 | 
			
		||||
embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 | 
			
		||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
			
		||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"]  }
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true }
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true }
 | 
			
		||||
lora-phy = { version = "1", optional = true }
 | 
			
		||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
 | 
			
		||||
lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature
 | 
			
		||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 | 
			
		||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"]  }
 | 
			
		||||
embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" }
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt", "external-lora-phy"] }
 | 
			
		||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
 | 
			
		||||
lora-phy = { version = "1" }
 | 
			
		||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] }
 | 
			
		||||
lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user