Remove legacy LoRa drivers.
This commit is contained in:
@ -55,9 +55,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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user