diff --git a/README.md b/README.md index 270f27a8..b65dcbe1 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The embassy-net network stac The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. - **LoRa** - -embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers. +embassy-lora supports LoRa networking. - **USB** - embassy-usb implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 692a8204..aa0d2911 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -9,19 +9,15 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, - { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, ] [lib] [features] -sx126x = [] -sx127x = [] stm32wl = ["dep:embassy-stm32"] time = [] -defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] +defmt = ["dep:defmt", "lorawan-device/defmt"] external-lora-phy = ["dep:lora-phy"] [dependencies] @@ -41,4 +37,3 @@ bit_field = { version = "0.10" } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.3", default-features = false } diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index c01d3f71..c391a802 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,24 +1,13 @@ #![no_std] #![feature(async_fn_in_trait, impl_trait_projections)] #![allow(incomplete_features)] -//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device -//! crate's async LoRaWAN MAC implementation. +//! embassy-lora holds LoRa-specific functionality. pub(crate) mod fmt; #[cfg(feature = "external-lora-phy")] -/// interface variants required by the external lora crate +/// interface variants required by the external lora physical layer crate (lora-phy) pub mod iv; -#[cfg(feature = "stm32wl")] -#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] -pub mod stm32wl; -#[cfg(feature = "sx126x")] -#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] -pub mod sx126x; -#[cfg(feature = "sx127x")] -#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] -pub mod sx127x; - #[cfg(feature = "time")] use embassy_time::{Duration, Instant, Timer}; diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs deleted file mode 100644 index dae9a195..00000000 --- a/embassy-lora/src/stm32wl/mod.rs +++ /dev/null @@ -1,291 +0,0 @@ -//! A radio driver integration for the radio found on STM32WL family devices. -#![allow(deprecated)] -use core::future::poll_fn; -use core::task::Poll; - -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; -use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; -use embassy_stm32::subghz::{ - CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, - LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, - Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, -}; -use embassy_sync::waitqueue::AtomicWaker; -use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; -use lorawan_device::async_device::Timings; - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum State { - Idle, - Txing, - Rxing, -} - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct RadioError; - -static IRQ_WAKER: AtomicWaker = AtomicWaker::new(); - -/// The radio peripheral keeping the radio state and owning the radio IRQ. -pub struct SubGhzRadio<'d, RS> { - radio: SubGhz<'d, NoDma, NoDma>, - switch: RS, - irq: PeripheralRef<'d, SUBGHZ_RADIO>, -} - -#[derive(Default)] -#[non_exhaustive] -pub struct SubGhzRadioConfig { - pub reg_mode: RegMode, - pub calibrate_image: CalibrateImage, - pub pa_config: PaConfig, - pub tx_params: TxParams, -} - -impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { - /// Create a new instance of a SubGhz radio for LoRaWAN. - pub fn new( - mut radio: SubGhz<'d, NoDma, NoDma>, - switch: RS, - irq: impl Peripheral

+ 'd, - config: SubGhzRadioConfig, - ) -> Result { - into_ref!(irq); - - radio.reset(); - - irq.disable(); - irq.set_handler(|_| { - IRQ_WAKER.wake(); - unsafe { SUBGHZ_RADIO::steal().disable() }; - }); - - configure_radio(&mut radio, config)?; - - Ok(Self { radio, switch, irq }) - } - - /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form - /// the upcoming RX window start. - async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - trace!("TX request: {:?}", config); - self.switch.set_tx(); - - self.radio - .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; - - self.set_lora_mod_params(config.rf)?; - - let packet_params = LoRaPacketParams::new() - .set_preamble_len(8) - .set_header_type(HeaderType::Variable) - .set_payload_len(buf.len() as u8) - .set_crc_en(true) - .set_invert_iq(false); - - self.radio.set_lora_packet_params(&packet_params)?; - - let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout); - self.radio.set_irq_cfg(&irq_cfg)?; - - self.radio.set_buffer_base_address(0, 0)?; - self.radio.write_buffer(0, buf)?; - - // The maximum airtime for any LoRaWAN package is 2793.5ms. - // The value of 4000ms is copied from C driver and gives us a good safety margin. - self.radio.set_tx(Timeout::from_millis_sat(4000))?; - trace!("TX started"); - - loop { - let (_status, irq_status) = self.irq_wait().await; - - if irq_status & Irq::TxDone.mask() != 0 { - trace!("TX done"); - return Ok(0); - } - - if irq_status & Irq::Timeout.mask() != 0 { - return Err(RadioError); - } - } - } - - fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> { - let mod_params = LoRaModParams::new() - .set_sf(convert_spreading_factor(&config.spreading_factor)) - .set_bw(convert_bandwidth(&config.bandwidth)) - .set_cr(CodingRate::Cr45) - .set_ldro_en(matches!( - (config.spreading_factor, config.bandwidth), - (SpreadingFactor::_12, Bandwidth::_125KHz) - | (SpreadingFactor::_12, Bandwidth::_250KHz) - | (SpreadingFactor::_11, Bandwidth::_125KHz) - )); - self.radio.set_lora_mod_params(&mod_params) - } - - /// Perform a radio receive operation with the radio config and receive buffer. The receive buffer must - /// be able to hold a single LoRaWAN packet. - async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { - assert!(buf.len() >= 255); - trace!("RX request: {:?}", config); - self.switch.set_rx(); - - self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; - - self.set_lora_mod_params(config)?; - - let packet_params = LoRaPacketParams::new() - .set_preamble_len(8) - .set_header_type(HeaderType::Variable) - .set_payload_len(0xFF) - .set_crc_en(false) - .set_invert_iq(true); - self.radio.set_lora_packet_params(&packet_params)?; - - let irq_cfg = CfgIrq::new() - .irq_enable_all(Irq::RxDone) - .irq_enable_all(Irq::PreambleDetected) - .irq_enable_all(Irq::HeaderValid) - .irq_enable_all(Irq::HeaderErr) - .irq_enable_all(Irq::Err) - .irq_enable_all(Irq::Timeout); - self.radio.set_irq_cfg(&irq_cfg)?; - - self.radio.set_buffer_base_address(0, 0)?; - - // NOTE: Upper layer handles timeout by cancelling the future - self.radio.set_rx(Timeout::DISABLED)?; - - trace!("RX started"); - - loop { - let (_status, irq_status) = self.irq_wait().await; - - if irq_status & Irq::RxDone.mask() != 0 { - let (_status, len, ptr) = self.radio.rx_buffer_status()?; - let packet_status = self.radio.lora_packet_status()?; - let rssi = packet_status.rssi_pkt().to_integer(); - let snr = packet_status.snr_pkt().to_integer(); - self.radio.read_buffer(ptr, &mut buf[..len as usize])?; - self.radio.set_standby(StandbyClk::Rc)?; - - #[cfg(feature = "defmt")] - trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); - - #[cfg(feature = "log")] - trace!("RX done: {:02x?}", &mut buf[..len as usize]); - return Ok((len as usize, RxQuality::new(rssi, snr as i8))); - } - - if irq_status & Irq::Timeout.mask() != 0 { - return Err(RadioError); - } - } - } - - async fn irq_wait(&mut self) -> (Status, u16) { - poll_fn(|cx| { - self.irq.unpend(); - self.irq.enable(); - IRQ_WAKER.register(cx.waker()); - - let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); - self.radio - .clear_irq_status(irq_status) - .expect("error clearing irq status"); - - trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status); - - if irq_status == 0 { - Poll::Pending - } else { - Poll::Ready((status, irq_status)) - } - }) - .await - } -} - -fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> { - trace!("Configuring STM32WL SUBGHZ radio"); - - radio.set_regulator_mode(config.reg_mode)?; - radio.set_standby(StandbyClk::Rc)?; - - let tcxo_mode = TcxoMode::new() - .set_txco_trim(TcxoTrim::Volts1pt7) - .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100))); - radio.set_tcxo_mode(&tcxo_mode)?; - // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control". - // The STM32CUBE C driver also does this. - radio.set_hse_in_trim(HseTrim::MIN)?; - - // Re-calibrate everything after setting the TXCO config. - radio.calibrate(0x7F)?; - radio.calibrate_image(config.calibrate_image)?; - - radio.set_pa_config(&config.pa_config)?; - radio.set_tx_params(&config.tx_params)?; - radio.set_pa_ocp(Ocp::Max140m)?; - - radio.set_packet_type(PacketType::LoRa)?; - radio.set_lora_sync_word(LoRaSyncWord::Public)?; - - trace!("Done initializing STM32WL SUBGHZ radio"); - Ok(()) -} - -impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { - type PhyError = RadioError; - - async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - self.do_tx(config, buf).await - } - - async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { - self.do_rx(config, buf).await - } -} - -impl From for RadioError { - fn from(_: embassy_stm32::spi::Error) -> Self { - RadioError - } -} - -impl<'d, RS> Timings for SubGhzRadio<'d, RS> { - fn get_rx_window_offset_ms(&self) -> i32 { - -3 - } - fn get_rx_window_duration_ms(&self) -> u32 { - 1003 - } -} - -pub trait RadioSwitch { - fn set_rx(&mut self); - fn set_tx(&mut self); -} - -fn convert_spreading_factor(sf: &SpreadingFactor) -> SF { - match sf { - SpreadingFactor::_7 => SF::Sf7, - SpreadingFactor::_8 => SF::Sf8, - SpreadingFactor::_9 => SF::Sf9, - SpreadingFactor::_10 => SF::Sf10, - SpreadingFactor::_11 => SF::Sf11, - SpreadingFactor::_12 => SF::Sf12, - } -} - -fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth { - match bw { - Bandwidth::_125KHz => LoRaBandwidth::Bw125, - Bandwidth::_250KHz => LoRaBandwidth::Bw250, - Bandwidth::_500KHz => LoRaBandwidth::Bw500, - } -} diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs deleted file mode 100644 index 2f0b8c8e..00000000 --- a/embassy-lora/src/sx126x/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -use defmt::Format; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::*; -use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig}; -use lorawan_device::async_device::Timings; - -mod sx126x_lora; -use sx126x_lora::LoRa; - -use self::sx126x_lora::mod_params::RadioError; - -/// Semtech Sx126x LoRa peripheral -pub struct Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - pub lora: LoRa, -} - -impl Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - pub async fn new( - spi: SPI, - cs: CTRL, - reset: CTRL, - antenna_rx: CTRL, - antenna_tx: CTRL, - dio1: WAIT, - busy: WAIT, - enable_public_network: bool, - ) -> Result> { - let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy); - lora.init().await?; - lora.set_lora_modem(enable_public_network).await?; - Ok(Self { lora }) - } -} - -impl Timings for Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - fn get_rx_window_offset_ms(&self) -> i32 { - -50 - } - fn get_rx_window_duration_ms(&self) -> u32 { - 1050 - } -} - -impl PhyRxTx for Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - type PhyError = RadioError; - - async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result { - trace!("TX START"); - self.lora - .set_tx_config( - config.pw, - config.rf.spreading_factor.into(), - config.rf.bandwidth.into(), - config.rf.coding_rate.into(), - 8, - false, - true, - false, - 0, - false, - ) - .await?; - self.lora.set_max_payload_length(buffer.len() as u8).await?; - self.lora.set_channel(config.rf.frequency).await?; - self.lora.send(buffer, 0xffffff).await?; - self.lora.process_irq(None, None, None).await?; - trace!("TX DONE"); - return Ok(0); - } - - async fn rx( - &mut self, - config: RfConfig, - receiving_buffer: &mut [u8], - ) -> Result<(usize, RxQuality), Self::PhyError> { - trace!("RX START"); - self.lora - .set_rx_config( - config.spreading_factor.into(), - config.bandwidth.into(), - config.coding_rate.into(), - 8, - 4, - false, - 0u8, - true, - false, - 0, - true, - true, - ) - .await?; - self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; - self.lora.set_channel(config.frequency).await?; - self.lora.rx(90 * 1000).await?; - let mut received_len = 0u8; - self.lora - .process_irq(Some(receiving_buffer), Some(&mut received_len), None) - .await?; - trace!("RX DONE"); - - let packet_status = self.lora.get_latest_packet_status(); - let mut rssi = 0i16; - let mut snr = 0i8; - if packet_status.is_some() { - rssi = packet_status.unwrap().rssi as i16; - snr = packet_status.unwrap().snr; - } - - Ok((received_len as usize, RxQuality::new(rssi, snr))) - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs deleted file mode 100644 index a7b9e148..00000000 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ /dev/null @@ -1,256 +0,0 @@ -use embassy_time::{Duration, Timer}; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::SpiBus; - -use super::mod_params::RadioError::*; -use super::mod_params::*; -use super::LoRa; - -// Defines the time required for the TCXO to wakeup [ms]. -const BRD_TCXO_WAKEUP_TIME: u32 = 10; - -// Provides board-specific functionality for Semtech SX126x-based boards. - -impl LoRa -where - SPI: SpiBus, - CTRL: OutputPin, - WAIT: Wait, -{ - // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. - pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently - } - - // Initialize the TCXO power pin - pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError> { - let timeout = self.brd_get_board_tcxo_wakeup_time() << 6; - self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout) - .await?; - Ok(()) - } - - // Initialize RF switch control pins - pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError> { - self.sub_set_dio2_as_rf_switch_ctrl(true).await?; - Ok(()) - } - - // Initialize the radio debug pins - pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently - } - - // Hardware reset of the radio - pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError> { - Timer::after(Duration::from_millis(10)).await; - self.reset.set_low().map_err(|_| Reset)?; - Timer::after(Duration::from_millis(20)).await; - self.reset.set_high().map_err(|_| Reset)?; - Timer::after(Duration::from_millis(10)).await; - Ok(()) - } - - // Wait while the busy pin is high - pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError> { - self.busy.wait_for_low().await.map_err(|_| Busy)?; - Ok(()) - } - - // Wake up the radio - pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError> { - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?; - self.spi.write(&[0x00]).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - self.brd_set_operating_mode(RadioMode::StandbyRC); - Ok(()) - } - - // Send a command that writes data to the radio - pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError> { - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[op_code.value()]).await.map_err(SPI)?; - self.spi.write(buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - if op_code != OpCode::SetSleep { - self.brd_wait_on_busy().await?; - } - Ok(()) - } - - // Send a command that reads data from the radio, filling the provided buffer and returning a status - pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result> { - let mut status = [0u8]; - let mut input = [0u8]; - - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[op_code.value()]).await.map_err(SPI)?; - self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?; - for i in 0..buffer.len() { - self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; - buffer[i] = input[0]; - } - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - - Ok(status[0]) - } - - // Write one or more bytes of data to the radio memory - pub(super) async fn brd_write_registers( - &mut self, - start_register: Register, - buffer: &[u8], - ) -> Result<(), RadioError> { - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?; - self.spi - .write(&[ - ((start_register.addr() & 0xFF00) >> 8) as u8, - (start_register.addr() & 0x00FF) as u8, - ]) - .await - .map_err(SPI)?; - self.spi.write(buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Read one or more bytes of data from the radio memory - pub(super) async fn brd_read_registers( - &mut self, - start_register: Register, - buffer: &mut [u8], - ) -> Result<(), RadioError> { - let mut input = [0u8]; - - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?; - self.spi - .write(&[ - ((start_register.addr() & 0xFF00) >> 8) as u8, - (start_register.addr() & 0x00FF) as u8, - 0x00u8, - ]) - .await - .map_err(SPI)?; - for i in 0..buffer.len() { - self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; - buffer[i] = input[0]; - } - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Write data to the buffer holding the payload in the radio - pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError> { - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?; - self.spi.write(&[offset]).await.map_err(SPI)?; - self.spi.write(buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Read data from the buffer holding the payload in the radio - pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError> { - let mut input = [0u8]; - - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?; - self.spi.write(&[offset]).await.map_err(SPI)?; - self.spi.write(&[0x00]).await.map_err(SPI)?; - for i in 0..buffer.len() { - self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; - buffer[i] = input[0]; - } - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Set the radio output power - pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError> { - self.sub_set_tx_params(power, RampTime::Ramp40Us).await?; - Ok(()) - } - - // Get the radio type - pub(super) fn brd_get_radio_type(&mut self) -> RadioType { - RadioType::SX1262 - } - - // Quiesce the antenna(s). - pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_low().map_err(|_| AntRx)?; - Ok(()) - } - - // Prepare the antenna(s) for a receive operation - pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_high().map_err(|_| AntRx)?; - Ok(()) - } - - // Prepare the antenna(s) for a send operation - pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { - self.antenna_rx.set_low().map_err(|_| AntRx)?; - self.antenna_tx.set_high().map_err(|_| AntTx)?; - Ok(()) - } - - // Check if the given RF frequency is supported by the hardware - pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { - Ok(true) - } - - // Get the duration required for the TCXO to wakeup [ms]. - pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 { - BRD_TCXO_WAKEUP_TIME - } - - /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process - pub(super) async fn brd_get_dio1_pin_state( - &mut self, - ) -> Result> { - Ok(0) - } - */ - - // Get the current radio operatiing mode - pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode { - self.operating_mode - } - - // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts. - pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) { - self.operating_mode = mode; - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs deleted file mode 100644 index 280f26d5..00000000 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ /dev/null @@ -1,732 +0,0 @@ -#![allow(dead_code)] - -use embassy_time::{Duration, Timer}; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::SpiBus; - -mod board_specific; -pub mod mod_params; -mod subroutine; - -use mod_params::RadioError::*; -use mod_params::*; - -// Syncwords for public and private networks -const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444; -const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424; - -// Maximum number of registers that can be added to the retention list -const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4; - -// Possible LoRa bandwidths -const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz]; - -// Radio complete wakeup time with margin for temperature compensation [ms] -const RADIO_WAKEUP_TIME: u32 = 3; - -/// Provides high-level access to Semtech SX126x-based boards -pub struct LoRa { - spi: SPI, - cs: CTRL, - reset: CTRL, - antenna_rx: CTRL, - antenna_tx: CTRL, - dio1: WAIT, - busy: WAIT, - operating_mode: RadioMode, - rx_continuous: bool, - max_payload_length: u8, - modulation_params: Option, - packet_type: PacketType, - packet_params: Option, - packet_status: Option, - image_calibrated: bool, - frequency_error: u32, -} - -impl LoRa -where - SPI: SpiBus, - CTRL: OutputPin, - WAIT: Wait, -{ - /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () - pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self { - Self { - spi, - cs, - reset, - antenna_rx, - antenna_tx, - dio1, - busy, - operating_mode: RadioMode::Sleep, - rx_continuous: false, - max_payload_length: 0xFFu8, - modulation_params: None, - packet_type: PacketType::LoRa, - packet_params: None, - packet_status: None, - image_calibrated: false, - frequency_error: 0u32, // where is volatile FrequencyError modified ??? - } - } - - /// Initialize the radio - pub async fn init(&mut self) -> Result<(), RadioError> { - self.sub_init().await?; - self.sub_set_standby(StandbyMode::RC).await?; - self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?; - self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?; - self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?; - self.sub_set_dio_irq_params( - IrqMask::All.value(), - IrqMask::All.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - self.add_register_to_retention_list(Register::RxGain.addr()).await?; - self.add_register_to_retention_list(Register::TxModulation.addr()) - .await?; - Ok(()) - } - - /// Return current radio state - pub fn get_status(&mut self) -> RadioState { - match self.brd_get_operating_mode() { - RadioMode::Transmit => RadioState::TxRunning, - RadioMode::Receive => RadioState::RxRunning, - RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting, - _ => RadioState::Idle, - } - } - - /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired) - pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError> { - self.sub_set_packet_type(PacketType::LoRa).await?; - if enable_public_network { - self.brd_write_registers( - Register::LoRaSyncword, - &[ - ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8, - (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8, - ], - ) - .await?; - } else { - self.brd_write_registers( - Register::LoRaSyncword, - &[ - ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8, - (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8, - ], - ) - .await?; - } - - Ok(()) - } - - /// Sets the channel frequency - pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError> { - self.sub_set_rf_frequency(frequency).await?; - Ok(()) - } - - /* Checks if the channel is free for the given time. This is currently not implemented until a substitute - for switching to the FSK modem is found. - - pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool; - */ - - /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand. - /// After calling this function either set_rx_config() or set_tx_config() must be called. - pub async fn get_random_value(&mut self) -> Result> { - self.sub_set_dio_irq_params( - IrqMask::None.value(), - IrqMask::None.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - let result = self.sub_get_random().await?; - Ok(result) - } - - /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand. - /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] - /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] - /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] - /// preamble_length length in symbols (the hardware adds 4 more symbols) - /// symb_timeout RxSingle timeout value in symbols - /// fixed_len fixed length packets [0: variable, 1: fixed] - /// payload_len payload length when fixed length is used - /// crc_on [0: OFF, 1: ON] - /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] - /// hop_period number of symbols between each hop - /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] - /// rx_continuous reception mode [false: single mode, true: continuous mode] - pub async fn set_rx_config( - &mut self, - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - symb_timeout: u16, - fixed_len: bool, - payload_len: u8, - crc_on: bool, - _freq_hop_on: bool, - _hop_period: u8, - iq_inverted: bool, - rx_continuous: bool, - ) -> Result<(), RadioError> { - let mut symb_timeout_final = symb_timeout; - - self.rx_continuous = rx_continuous; - if self.rx_continuous { - symb_timeout_final = 0; - } - if fixed_len { - self.max_payload_length = payload_len; - } else { - self.max_payload_length = 0xFFu8; - } - - self.sub_set_stop_rx_timer_on_preamble_detect(false).await?; - - let mut low_data_rate_optimize = 0x00u8; - if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) - && (bandwidth == Bandwidth::_125KHz)) - || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) - { - low_data_rate_optimize = 0x01u8; - } - - let modulation_params = ModulationParams { - spreading_factor: spreading_factor, - bandwidth: bandwidth, - coding_rate: coding_rate, - low_data_rate_optimize: low_data_rate_optimize, - }; - - let mut preamble_length_final = preamble_length; - if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) - && (preamble_length < 12) - { - preamble_length_final = 12; - } - - let packet_params = PacketParams { - preamble_length: preamble_length_final, - implicit_header: fixed_len, - payload_length: self.max_payload_length, - crc_on: crc_on, - iq_inverted: iq_inverted, - }; - - self.modulation_params = Some(modulation_params); - self.packet_params = Some(packet_params); - - self.standby().await?; - self.sub_set_modulation_params().await?; - self.sub_set_packet_params().await?; - self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?; - - // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) - let mut iq_polarity = [0x00u8]; - self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?; - if iq_inverted { - self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))]) - .await?; - } else { - self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)]) - .await?; - } - Ok(()) - } - - /// Set the transmission parameters for the LoRa modem (only). - /// power output power [dBm] - /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] - /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] - /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] - /// preamble_length length in symbols (the hardware adds 4 more symbols) - /// fixed_len fixed length packets [0: variable, 1: fixed] - /// crc_on [0: OFF, 1: ON] - /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] - /// hop_period number of symbols between each hop - /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] - pub async fn set_tx_config( - &mut self, - power: i8, - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - fixed_len: bool, - crc_on: bool, - _freq_hop_on: bool, - _hop_period: u8, - iq_inverted: bool, - ) -> Result<(), RadioError> { - let mut low_data_rate_optimize = 0x00u8; - if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) - && (bandwidth == Bandwidth::_125KHz)) - || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) - { - low_data_rate_optimize = 0x01u8; - } - - let modulation_params = ModulationParams { - spreading_factor: spreading_factor, - bandwidth: bandwidth, - coding_rate: coding_rate, - low_data_rate_optimize: low_data_rate_optimize, - }; - - let mut preamble_length_final = preamble_length; - if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) - && (preamble_length < 12) - { - preamble_length_final = 12; - } - - let packet_params = PacketParams { - preamble_length: preamble_length_final, - implicit_header: fixed_len, - payload_length: self.max_payload_length, - crc_on: crc_on, - iq_inverted: iq_inverted, - }; - - self.modulation_params = Some(modulation_params); - self.packet_params = Some(packet_params); - - self.standby().await?; - self.sub_set_modulation_params().await?; - self.sub_set_packet_params().await?; - - // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) - - let mut tx_modulation = [0x00u8]; - self.brd_read_registers(Register::TxModulation, &mut tx_modulation) - .await?; - if bandwidth == Bandwidth::_500KHz { - self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))]) - .await?; - } else { - self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)]) - .await?; - } - - self.brd_set_rf_tx_power(power).await?; - Ok(()) - } - - /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported] - pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result> { - Ok(self.brd_check_rf_frequency(frequency).await?) - } - - /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called) - /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] - /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] - /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] - /// preamble_length length in symbols (the hardware adds 4 more symbols) - /// fixed_len fixed length packets [0: variable, 1: fixed] - /// payload_len sets payload length when fixed length is used - /// crc_on [0: OFF, 1: ON] - pub fn get_time_on_air( - &mut self, - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - fixed_len: bool, - payload_len: u8, - crc_on: bool, - ) -> Result> { - let numerator = 1000 - * Self::get_lora_time_on_air_numerator( - spreading_factor, - bandwidth, - coding_rate, - preamble_length, - fixed_len, - payload_len, - crc_on, - ); - let denominator = bandwidth.value_in_hz(); - if denominator == 0 { - Err(RadioError::InvalidBandwidth) - } else { - Ok((numerator + denominator - 1) / denominator) - } - } - - /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms] - pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError> { - if self.packet_params.is_some() { - self.sub_set_dio_irq_params( - IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), - IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - let mut packet_params = self.packet_params.as_mut().unwrap(); - packet_params.payload_length = buffer.len() as u8; - self.sub_set_packet_params().await?; - self.sub_send_payload(buffer, timeout).await?; - Ok(()) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - /// Set the radio in sleep mode - pub async fn sleep(&mut self) -> Result<(), RadioError> { - self.sub_set_sleep(SleepParams { - wakeup_rtc: false, - reset: false, - warm_start: true, - }) - .await?; - Timer::after(Duration::from_millis(2)).await; - Ok(()) - } - - /// Set the radio in standby mode - pub async fn standby(&mut self) -> Result<(), RadioError> { - self.sub_set_standby(StandbyMode::RC).await?; - Ok(()) - } - - /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)] - pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError> { - self.sub_set_dio_irq_params( - IrqMask::All.value(), - IrqMask::All.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - if self.rx_continuous { - self.sub_set_rx(0xFFFFFF).await?; - } else { - self.sub_set_rx(timeout << 6).await?; - } - - Ok(()) - } - - /// Start a Channel Activity Detection - pub async fn start_cad(&mut self) -> Result<(), RadioError> { - self.sub_set_dio_irq_params( - IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), - IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - self.sub_set_cad().await?; - Ok(()) - } - - /// Sets the radio in continuous wave transmission mode - /// frequency channel RF frequency - /// power output power [dBm] - /// timeout transmission mode timeout [s] - pub async fn set_tx_continuous_wave( - &mut self, - frequency: u32, - power: i8, - _timeout: u16, - ) -> Result<(), RadioError> { - self.sub_set_rf_frequency(frequency).await?; - self.brd_set_rf_tx_power(power).await?; - self.sub_set_tx_continuous_wave().await?; - - Ok(()) - } - - /// Read the current RSSI value for the LoRa modem (only) [dBm] - pub async fn get_rssi(&mut self) -> Result> { - let value = self.sub_get_rssi_inst().await?; - Ok(value as i16) - } - - /// Write one or more radio registers with a buffer of a given size, starting at the first register address - pub async fn write_registers_from_buffer( - &mut self, - start_register: Register, - buffer: &[u8], - ) -> Result<(), RadioError> { - self.brd_write_registers(start_register, buffer).await?; - Ok(()) - } - - /// Read one or more radio registers into a buffer of a given size, starting at the first register address - pub async fn read_registers_into_buffer( - &mut self, - start_register: Register, - buffer: &mut [u8], - ) -> Result<(), RadioError> { - self.brd_read_registers(start_register, buffer).await?; - Ok(()) - } - - /// Set the maximum payload length (in bytes) for a LoRa modem (only). - pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError> { - if self.packet_params.is_some() { - let packet_params = self.packet_params.as_mut().unwrap(); - self.max_payload_length = max; - packet_params.payload_length = max; - self.sub_set_packet_params().await?; - Ok(()) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - /// Get the time required for the board plus radio to get out of sleep [ms] - pub fn get_wakeup_time(&mut self) -> u32 { - self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME - } - - /// Process the radio irq - pub async fn process_irq( - &mut self, - receiving_buffer: Option<&mut [u8]>, - received_len: Option<&mut u8>, - cad_activity_detected: Option<&mut bool>, - ) -> Result<(), RadioError> { - loop { - trace!("process_irq loop entered"); - - let de = self.sub_get_device_errors().await?; - trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", - de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); - let st = self.sub_get_status().await?; - trace!( - "radio status: cmd_status: {:x}, chip_mode: {:x}", - st.cmd_status, - st.chip_mode - ); - - self.dio1.wait_for_high().await.map_err(|_| DIO1)?; - let operating_mode = self.brd_get_operating_mode(); - let irq_flags = self.sub_get_irq_status().await?; - self.sub_clear_irq_status(irq_flags).await?; - trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); - - // check for errors and unexpected interrupt masks (based on operation mode) - if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { - if !self.rx_continuous { - self.brd_set_operating_mode(RadioMode::StandbyRC); - } - return Err(RadioError::HeaderError); - } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() { - if operating_mode == RadioMode::Receive { - if !self.rx_continuous { - self.brd_set_operating_mode(RadioMode::StandbyRC); - } - return Err(RadioError::CRCErrorOnReceive); - } else { - return Err(RadioError::CRCErrorUnexpected); - } - } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() { - if operating_mode == RadioMode::Transmit { - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Err(RadioError::TransmitTimeout); - } else if operating_mode == RadioMode::Receive { - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Err(RadioError::ReceiveTimeout); - } else { - return Err(RadioError::TimeoutUnexpected); - } - } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value()) - && (operating_mode != RadioMode::Transmit) - { - return Err(RadioError::TransmitDoneUnexpected); - } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value()) - && (operating_mode != RadioMode::Receive) - { - return Err(RadioError::ReceiveDoneUnexpected); - } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value()) - || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value())) - && (operating_mode != RadioMode::ChannelActivityDetection) - { - return Err(RadioError::CADUnexpected); - } - - if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { - trace!("HeaderValid"); - } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { - trace!("PreambleDetected"); - } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { - trace!("SyncwordValid"); - } - - // handle completions - if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() { - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Ok(()); - } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() { - if !self.rx_continuous { - self.brd_set_operating_mode(RadioMode::StandbyRC); - - // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3) - self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?; - let mut evt_clr = [0x00u8]; - self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?; - evt_clr[0] |= 1 << 1; - self.brd_write_registers(Register::EvtClr, &evt_clr).await?; - } - - if receiving_buffer.is_some() && received_len.is_some() { - *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?; - } - self.packet_status = self.sub_get_packet_status().await?.into(); - return Ok(()); - } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() { - if cad_activity_detected.is_some() { - *(cad_activity_detected.unwrap()) = - (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value(); - } - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Ok(()); - } - - // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid - // are in that category), loop to wait again - } - } - - // SX126x-specific functions - - /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms] - pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { - self.sub_set_dio_irq_params( - IrqMask::All.value(), - IrqMask::All.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - if self.rx_continuous { - self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous - } else { - self.sub_set_rx_boosted(timeout << 6).await?; - } - - Ok(()) - } - - /// Set the Rx duty cycle management parameters (SX126x radios only) - /// rx_time structure describing reception timeout value - /// sleep_time structure describing sleep timeout value - pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { - self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?; - Ok(()) - } - - pub fn get_latest_packet_status(&mut self) -> Option { - self.packet_status - } - - // Utilities - - async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError> { - let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize]; - - // Read the address and registers already added to the list - self.brd_read_registers(Register::RetentionList, &mut buffer).await?; - - let number_of_registers = buffer[0]; - for i in 0..number_of_registers { - if register_address - == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16) - { - return Ok(()); // register already in list - } - } - - if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION { - buffer[0] += 1; // increment number of registers - - buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8; - buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8; - self.brd_write_registers(Register::RetentionList, &buffer).await?; - - Ok(()) - } else { - Err(RadioError::RetentionListExceeded) - } - } - - fn get_lora_time_on_air_numerator( - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - fixed_len: bool, - payload_len: u8, - crc_on: bool, - ) -> u32 { - let cell_denominator; - let cr_denominator = (coding_rate.value() as i32) + 4; - - // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 - let mut preamble_length_final = preamble_length; - if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) - && (preamble_length < 12) - { - preamble_length_final = 12; - } - - let mut low_data_rate_optimize = false; - if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) - && (bandwidth == Bandwidth::_125KHz)) - || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) - { - low_data_rate_optimize = true; - } - - let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 }) - - (4 * spreading_factor.value() as i32) - + (if fixed_len { 0 } else { 20 }); - - if spreading_factor.value() <= 6 { - cell_denominator = 4 * (spreading_factor.value() as i32); - } else { - cell_numerator += 8; - if low_data_rate_optimize { - cell_denominator = 4 * ((spreading_factor.value() as i32) - 2); - } else { - cell_denominator = 4 * (spreading_factor.value() as i32); - } - } - - if cell_numerator < 0 { - cell_numerator = 0; - } - - let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator) - + (preamble_length_final as i32) - + 12; - - if spreading_factor.value() <= 6 { - intermediate = intermediate + 2; - } - - (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32 - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs deleted file mode 100644 index e270b2a0..00000000 --- a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs +++ /dev/null @@ -1,469 +0,0 @@ -use core::fmt::Debug; - -use lorawan_device::async_device::radio as device; - -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum RadioError { - SPI(BUS), - CS, - Reset, - AntRx, - AntTx, - Busy, - DIO1, - PayloadSizeMismatch(usize, usize), - RetentionListExceeded, - InvalidBandwidth, - ModulationParamsMissing, - PacketParamsMissing, - HeaderError, - CRCErrorUnexpected, - CRCErrorOnReceive, - TransmitTimeout, - ReceiveTimeout, - TimeoutUnexpected, - TransmitDoneUnexpected, - ReceiveDoneUnexpected, - CADUnexpected, -} - -pub struct RadioSystemError { - pub rc_64khz_calibration: bool, - pub rc_13mhz_calibration: bool, - pub pll_calibration: bool, - pub adc_calibration: bool, - pub image_calibration: bool, - pub xosc_start: bool, - pub pll_lock: bool, - pub pa_ramp: bool, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum PacketType { - GFSK = 0x00, - LoRa = 0x01, - None = 0x0F, -} - -impl PacketType { - pub const fn value(self) -> u8 { - self as u8 - } - pub fn to_enum(value: u8) -> Self { - if value == 0x00 { - PacketType::GFSK - } else if value == 0x01 { - PacketType::LoRa - } else { - PacketType::None - } - } -} - -#[derive(Clone, Copy)] -pub struct PacketStatus { - pub rssi: i8, - pub snr: i8, - pub signal_rssi: i8, - pub freq_error: u32, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum RadioType { - SX1261, - SX1262, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum RadioMode { - Sleep = 0x00, // sleep mode - StandbyRC = 0x01, // standby mode with RC oscillator - StandbyXOSC = 0x02, // standby mode with XOSC oscillator - FrequencySynthesis = 0x03, // frequency synthesis mode - Transmit = 0x04, // transmit mode - Receive = 0x05, // receive mode - ReceiveDutyCycle = 0x06, // receive duty cycle mode - ChannelActivityDetection = 0x07, // channel activity detection mode -} - -impl RadioMode { - /// Returns the value of the mode. - pub const fn value(self) -> u8 { - self as u8 - } - pub fn to_enum(value: u8) -> Self { - if value == 0x00 { - RadioMode::Sleep - } else if value == 0x01 { - RadioMode::StandbyRC - } else if value == 0x02 { - RadioMode::StandbyXOSC - } else if value == 0x03 { - RadioMode::FrequencySynthesis - } else if value == 0x04 { - RadioMode::Transmit - } else if value == 0x05 { - RadioMode::Receive - } else if value == 0x06 { - RadioMode::ReceiveDutyCycle - } else if value == 0x07 { - RadioMode::ChannelActivityDetection - } else { - RadioMode::Sleep - } - } -} - -pub enum RadioState { - Idle = 0x00, - RxRunning = 0x01, - TxRunning = 0x02, - ChannelActivityDetecting = 0x03, -} - -impl RadioState { - /// Returns the value of the state. - pub fn value(self) -> u8 { - self as u8 - } -} - -pub struct RadioStatus { - pub cmd_status: u8, - pub chip_mode: u8, -} - -impl RadioStatus { - pub fn value(self) -> u8 { - (self.chip_mode << 4) | (self.cmd_status << 1) - } -} - -#[derive(Clone, Copy)] -pub enum IrqMask { - None = 0x0000, - TxDone = 0x0001, - RxDone = 0x0002, - PreambleDetected = 0x0004, - SyncwordValid = 0x0008, - HeaderValid = 0x0010, - HeaderError = 0x0020, - CRCError = 0x0040, - CADDone = 0x0080, - CADActivityDetected = 0x0100, - RxTxTimeout = 0x0200, - All = 0xFFFF, -} - -impl IrqMask { - pub fn value(self) -> u16 { - self as u16 - } -} - -#[derive(Clone, Copy)] -pub enum Register { - PacketParams = 0x0704, // packet configuration - PayloadLength = 0x0702, // payload size - SynchTimeout = 0x0706, // recalculated number of symbols - Syncword = 0x06C0, // Syncword values - LoRaSyncword = 0x0740, // LoRa Syncword value - GeneratedRandomNumber = 0x0819, //32-bit generated random number - AnaLNA = 0x08E2, // disable the LNA - AnaMixer = 0x08E5, // disable the mixer - RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted) - XTATrim = 0x0911, // device internal trimming capacitor - OCP = 0x08E7, // over current protection max value - RetentionList = 0x029F, // retention list - IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) - TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) - TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) - RTCCtrl = 0x0902, // RTC control - EvtClr = 0x0944, // event clear -} - -impl Register { - pub fn addr(self) -> u16 { - self as u16 - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum OpCode { - GetStatus = 0xC0, - WriteRegister = 0x0D, - ReadRegister = 0x1D, - WriteBuffer = 0x0E, - ReadBuffer = 0x1E, - SetSleep = 0x84, - SetStandby = 0x80, - SetFS = 0xC1, - SetTx = 0x83, - SetRx = 0x82, - SetRxDutyCycle = 0x94, - SetCAD = 0xC5, - SetTxContinuousWave = 0xD1, - SetTxContinuousPremable = 0xD2, - SetPacketType = 0x8A, - GetPacketType = 0x11, - SetRFFrequency = 0x86, - SetTxParams = 0x8E, - SetPAConfig = 0x95, - SetCADParams = 0x88, - SetBufferBaseAddress = 0x8F, - SetModulationParams = 0x8B, - SetPacketParams = 0x8C, - GetRxBufferStatus = 0x13, - GetPacketStatus = 0x14, - GetRSSIInst = 0x15, - GetStats = 0x10, - ResetStats = 0x00, - CfgDIOIrq = 0x08, - GetIrqStatus = 0x12, - ClrIrqStatus = 0x02, - Calibrate = 0x89, - CalibrateImage = 0x98, - SetRegulatorMode = 0x96, - GetErrors = 0x17, - ClrErrors = 0x07, - SetTCXOMode = 0x97, - SetTxFallbackMode = 0x93, - SetRFSwitchMode = 0x9D, - SetStopRxTimerOnPreamble = 0x9F, - SetLoRaSymbTimeout = 0xA0, -} - -impl OpCode { - pub fn value(self) -> u8 { - self as u8 - } -} - -pub struct SleepParams { - pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC - pub reset: bool, - pub warm_start: bool, -} - -impl SleepParams { - pub fn value(self) -> u8 { - ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8) - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum StandbyMode { - RC = 0x00, - XOSC = 0x01, -} - -impl StandbyMode { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum RegulatorMode { - UseLDO = 0x00, - UseDCDC = 0x01, -} - -impl RegulatorMode { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub struct CalibrationParams { - pub rc64k_enable: bool, // calibrate RC64K clock - pub rc13m_enable: bool, // calibrate RC13M clock - pub pll_enable: bool, // calibrate PLL - pub adc_pulse_enable: bool, // calibrate ADC Pulse - pub adc_bulkn_enable: bool, // calibrate ADC bulkN - pub adc_bulkp_enable: bool, // calibrate ADC bulkP - pub img_enable: bool, -} - -impl CalibrationParams { - pub fn value(self) -> u8 { - ((self.img_enable as u8) << 6) - | ((self.adc_bulkp_enable as u8) << 5) - | ((self.adc_bulkn_enable as u8) << 4) - | ((self.adc_pulse_enable as u8) << 3) - | ((self.pll_enable as u8) << 2) - | ((self.rc13m_enable as u8) << 1) - | ((self.rc64k_enable as u8) << 0) - } -} - -#[derive(Clone, Copy)] -pub enum TcxoCtrlVoltage { - Ctrl1V6 = 0x00, - Ctrl1V7 = 0x01, - Ctrl1V8 = 0x02, - Ctrl2V2 = 0x03, - Ctrl2V4 = 0x04, - Ctrl2V7 = 0x05, - Ctrl3V0 = 0x06, - Ctrl3V3 = 0x07, -} - -impl TcxoCtrlVoltage { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum RampTime { - Ramp10Us = 0x00, - Ramp20Us = 0x01, - Ramp40Us = 0x02, - Ramp80Us = 0x03, - Ramp200Us = 0x04, - Ramp800Us = 0x05, - Ramp1700Us = 0x06, - Ramp3400Us = 0x07, -} - -impl RampTime { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum SpreadingFactor { - _5 = 0x05, - _6 = 0x06, - _7 = 0x07, - _8 = 0x08, - _9 = 0x09, - _10 = 0x0A, - _11 = 0x0B, - _12 = 0x0C, -} - -impl SpreadingFactor { - pub fn value(self) -> u8 { - self as u8 - } -} - -impl From for SpreadingFactor { - fn from(sf: device::SpreadingFactor) -> Self { - match sf { - device::SpreadingFactor::_7 => SpreadingFactor::_7, - device::SpreadingFactor::_8 => SpreadingFactor::_8, - device::SpreadingFactor::_9 => SpreadingFactor::_9, - device::SpreadingFactor::_10 => SpreadingFactor::_10, - device::SpreadingFactor::_11 => SpreadingFactor::_11, - device::SpreadingFactor::_12 => SpreadingFactor::_12, - } - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum Bandwidth { - _500KHz = 0x06, - _250KHz = 0x05, - _125KHz = 0x04, -} - -impl Bandwidth { - pub fn value(self) -> u8 { - self as u8 - } - - pub fn value_in_hz(self) -> u32 { - match self { - Bandwidth::_125KHz => 125000u32, - Bandwidth::_250KHz => 250000u32, - Bandwidth::_500KHz => 500000u32, - } - } -} - -impl From for Bandwidth { - fn from(bw: device::Bandwidth) -> Self { - match bw { - device::Bandwidth::_500KHz => Bandwidth::_500KHz, - device::Bandwidth::_250KHz => Bandwidth::_250KHz, - device::Bandwidth::_125KHz => Bandwidth::_125KHz, - } - } -} - -#[derive(Clone, Copy)] -pub enum CodingRate { - _4_5 = 0x01, - _4_6 = 0x02, - _4_7 = 0x03, - _4_8 = 0x04, -} - -impl CodingRate { - pub fn value(self) -> u8 { - self as u8 - } -} - -impl From for CodingRate { - fn from(cr: device::CodingRate) -> Self { - match cr { - device::CodingRate::_4_5 => CodingRate::_4_5, - device::CodingRate::_4_6 => CodingRate::_4_6, - device::CodingRate::_4_7 => CodingRate::_4_7, - device::CodingRate::_4_8 => CodingRate::_4_8, - } - } -} - -#[derive(Clone, Copy)] -pub struct ModulationParams { - pub spreading_factor: SpreadingFactor, - pub bandwidth: Bandwidth, - pub coding_rate: CodingRate, - pub low_data_rate_optimize: u8, -} - -#[derive(Clone, Copy)] -pub struct PacketParams { - pub preamble_length: u16, // number of LoRa symbols in the preamble - pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length) - pub payload_length: u8, - pub crc_on: bool, - pub iq_inverted: bool, -} - -#[derive(Clone, Copy)] -pub enum CADSymbols { - _1 = 0x00, - _2 = 0x01, - _4 = 0x02, - _8 = 0x03, - _16 = 0x04, -} - -impl CADSymbols { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum CADExitMode { - CADOnly = 0x00, - CADRx = 0x01, - CADLBT = 0x10, -} - -impl CADExitMode { - pub fn value(self) -> u8 { - self as u8 - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs deleted file mode 100644 index 2e78b919..00000000 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ /dev/null @@ -1,674 +0,0 @@ -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::SpiBus; - -use super::mod_params::*; -use super::LoRa; - -// Internal frequency of the radio -const SX126X_XTAL_FREQ: u32 = 32000000; - -// Scaling factor used to perform fixed-point operations -const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14; - -// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT -const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT); - -// Maximum value for parameter symbNum -const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; - -// Provides board-specific functionality for Semtech SX126x-based boards - -impl LoRa -where - SPI: SpiBus, - CTRL: OutputPin, - WAIT: Wait, -{ - // Initialize the radio driver - pub(super) async fn sub_init(&mut self) -> Result<(), RadioError> { - self.brd_reset().await?; - self.brd_wakeup().await?; - self.sub_set_standby(StandbyMode::RC).await?; - self.brd_io_tcxo_init().await?; - self.brd_io_rf_switch_init().await?; - self.image_calibrated = false; - Ok(()) - } - - // Wakeup the radio if it is in Sleep mode and check that Busy is low - pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError> { - let operating_mode = self.brd_get_operating_mode(); - if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { - self.brd_wakeup().await?; - } - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Save the payload to be sent in the radio buffer - pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError> { - self.brd_write_buffer(0x00, payload).await?; - Ok(()) - } - - // Read the payload received. - pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result> { - let (size, offset) = self.sub_get_rx_buffer_status().await?; - if (size as usize) > buffer.len() { - Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len())) - } else { - self.brd_read_buffer(offset, buffer).await?; - Ok(size) - } - } - - // Send a payload - pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError> { - self.sub_set_payload(payload).await?; - self.sub_set_tx(timeout).await?; - Ok(()) - } - - // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command. - // - // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of - // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts - // generated during this process will not cause undesired side-effects in the software. - // - // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing. - pub(super) async fn sub_get_random(&mut self) -> Result> { - let mut reg_ana_lna_buffer_original = [0x00u8]; - let mut reg_ana_mixer_buffer_original = [0x00u8]; - let mut reg_ana_lna_buffer = [0x00u8]; - let mut reg_ana_mixer_buffer = [0x00u8]; - let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8]; - - self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original) - .await?; - reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0)); - self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?; - - self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original) - .await?; - reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7)); - self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer) - .await?; - - // Set radio in continuous reception - self.sub_set_rx(0xFFFFFFu32).await?; - - self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer) - .await?; - - self.sub_set_standby(StandbyMode::RC).await?; - - self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original) - .await?; - self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original) - .await?; - - Ok(Self::convert_u8_buffer_to_u32(&number_buffer)) - } - - // Set the radio in sleep mode - pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { - self.brd_ant_sleep()?; - - if !sleep_config.warm_start { - self.image_calibrated = false; - } - - self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()]) - .await?; - self.brd_set_operating_mode(RadioMode::Sleep); - Ok(()) - } - - // Set the radio in configuration mode - pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?; - if mode == StandbyMode::RC { - self.brd_set_operating_mode(RadioMode::StandbyRC); - } else { - self.brd_set_operating_mode(RadioMode::StandbyXOSC); - } - - self.brd_ant_sleep()?; - Ok(()) - } - - // Set the radio in FS mode - pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError> { - // antenna settings ??? - self.brd_write_command(OpCode::SetFS, &[]).await?; - self.brd_set_operating_mode(RadioMode::FrequencySynthesis); - Ok(()) - } - - // Set the radio in transmission mode with timeout specified - pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError> { - let buffer = [ - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - - self.brd_ant_set_tx()?; - - self.brd_set_operating_mode(RadioMode::Transmit); - self.brd_write_command(OpCode::SetTx, &buffer).await?; - Ok(()) - } - - // Set the radio in reception mode with timeout specified - pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError> { - let buffer = [ - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - - self.brd_ant_set_rx()?; - - self.brd_set_operating_mode(RadioMode::Receive); - self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; - self.brd_write_command(OpCode::SetRx, &buffer).await?; - Ok(()) - } - - // Set the radio in reception mode with Boosted LNA gain and timeout specified - pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { - let buffer = [ - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - - self.brd_ant_set_rx()?; - - self.brd_set_operating_mode(RadioMode::Receive); - // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity - self.brd_write_registers(Register::RxGain, &[0x96u8]).await?; - self.brd_write_command(OpCode::SetRx, &buffer).await?; - Ok(()) - } - - // Set the Rx duty cycle management parameters - pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { - let buffer = [ - ((rx_time >> 16) & 0xFF) as u8, - ((rx_time >> 8) & 0xFF) as u8, - (rx_time & 0xFF) as u8, - ((sleep_time >> 16) & 0xFF) as u8, - ((sleep_time >> 8) & 0xFF) as u8, - (sleep_time & 0xFF) as u8, - ]; - - // antenna settings ??? - - self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?; - self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle); - Ok(()) - } - - // Set the radio in CAD mode - pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { - self.brd_ant_set_rx()?; - - self.brd_write_command(OpCode::SetCAD, &[]).await?; - self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); - Ok(()) - } - - // Set the radio in continuous wave transmission mode - pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { - self.brd_ant_set_tx()?; - - self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; - self.brd_set_operating_mode(RadioMode::Transmit); - Ok(()) - } - - // Set the radio in continuous preamble transmission mode - pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { - self.brd_ant_set_tx()?; - - self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; - self.brd_set_operating_mode(RadioMode::Transmit); - Ok(()) - } - - // Decide which interrupt will stop the internal radio rx timer. - // false timer stop after header/syncword detection - // true timer stop after preamble detection - pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect( - &mut self, - enable: bool, - ) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8]) - .await?; - Ok(()) - } - - // Set the number of symbols the radio will wait to validate a reception - pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError> { - let mut exp = 0u8; - let mut reg; - let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1; - while mant > 31 { - mant = (mant + 3) >> 2; - exp += 1; - } - reg = mant << ((2 * exp) + 1); - - self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?; - - if symb_num != 0 { - reg = exp + (mant << 3); - self.brd_write_registers(Register::SynchTimeout, &[reg]).await?; - } - - Ok(()) - } - - // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled - pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()]) - .await?; - Ok(()) - } - - // Calibrate the given radio block - pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError> { - self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()]) - .await?; - Ok(()) - } - - // Calibrate the image rejection based on the given frequency - pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError> { - let mut cal_freq = [0x00u8, 0x00u8]; - - if freq > 900000000 { - cal_freq[0] = 0xE1; - cal_freq[1] = 0xE9; - } else if freq > 850000000 { - cal_freq[0] = 0xD7; - cal_freq[1] = 0xDB; - } else if freq > 770000000 { - cal_freq[0] = 0xC1; - cal_freq[1] = 0xC5; - } else if freq > 460000000 { - cal_freq[0] = 0x75; - cal_freq[1] = 0x81; - } else if freq > 425000000 { - cal_freq[0] = 0x6B; - cal_freq[1] = 0x6F; - } - self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?; - Ok(()) - } - - // Activate the extention of the timeout when a long preamble is used - pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError> { - Ok(()) // no operation currently - } - - // Set the transmission parameters - // hp_max 0 for sx1261, 7 for sx1262 - // device_sel 1 for sx1261, 0 for sx1262 - // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT - pub(super) async fn sub_set_pa_config( - &mut self, - pa_duty_cycle: u8, - hp_max: u8, - device_sel: u8, - pa_lut: u8, - ) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut]) - .await?; - Ok(()) - } - - // Define into which mode the chip goes after a TX / RX done - pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode]) - .await?; - Ok(()) - } - - // Set the IRQ mask and DIO masks - pub(super) async fn sub_set_dio_irq_params( - &mut self, - irq_mask: u16, - dio1_mask: u16, - dio2_mask: u16, - dio3_mask: u16, - ) -> Result<(), RadioError> { - let mut buffer = [0x00u8; 8]; - - buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8; - buffer[1] = (irq_mask & 0x00FF) as u8; - buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8; - buffer[3] = (dio1_mask & 0x00FF) as u8; - buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8; - buffer[5] = (dio2_mask & 0x00FF) as u8; - buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8; - buffer[7] = (dio3_mask & 0x00FF) as u8; - self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?; - Ok(()) - } - - // Return the current IRQ status - pub(super) async fn sub_get_irq_status(&mut self) -> Result> { - let mut irq_status = [0x00u8, 0x00u8]; - self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?; - Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16)) - } - - // Indicate if DIO2 is used to control an RF Switch - pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?; - Ok(()) - } - - // Indicate if the radio main clock is supplied from a TCXO - // tcxo_voltage voltage used to control the TCXO on/off from DIO3 - // timeout duration given to the TCXO to go to 32MHz - pub(super) async fn sub_set_dio3_as_tcxo_ctrl( - &mut self, - tcxo_voltage: TcxoCtrlVoltage, - timeout: u32, - ) -> Result<(), RadioError> { - let buffer = [ - tcxo_voltage.value() & 0x07, - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?; - - Ok(()) - } - - // Set the RF frequency (Hz) - pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError> { - let mut buffer = [0x00u8; 4]; - - if !self.image_calibrated { - self.sub_calibrate_image(frequency).await?; - self.image_calibrated = true; - } - - let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency); - - buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8; - buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8; - buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8; - buffer[3] = (freq_in_pll_steps & 0xFF) as u8; - self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?; - Ok(()) - } - - // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters. - pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError> { - self.packet_type = packet_type; - self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()]) - .await?; - Ok(()) - } - - // Get the current radio protocol (LoRa or GFSK) - pub(super) fn sub_get_packet_type(&mut self) -> PacketType { - self.packet_type - } - - // Set the transmission parameters - // power RF output power [-18..13] dBm - // ramp_time transmission ramp up time - pub(super) async fn sub_set_tx_params( - &mut self, - mut power: i8, - ramp_time: RampTime, - ) -> Result<(), RadioError> { - if self.brd_get_radio_type() == RadioType::SX1261 { - if power == 15 { - self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?; - } else { - self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?; - } - - if power >= 14 { - power = 14; - } else if power < -17 { - power = -17; - } - } else { - // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) - let mut tx_clamp_cfg = [0x00u8]; - self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?; - tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1); - self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?; - - self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?; - - if power > 22 { - power = 22; - } else if power < -9 { - power = -9; - } - } - - // power conversion of negative number from i8 to u8 ??? - self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()]) - .await?; - Ok(()) - } - - // Set the modulation parameters - pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError> { - if self.modulation_params.is_some() { - let mut buffer = [0x00u8; 4]; - - // Since this driver only supports LoRa, ensure the packet type is set accordingly - self.sub_set_packet_type(PacketType::LoRa).await?; - - let modulation_params = self.modulation_params.unwrap(); - buffer[0] = modulation_params.spreading_factor.value(); - buffer[1] = modulation_params.bandwidth.value(); - buffer[2] = modulation_params.coding_rate.value(); - buffer[3] = modulation_params.low_data_rate_optimize; - - self.brd_write_command(OpCode::SetModulationParams, &buffer).await?; - Ok(()) - } else { - Err(RadioError::ModulationParamsMissing) - } - } - - // Set the packet parameters - pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError> { - if self.packet_params.is_some() { - let mut buffer = [0x00u8; 6]; - - // Since this driver only supports LoRa, ensure the packet type is set accordingly - self.sub_set_packet_type(PacketType::LoRa).await?; - - let packet_params = self.packet_params.unwrap(); - buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8; - buffer[1] = (packet_params.preamble_length & 0xFF) as u8; - buffer[2] = packet_params.implicit_header as u8; - buffer[3] = packet_params.payload_length; - buffer[4] = packet_params.crc_on as u8; - buffer[5] = packet_params.iq_inverted as u8; - - self.brd_write_command(OpCode::SetPacketParams, &buffer).await?; - Ok(()) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - // Set the channel activity detection (CAD) parameters - // symbols number of symbols to use for CAD operations - // det_peak limit for detection of SNR peak used in the CAD - // det_min minimum symbol recognition for CAD - // exit_mode operation to be done at the end of CAD action - // timeout timeout value to abort the CAD activity - - pub(super) async fn sub_set_cad_params( - &mut self, - symbols: CADSymbols, - det_peak: u8, - det_min: u8, - exit_mode: CADExitMode, - timeout: u32, - ) -> Result<(), RadioError> { - let mut buffer = [0x00u8; 7]; - - buffer[0] = symbols.value(); - buffer[1] = det_peak; - buffer[2] = det_min; - buffer[3] = exit_mode.value(); - buffer[4] = Self::timeout_1(timeout); - buffer[5] = Self::timeout_2(timeout); - buffer[6] = Self::timeout_3(timeout); - - self.brd_write_command(OpCode::SetCADParams, &buffer).await?; - self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); - Ok(()) - } - - // Set the data buffer base address for transmission and reception - pub(super) async fn sub_set_buffer_base_address( - &mut self, - tx_base_address: u8, - rx_base_address: u8, - ) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address]) - .await?; - Ok(()) - } - - // Get the current radio status - pub(super) async fn sub_get_status(&mut self) -> Result> { - let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?; - Ok(RadioStatus { - cmd_status: (status & (0x07 << 1)) >> 1, - chip_mode: (status & (0x07 << 4)) >> 4, - }) - } - - // Get the instantaneous RSSI value for the last packet received - pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { - let mut buffer = [0x00u8]; - self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; - let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ??? - Ok(rssi) - } - - // Get the last received packet buffer status - pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError> { - if self.packet_params.is_some() { - let mut status = [0x00u8; 2]; - let mut payload_length_buffer = [0x00u8]; - - self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?; - if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header { - self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer) - .await?; - } else { - payload_length_buffer[0] = status[0]; - } - - let payload_length = payload_length_buffer[0]; - let offset = status[1]; - - Ok((payload_length, offset)) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - // Get the last received packet payload status - pub(super) async fn sub_get_packet_status(&mut self) -> Result> { - let mut status = [0x00u8; 3]; - self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; - - // check this ??? - let rssi = ((-(status[0] as i32)) >> 1) as i8; - let snr = ((status[1] as i8) + 2) >> 2; - let signal_rssi = ((-(status[2] as i32)) >> 1) as i8; - let freq_error = self.frequency_error; - - Ok(PacketStatus { - rssi, - snr, - signal_rssi, - freq_error, - }) - } - - // Get the possible system errors - pub(super) async fn sub_get_device_errors(&mut self) -> Result> { - let mut errors = [0x00u8; 2]; - self.brd_read_command(OpCode::GetErrors, &mut errors).await?; - - Ok(RadioSystemError { - rc_64khz_calibration: (errors[1] & (1 << 0)) != 0, - rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0, - pll_calibration: (errors[1] & (1 << 2)) != 0, - adc_calibration: (errors[1] & (1 << 3)) != 0, - image_calibration: (errors[1] & (1 << 4)) != 0, - xosc_start: (errors[1] & (1 << 5)) != 0, - pll_lock: (errors[1] & (1 << 6)) != 0, - pa_ramp: (errors[0] & (1 << 0)) != 0, - }) - } - - // Clear all the errors in the device - pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError> { - self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?; - Ok(()) - } - - // Clear the IRQs - pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError> { - let mut buffer = [0x00u8, 0x00u8]; - buffer[0] = ((irq >> 8) & 0xFF) as u8; - buffer[1] = (irq & 0xFF) as u8; - self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?; - Ok(()) - } - - // Utility functions - - fn timeout_1(timeout: u32) -> u8 { - ((timeout >> 16) & 0xFF) as u8 - } - fn timeout_2(timeout: u32) -> u8 { - ((timeout >> 8) & 0xFF) as u8 - } - fn timeout_3(timeout: u32) -> u8 { - (timeout & 0xFF) as u8 - } - - // check this ??? - fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 { - let b0 = buffer[0] as u32; - let b1 = buffer[1] as u32; - let b2 = buffer[2] as u32; - let b3 = buffer[3] as u32; - (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 - } - - fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 { - // Get integer and fractional parts of the frequency computed with a PLL step scaled value - let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED; - let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED); - - (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT) - + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED) - } -} diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs deleted file mode 100644 index 4e8dc223..00000000 --- a/embassy-lora/src/sx127x/mod.rs +++ /dev/null @@ -1,192 +0,0 @@ -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::*; -use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; -use lorawan_device::async_device::Timings; - -mod sx127x_lora; -use sx127x_lora::{Error as RadioError, LoRa, RadioMode, IRQ}; - -/// Trait representing a radio switch for boards using the Sx127x radio. One some -/// boards, this will be a dummy implementation that does nothing. -pub trait RadioSwitch { - fn set_tx(&mut self); - fn set_rx(&mut self); -} - -/// Semtech Sx127x radio peripheral -pub struct Sx127xRadio -where - SPI: SpiBus + 'static, - E: 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, -{ - radio: LoRa, - rfs: RFS, - irq: I, -} - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum State { - Idle, - Txing, - Rxing, -} - -impl Sx127xRadio -where - SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, - E: 'static, -{ - pub async fn new( - spi: SPI, - cs: CS, - reset: RESET, - irq: I, - rfs: RFS, - ) -> Result> { - let mut radio = LoRa::new(spi, cs, reset); - radio.reset().await?; - Ok(Self { radio, irq, rfs }) - } -} - -impl Timings for Sx127xRadio -where - SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, -{ - fn get_rx_window_offset_ms(&self) -> i32 { - -3 - } - fn get_rx_window_duration_ms(&self) -> u32 { - 1003 - } -} - -impl PhyRxTx for Sx127xRadio -where - SPI: SpiBus + 'static, - CS: OutputPin + 'static, - E: 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, -{ - type PhyError = Sx127xError; - - async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - trace!("TX START"); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - self.rfs.set_tx(); - self.radio.set_tx_power(14, 0).await?; - self.radio.set_frequency(config.rf.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor)) - .await?; - - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_pa_ramp().await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(false).await?; - self.radio.set_crc(true).await?; - - self.radio.set_dio0_tx_done().await?; - - self.radio.transmit_start(buf).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { - trace!("TX DONE"); - return Ok(0); - } - } - } - - async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { - self.rfs.set_rx(); - self.radio.reset_payload_length().await?; - self.radio.set_frequency(config.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) - .await?; - - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(true).await?; - self.radio.set_crc(true).await?; - - self.radio.set_dio0_rx_done().await?; - self.radio.set_mode(RadioMode::RxContinuous).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { - let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16; - let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8; - let response = if let Ok(size) = self.radio.read_packet_size().await { - self.radio.read_packet(buf).await?; - Ok((size, RxQuality::new(rssi, snr))) - } else { - Ok((0, RxQuality::new(rssi, snr))) - }; - trace!("RX DONE"); - return response; - } - } - } -} - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Sx127xError; - -impl From> for Sx127xError { - fn from(_: sx127x_lora::Error) -> Self { - Sx127xError - } -} - -fn spreading_factor_to_u8(sf: SpreadingFactor) -> u8 { - match sf { - SpreadingFactor::_7 => 7, - SpreadingFactor::_8 => 8, - SpreadingFactor::_9 => 9, - SpreadingFactor::_10 => 10, - SpreadingFactor::_11 => 11, - SpreadingFactor::_12 => 12, - } -} - -fn bandwidth_to_i64(bw: Bandwidth) -> i64 { - match bw { - Bandwidth::_125KHz => 125_000, - Bandwidth::_250KHz => 250_000, - Bandwidth::_500KHz => 500_000, - } -} diff --git a/embassy-lora/src/sx127x/sx127x_lora/mod.rs b/embassy-lora/src/sx127x/sx127x_lora/mod.rs deleted file mode 100644 index aacc9da2..00000000 --- a/embassy-lora/src/sx127x/sx127x_lora/mod.rs +++ /dev/null @@ -1,539 +0,0 @@ -// Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 -// license -// -// Modifications made to make the driver work with the rust-lorawan link layer. - -#![allow(dead_code)] - -use bit_field::BitField; -use embassy_time::{Duration, Timer}; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::spi::SpiBus; - -mod register; -pub use self::register::IRQ; -use self::register::{PaConfig, Register}; - -/// Provides high-level access to Semtech SX1276/77/78/79 based boards connected to a Raspberry Pi -pub struct LoRa { - spi: SPI, - cs: CS, - reset: RESET, - pub explicit_header: bool, - pub mode: RadioMode, -} - -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - Uninformative, - VersionMismatch(u8), - CS(CS), - Reset(RESET), - SPI(SPI), - Transmitting, -} - -use Error::*; - -use super::sx127x_lora::register::{FskDataModulationShaping, FskRampUpRamDown}; - -#[cfg(not(feature = "version_0x09"))] -const VERSION_CHECK: u8 = 0x12; - -#[cfg(feature = "version_0x09")] -const VERSION_CHECK: u8 = 0x09; - -impl LoRa -where - SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, -{ - /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time. - /// This also preforms a hardware reset of the module and then puts it in standby. - pub fn new(spi: SPI, cs: CS, reset: RESET) -> Self { - Self { - spi, - cs, - reset, - explicit_header: true, - mode: RadioMode::Sleep, - } - } - - pub async fn reset(&mut self) -> Result<(), Error> { - self.reset.set_low().map_err(Reset)?; - Timer::after(Duration::from_millis(10)).await; - self.reset.set_high().map_err(Reset)?; - Timer::after(Duration::from_millis(10)).await; - let version = self.read_register(Register::RegVersion.addr()).await?; - if version == VERSION_CHECK { - self.set_mode(RadioMode::Sleep).await?; - self.write_register(Register::RegFifoTxBaseAddr.addr(), 0).await?; - self.write_register(Register::RegFifoRxBaseAddr.addr(), 0).await?; - let lna = self.read_register(Register::RegLna.addr()).await?; - self.write_register(Register::RegLna.addr(), lna | 0x03).await?; - self.write_register(Register::RegModemConfig3.addr(), 0x04).await?; - self.set_tcxo(true).await?; - self.set_mode(RadioMode::Stdby).await?; - self.cs.set_high().map_err(CS)?; - Ok(()) - } else { - Err(Error::VersionMismatch(version)) - } - } - - pub async fn set_dio0_tx_done(&mut self) -> Result<(), Error> { - self.write_register(Register::RegIrqFlagsMask.addr(), 0b1111_0111) - .await?; - let mapping = self.read_register(Register::RegDioMapping1.addr()).await?; - self.write_register(Register::RegDioMapping1.addr(), (mapping & 0x3F) | 0x40) - .await - } - - pub async fn set_dio0_rx_done(&mut self) -> Result<(), Error> { - self.write_register(Register::RegIrqFlagsMask.addr(), 0b0001_1111) - .await?; - let mapping = self.read_register(Register::RegDioMapping1.addr()).await?; - self.write_register(Register::RegDioMapping1.addr(), mapping & 0x3F) - .await - } - - pub async fn transmit_start(&mut self, buffer: &[u8]) -> Result<(), Error> { - assert!(buffer.len() < 255); - if self.transmitting().await? { - //trace!("ALREADY TRANSMNITTING"); - Err(Transmitting) - } else { - self.set_mode(RadioMode::Stdby).await?; - if self.explicit_header { - self.set_explicit_header_mode().await?; - } else { - self.set_implicit_header_mode().await?; - } - - self.write_register(Register::RegIrqFlags.addr(), 0).await?; - self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?; - self.write_register(Register::RegPayloadLength.addr(), 0).await?; - for byte in buffer.iter() { - self.write_register(Register::RegFifo.addr(), *byte).await?; - } - self.write_register(Register::RegPayloadLength.addr(), buffer.len() as u8) - .await?; - self.set_mode(RadioMode::Tx).await?; - Ok(()) - } - } - - pub async fn packet_ready(&mut self) -> Result> { - Ok(self.read_register(Register::RegIrqFlags.addr()).await?.get_bit(6)) - } - - pub async fn irq_flags_mask(&mut self) -> Result> { - Ok(self.read_register(Register::RegIrqFlagsMask.addr()).await? as u8) - } - - pub async fn irq_flags(&mut self) -> Result> { - Ok(self.read_register(Register::RegIrqFlags.addr()).await? as u8) - } - - pub async fn read_packet_size(&mut self) -> Result> { - let size = self.read_register(Register::RegRxNbBytes.addr()).await?; - Ok(size as usize) - } - - /// Returns the contents of the fifo as a fixed 255 u8 array. This should only be called is there is a - /// new packet ready to be read. - pub async fn read_packet(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.clear_irq().await?; - let size = self.read_register(Register::RegRxNbBytes.addr()).await?; - assert!(size as usize <= buffer.len()); - let fifo_addr = self.read_register(Register::RegFifoRxCurrentAddr.addr()).await?; - self.write_register(Register::RegFifoAddrPtr.addr(), fifo_addr).await?; - for i in 0..size { - let byte = self.read_register(Register::RegFifo.addr()).await?; - buffer[i as usize] = byte; - } - self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?; - Ok(()) - } - - /// Returns true if the radio is currently transmitting a packet. - pub async fn transmitting(&mut self) -> Result> { - if (self.read_register(Register::RegOpMode.addr()).await?) & RadioMode::Tx.addr() == RadioMode::Tx.addr() { - Ok(true) - } else { - if (self.read_register(Register::RegIrqFlags.addr()).await? & IRQ::IrqTxDoneMask.addr()) == 1 { - self.write_register(Register::RegIrqFlags.addr(), IRQ::IrqTxDoneMask.addr()) - .await?; - } - Ok(false) - } - } - - /// Clears the radio's IRQ registers. - pub async fn clear_irq(&mut self) -> Result> { - let irq_flags = self.read_register(Register::RegIrqFlags.addr()).await?; - self.write_register(Register::RegIrqFlags.addr(), 0xFF).await?; - Ok(irq_flags) - } - - /// Sets the transmit power and pin. Levels can range from 0-14 when the output - /// pin = 0(RFO), and form 0-20 when output pin = 1(PaBoost). Power is in dB. - /// Default value is `17`. - pub async fn set_tx_power( - &mut self, - mut level: i32, - output_pin: u8, - ) -> Result<(), Error> { - if PaConfig::PaOutputRfoPin.addr() == output_pin { - // RFO - if level < 0 { - level = 0; - } else if level > 14 { - level = 14; - } - self.write_register(Register::RegPaConfig.addr(), (0x70 | level) as u8) - .await - } else { - // PA BOOST - if level > 17 { - if level > 20 { - level = 20; - } - // subtract 3 from level, so 18 - 20 maps to 15 - 17 - level -= 3; - - // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) - self.write_register(Register::RegPaDac.addr(), 0x87).await?; - self.set_ocp(140).await?; - } else { - if level < 2 { - level = 2; - } - //Default value PA_HF/LF or +17dBm - self.write_register(Register::RegPaDac.addr(), 0x84).await?; - self.set_ocp(100).await?; - } - level -= 2; - self.write_register(Register::RegPaConfig.addr(), PaConfig::PaBoost.addr() | level as u8) - .await - } - } - - pub async fn get_modem_stat(&mut self) -> Result> { - Ok(self.read_register(Register::RegModemStat.addr()).await? as u8) - } - - /// Sets the over current protection on the radio(mA). - pub async fn set_ocp(&mut self, ma: u8) -> Result<(), Error> { - let mut ocp_trim: u8 = 27; - - if ma <= 120 { - ocp_trim = (ma - 45) / 5; - } else if ma <= 240 { - ocp_trim = (ma + 30) / 10; - } - self.write_register(Register::RegOcp.addr(), 0x20 | (0x1F & ocp_trim)) - .await - } - - /// Sets the state of the radio. Default mode after initiation is `Standby`. - pub async fn set_mode(&mut self, mode: RadioMode) -> Result<(), Error> { - if self.explicit_header { - self.set_explicit_header_mode().await?; - } else { - self.set_implicit_header_mode().await?; - } - self.write_register( - Register::RegOpMode.addr(), - RadioMode::LongRangeMode.addr() | mode.addr(), - ) - .await?; - - self.mode = mode; - Ok(()) - } - - pub async fn reset_payload_length(&mut self) -> Result<(), Error> { - self.write_register(Register::RegPayloadLength.addr(), 0xFF).await - } - - /// Sets the frequency of the radio. Values are in megahertz. - /// I.E. 915 MHz must be used for North America. Check regulation for your area. - pub async fn set_frequency(&mut self, freq: u32) -> Result<(), Error> { - const FREQ_STEP: f64 = 61.03515625; - // calculate register values - let frf = (freq as f64 / FREQ_STEP) as u32; - // write registers - self.write_register(Register::RegFrfMsb.addr(), ((frf & 0x00FF_0000) >> 16) as u8) - .await?; - self.write_register(Register::RegFrfMid.addr(), ((frf & 0x0000_FF00) >> 8) as u8) - .await?; - self.write_register(Register::RegFrfLsb.addr(), (frf & 0x0000_00FF) as u8) - .await - } - - /// Sets the radio to use an explicit header. Default state is `ON`. - async fn set_explicit_header_mode(&mut self) -> Result<(), Error> { - let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0xfe) - .await?; - self.explicit_header = true; - Ok(()) - } - - /// Sets the radio to use an implicit header. Default state is `OFF`. - async fn set_implicit_header_mode(&mut self) -> Result<(), Error> { - let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0x01) - .await?; - self.explicit_header = false; - Ok(()) - } - - /// Sets the spreading factor of the radio. Supported values are between 6 and 12. - /// If a spreading factor of 6 is set, implicit header mode must be used to transmit - /// and receive packets. Default value is `7`. - pub async fn set_spreading_factor(&mut self, mut sf: u8) -> Result<(), Error> { - if sf < 6 { - sf = 6; - } else if sf > 12 { - sf = 12; - } - - if sf == 6 { - self.write_register(Register::RegDetectionOptimize.addr(), 0xc5).await?; - self.write_register(Register::RegDetectionThreshold.addr(), 0x0c) - .await?; - } else { - self.write_register(Register::RegDetectionOptimize.addr(), 0xc3).await?; - self.write_register(Register::RegDetectionThreshold.addr(), 0x0a) - .await?; - } - let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?; - self.write_register( - Register::RegModemConfig2.addr(), - (modem_config_2 & 0x0f) | ((sf << 4) & 0xf0), - ) - .await?; - self.set_ldo_flag().await?; - - self.write_register(Register::RegSymbTimeoutLsb.addr(), 0x05).await?; - - Ok(()) - } - - pub async fn set_tcxo(&mut self, external: bool) -> Result<(), Error> { - if external { - self.write_register(Register::RegTcxo.addr(), 0x10).await - } else { - self.write_register(Register::RegTcxo.addr(), 0x00).await - } - } - - /// Sets the signal bandwidth of the radio. Supported values are: `7800 Hz`, `10400 Hz`, - /// `15600 Hz`, `20800 Hz`, `31250 Hz`,`41700 Hz` ,`62500 Hz`,`125000 Hz` and `250000 Hz` - /// Default value is `125000 Hz` - pub async fn set_signal_bandwidth(&mut self, sbw: i64) -> Result<(), Error> { - let bw: i64 = match sbw { - 7_800 => 0, - 10_400 => 1, - 15_600 => 2, - 20_800 => 3, - 31_250 => 4, - 41_700 => 5, - 62_500 => 6, - 125_000 => 7, - 250_000 => 8, - _ => 9, - }; - let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register( - Register::RegModemConfig1.addr(), - (modem_config_1 & 0x0f) | ((bw << 4) as u8), - ) - .await?; - self.set_ldo_flag().await?; - Ok(()) - } - - /// Sets the coding rate of the radio with the numerator fixed at 4. Supported values - /// are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. - /// Default value is `5`. - pub async fn set_coding_rate_4(&mut self, mut denominator: u8) -> Result<(), Error> { - if denominator < 5 { - denominator = 5; - } else if denominator > 8 { - denominator = 8; - } - let cr = denominator - 4; - let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register(Register::RegModemConfig1.addr(), (modem_config_1 & 0xf1) | (cr << 1)) - .await - } - - /// Sets the preamble length of the radio. Values are between 6 and 65535. - /// Default value is `8`. - pub async fn set_preamble_length(&mut self, length: i64) -> Result<(), Error> { - self.write_register(Register::RegPreambleMsb.addr(), (length >> 8) as u8) - .await?; - self.write_register(Register::RegPreambleLsb.addr(), length as u8).await - } - - /// Enables are disables the radio's CRC check. Default value is `false`. - pub async fn set_crc(&mut self, value: bool) -> Result<(), Error> { - let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?; - if value { - self.write_register(Register::RegModemConfig2.addr(), modem_config_2 | 0x04) - .await - } else { - self.write_register(Register::RegModemConfig2.addr(), modem_config_2 & 0xfb) - .await - } - } - - /// Inverts the radio's IQ signals. Default value is `false`. - pub async fn set_invert_iq(&mut self, value: bool) -> Result<(), Error> { - if value { - self.write_register(Register::RegInvertiq.addr(), 0x66).await?; - self.write_register(Register::RegInvertiq2.addr(), 0x19).await - } else { - self.write_register(Register::RegInvertiq.addr(), 0x27).await?; - self.write_register(Register::RegInvertiq2.addr(), 0x1d).await - } - } - - /// Returns the spreading factor of the radio. - pub async fn get_spreading_factor(&mut self) -> Result> { - Ok(self.read_register(Register::RegModemConfig2.addr()).await? >> 4) - } - - /// Returns the signal bandwidth of the radio. - pub async fn get_signal_bandwidth(&mut self) -> Result> { - let bw = self.read_register(Register::RegModemConfig1.addr()).await? >> 4; - let bw = match bw { - 0 => 7_800, - 1 => 10_400, - 2 => 15_600, - 3 => 20_800, - 4 => 31_250, - 5 => 41_700, - 6 => 62_500, - 7 => 125_000, - 8 => 250_000, - 9 => 500_000, - _ => -1, - }; - Ok(bw) - } - - /// Returns the RSSI of the last received packet. - pub async fn get_packet_rssi(&mut self) -> Result> { - Ok(i32::from(self.read_register(Register::RegPktRssiValue.addr()).await?) - 157) - } - - /// Returns the signal to noise radio of the the last received packet. - pub async fn get_packet_snr(&mut self) -> Result> { - Ok(f64::from(self.read_register(Register::RegPktSnrValue.addr()).await?)) - } - - /// Returns the frequency error of the last received packet in Hz. - pub async fn get_packet_frequency_error(&mut self) -> Result> { - let mut freq_error: i32; - freq_error = i32::from(self.read_register(Register::RegFreqErrorMsb.addr()).await? & 0x7); - freq_error <<= 8i64; - freq_error += i32::from(self.read_register(Register::RegFreqErrorMid.addr()).await?); - freq_error <<= 8i64; - freq_error += i32::from(self.read_register(Register::RegFreqErrorLsb.addr()).await?); - - let f_xtal = 32_000_000; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) - let f_error = ((f64::from(freq_error) * (1i64 << 24) as f64) / f64::from(f_xtal)) - * (self.get_signal_bandwidth().await? as f64 / 500_000.0f64); // p. 37 - Ok(f_error as i64) - } - - async fn set_ldo_flag(&mut self) -> Result<(), Error> { - let sw = self.get_signal_bandwidth().await?; - // Section 4.1.1.5 - let symbol_duration = 1000 / (sw / ((1_i64) << self.get_spreading_factor().await?)); - - // Section 4.1.1.6 - let ldo_on = symbol_duration > 16; - - let mut config_3 = self.read_register(Register::RegModemConfig3.addr()).await?; - config_3.set_bit(3, ldo_on); - //config_3.set_bit(2, true); - self.write_register(Register::RegModemConfig3.addr(), config_3).await - } - - async fn read_register(&mut self, reg: u8) -> Result> { - let mut buffer = [reg & 0x7f, 0]; - self.cs.set_low().map_err(CS)?; - - let _ = self.spi.transfer(&mut buffer, &[reg & 0x7f, 0]).await.map_err(SPI)?; - - self.cs.set_high().map_err(CS)?; - Ok(buffer[1]) - } - - async fn write_register(&mut self, reg: u8, byte: u8) -> Result<(), Error> { - self.cs.set_low().map_err(CS)?; - let buffer = [reg | 0x80, byte]; - self.spi.write(&buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(CS)?; - Ok(()) - } - - pub async fn put_in_fsk_mode(&mut self) -> Result<(), Error> { - // Put in FSK mode - let mut op_mode = 0; - op_mode - .set_bit(7, false) // FSK mode - .set_bits(5..6, 0x00) // FSK modulation - .set_bit(3, false) //Low freq registers - .set_bits(0..2, 0b011); // Mode - - self.write_register(Register::RegOpMode as u8, op_mode).await - } - - pub async fn set_fsk_pa_ramp( - &mut self, - modulation_shaping: FskDataModulationShaping, - ramp: FskRampUpRamDown, - ) -> Result<(), Error> { - let mut pa_ramp = 0; - pa_ramp - .set_bits(5..6, modulation_shaping as u8) - .set_bits(0..3, ramp as u8); - - self.write_register(Register::RegPaRamp as u8, pa_ramp).await - } - - pub async fn set_lora_pa_ramp(&mut self) -> Result<(), Error> { - self.write_register(Register::RegPaRamp as u8, 0b1000).await - } - - pub async fn set_lora_sync_word(&mut self) -> Result<(), Error> { - self.write_register(Register::RegSyncWord as u8, 0x34).await - } -} -/// Modes of the radio and their corresponding register values. -#[derive(Clone, Copy)] -pub enum RadioMode { - LongRangeMode = 0x80, - Sleep = 0x00, - Stdby = 0x01, - Tx = 0x03, - RxContinuous = 0x05, - RxSingle = 0x06, -} - -impl RadioMode { - /// Returns the address of the mode. - pub fn addr(self) -> u8 { - self as u8 - } -} diff --git a/embassy-lora/src/sx127x/sx127x_lora/register.rs b/embassy-lora/src/sx127x/sx127x_lora/register.rs deleted file mode 100644 index 2445e21b..00000000 --- a/embassy-lora/src/sx127x/sx127x_lora/register.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 -// license -// -// Modifications made to make the driver work with the rust-lorawan link layer. -#![allow(dead_code, clippy::enum_variant_names)] - -#[derive(Clone, Copy)] -pub enum Register { - RegFifo = 0x00, - RegOpMode = 0x01, - RegFrfMsb = 0x06, - RegFrfMid = 0x07, - RegFrfLsb = 0x08, - RegPaConfig = 0x09, - RegPaRamp = 0x0a, - RegOcp = 0x0b, - RegLna = 0x0c, - RegFifoAddrPtr = 0x0d, - RegFifoTxBaseAddr = 0x0e, - RegFifoRxBaseAddr = 0x0f, - RegFifoRxCurrentAddr = 0x10, - RegIrqFlagsMask = 0x11, - RegIrqFlags = 0x12, - RegRxNbBytes = 0x13, - RegPktSnrValue = 0x19, - RegModemStat = 0x18, - RegPktRssiValue = 0x1a, - RegModemConfig1 = 0x1d, - RegModemConfig2 = 0x1e, - RegSymbTimeoutLsb = 0x1f, - RegPreambleMsb = 0x20, - RegPreambleLsb = 0x21, - RegPayloadLength = 0x22, - RegMaxPayloadLength = 0x23, - RegModemConfig3 = 0x26, - RegFreqErrorMsb = 0x28, - RegFreqErrorMid = 0x29, - RegFreqErrorLsb = 0x2a, - RegRssiWideband = 0x2c, - RegDetectionOptimize = 0x31, - RegInvertiq = 0x33, - RegDetectionThreshold = 0x37, - RegSyncWord = 0x39, - RegInvertiq2 = 0x3b, - RegDioMapping1 = 0x40, - RegVersion = 0x42, - RegTcxo = 0x4b, - RegPaDac = 0x4d, -} -#[derive(Clone, Copy)] -pub enum PaConfig { - PaBoost = 0x80, - PaOutputRfoPin = 0, -} - -#[derive(Clone, Copy)] -pub enum IRQ { - IrqTxDoneMask = 0x08, - IrqPayloadCrcErrorMask = 0x20, - IrqRxDoneMask = 0x40, -} - -impl Register { - pub fn addr(self) -> u8 { - self as u8 - } -} - -impl PaConfig { - pub fn addr(self) -> u8 { - self as u8 - } -} - -impl IRQ { - pub fn addr(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum FskDataModulationShaping { - None = 1, - GaussianBt1d0 = 2, - GaussianBt0d5 = 10, - GaussianBt0d3 = 11, -} - -#[derive(Clone, Copy)] -pub enum FskRampUpRamDown { - _3d4ms = 0b000, - _2ms = 0b0001, - _1ms = 0b0010, - _500us = 0b0011, - _250us = 0b0100, - _125us = 0b0101, - _100us = 0b0110, - _62us = 0b0111, - _50us = 0b1000, - _40us = 0b1001, - _31us = 0b1010, - _25us = 0b1011, - _20us = 0b1100, - _15us = 0b1101, - _12us = 0b1110, - _10us = 0b1111, -} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index f0fc152c..24a26edd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -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"))] diff --git a/embassy-stm32/src/subghz/bit_sync.rs b/embassy-stm32/src/subghz/bit_sync.rs deleted file mode 100644 index f3cba05f..00000000 --- a/embassy-stm32/src/subghz/bit_sync.rs +++ /dev/null @@ -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 for u8 { - fn from(bs: BitSync) -> Self { - bs.val - } -} - -impl Default for BitSync { - fn default() -> Self { - Self::RESET - } -} diff --git a/embassy-stm32/src/subghz/cad_params.rs b/embassy-stm32/src/subghz/cad_params.rs deleted file mode 100644 index 1d90ff70..00000000 --- a/embassy-stm32/src/subghz/cad_params.rs +++ /dev/null @@ -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() - } -} diff --git a/embassy-stm32/src/subghz/calibrate.rs b/embassy-stm32/src/subghz/calibrate.rs deleted file mode 100644 index f94538f8..00000000 --- a/embassy-stm32/src/subghz/calibrate.rs +++ /dev/null @@ -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 - } -} diff --git a/embassy-stm32/src/subghz/fallback_mode.rs b/embassy-stm32/src/subghz/fallback_mode.rs deleted file mode 100644 index 50ec592f..00000000 --- a/embassy-stm32/src/subghz/fallback_mode.rs +++ /dev/null @@ -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 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 - } -} diff --git a/embassy-stm32/src/subghz/hse_trim.rs b/embassy-stm32/src/subghz/hse_trim.rs deleted file mode 100644 index edfd52ac..00000000 --- a/embassy-stm32/src/subghz/hse_trim.rs +++ /dev/null @@ -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> { - 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 for u8 { - fn from(ht: HseTrim) -> Self { - ht.val - } -} - -impl Default for HseTrim { - fn default() -> Self { - Self::POR - } -} diff --git a/embassy-stm32/src/subghz/irq.rs b/embassy-stm32/src/subghz/irq.rs deleted file mode 100644 index b56b8ad9..00000000 --- a/embassy-stm32/src/subghz/irq.rs +++ /dev/null @@ -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() - } -} diff --git a/embassy-stm32/src/subghz/lora_sync_word.rs b/embassy-stm32/src/subghz/lora_sync_word.rs deleted file mode 100644 index 2c163104..00000000 --- a/embassy-stm32/src/subghz/lora_sync_word.rs +++ /dev/null @@ -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], - } - } -} diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs deleted file mode 100644 index cd566ba2..00000000 --- a/embassy-stm32/src/subghz/mod.rs +++ /dev/null @@ -1,1004 +0,0 @@ -//! Sub-GHz radio operating in the 150 - 960 MHz ISM band -//! -//! The main radio type is [`SubGhz`]. -//! -//! ## LoRa user notice -//! -//! The Sub-GHz radio may have an undocumented erratum, see this ST community -//! post for more information: [link] -//! -//! [link]: https://community.st.com/s/question/0D53W00000hR8kpSAC/stm32wl55-erratum-clairification -//! -//! NOTE: This HAL is based on https://github.com/newAM/stm32wl-hal, but adopted for use with the stm32-metapac -//! and SPI HALs. - -mod bit_sync; -mod cad_params; -mod calibrate; -mod fallback_mode; -mod hse_trim; -mod irq; -mod lora_sync_word; -mod mod_params; -mod ocp; -mod op_error; -mod pa_config; -mod packet_params; -mod packet_status; -mod packet_type; -mod pkt_ctrl; -mod pmode; -mod pwr_ctrl; -mod reg_mode; -mod rf_frequency; -mod rx_timeout_stop; -mod sleep_cfg; -mod smps; -mod standby_clk; -mod stats; -mod status; -mod tcxo_mode; -mod timeout; -mod tx_params; -mod value_error; - -pub use bit_sync::BitSync; -pub use cad_params::{CadParams, ExitMode, NbCadSymbol}; -pub use calibrate::{Calibrate, CalibrateImage}; -use embassy_hal_common::ratio::Ratio; -pub use fallback_mode::FallbackMode; -pub use hse_trim::HseTrim; -pub use irq::{CfgIrq, Irq, IrqLine}; -pub use lora_sync_word::LoRaSyncWord; -pub use mod_params::{ - BpskModParams, CodingRate, FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape, LoRaBandwidth, - LoRaModParams, SpreadingFactor, -}; -pub use ocp::Ocp; -pub use op_error::OpError; -pub use pa_config::{PaConfig, PaSel}; -pub use packet_params::{ - AddrComp, BpskPacketParams, CrcType, GenericPacketParams, HeaderType, LoRaPacketParams, PreambleDetection, -}; -pub use packet_status::{FskPacketStatus, LoRaPacketStatus}; -pub use packet_type::PacketType; -pub use pkt_ctrl::{InfSeqSel, PktCtrl}; -pub use pmode::PMode; -pub use pwr_ctrl::{CurrentLim, PwrCtrl}; -pub use reg_mode::RegMode; -pub use rf_frequency::RfFreq; -pub use rx_timeout_stop::RxTimeoutStop; -pub use sleep_cfg::{SleepCfg, Startup}; -pub use smps::SmpsDrv; -pub use standby_clk::StandbyClk; -pub use stats::{FskStats, LoRaStats, Stats}; -pub use status::{CmdStatus, Status, StatusMode}; -pub use tcxo_mode::{TcxoMode, TcxoTrim}; -pub use timeout::Timeout; -pub use tx_params::{RampTime, TxParams}; -pub use value_error::ValueError; - -use crate::dma::NoDma; -use crate::peripherals::SUBGHZSPI; -use crate::rcc::sealed::RccPeripheral; -use crate::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; -use crate::time::Hertz; -use crate::{pac, Peripheral}; - -/// Passthrough for SPI errors (for now) -pub type Error = crate::spi::Error; - -struct Nss { - _priv: (), -} - -impl Nss { - #[inline(always)] - pub fn new() -> Nss { - Self::clear(); - Nss { _priv: () } - } - - /// Clear NSS, enabling SPI transactions - #[inline(always)] - fn clear() { - let pwr = pac::PWR; - unsafe { - pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW)); - } - } - - /// Set NSS, disabling SPI transactions - #[inline(always)] - fn set() { - let pwr = pac::PWR; - unsafe { - pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH)); - } - } -} - -impl Drop for Nss { - fn drop(&mut self) { - Self::set() - } -} - -/// Wakeup the radio from sleep mode. -/// -/// # Safety -/// -/// 1. This must not be called when the SubGHz radio is in use. -/// 2. This must not be called when the SubGHz SPI bus is in use. -/// -/// # Example -/// -/// See [`SubGhz::set_sleep`] -#[inline] -unsafe fn wakeup() { - Nss::clear(); - // RM0453 rev 2 page 171 section 5.7.2 "Sleep mode" - // on a firmware request via the sub-GHz radio SPI NSS signal - // (keeping sub-GHz radio SPI NSS low for at least 20 μs) - // - // I have found this to be a more reliable mechanism for ensuring NSS is - // pulled low for long enough to wake the radio. - while rfbusys() {} - Nss::set(); -} - -/// Returns `true` if the radio is busy. -/// -/// This may not be set immediately after NSS going low. -/// -/// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more -/// details. -#[inline] -fn rfbusys() -> bool { - // safety: atmoic read with no side-effects - //unsafe { (*pac::PWR::ptr()).sr2.read().rfbusys().is_busy() } - let pwr = pac::PWR; - unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } -} - -/* -/// Returns `true` if the radio is busy or NSS is low. -/// -/// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more -/// details. -#[inline] -fn rfbusyms() -> bool { - let pwr = pac::PWR; - unsafe { pwr.sr2().read().rfbusyms() == pac::pwr::vals::Rfbusyms::BUSY } -} -*/ - -/// Sub-GHz radio peripheral -pub struct SubGhz<'d, Tx, Rx> { - spi: Spi<'d, SUBGHZSPI, Tx, Rx>, -} - -impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { - fn pulse_radio_reset() { - let rcc = pac::RCC; - unsafe { - rcc.csr().modify(|w| w.set_rfrst(true)); - rcc.csr().modify(|w| w.set_rfrst(false)); - } - } - - // TODO: This should be replaced with async handling based on IRQ - fn poll_not_busy(&self) { - let mut count: u32 = 1_000_000; - while rfbusys() { - count -= 1; - if count == 0 { - let pwr = pac::PWR; - unsafe { - panic!( - "rfbusys timeout pwr.sr2=0x{:X} pwr.subghzspicr=0x{:X} pwr.cr1=0x{:X}", - pwr.sr2().read().0, - pwr.subghzspicr().read().0, - pwr.cr1().read().0 - ); - } - } - } - } - - /// Create a new sub-GHz radio driver from a peripheral. - /// - /// This will reset the radio and the SPI bus, and enable the peripheral - /// clock. - pub fn new( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - ) -> Self { - Self::pulse_radio_reset(); - - // see RM0453 rev 1 section 7.2.13 page 291 - // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. - // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. - let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); - let mut config = SpiConfig::default(); - config.mode = MODE_0; - config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new_subghz(peri, txdma, rxdma, clk, config); - - unsafe { wakeup() }; - - SubGhz { spi } - } - - pub fn is_busy(&mut self) -> bool { - rfbusys() - } - - pub fn reset(&mut self) { - Self::pulse_radio_reset(); - } -} - -impl<'d> SubGhz<'d, NoDma, NoDma> { - fn read(&mut self, opcode: OpCode, data: &mut [u8]) -> Result<(), Error> { - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[opcode as u8])?; - self.spi.blocking_transfer_in_place(data)?; - } - self.poll_not_busy(); - Ok(()) - } - - /// Read one byte from the sub-Ghz radio. - fn read_1(&mut self, opcode: OpCode) -> Result { - let mut buf: [u8; 1] = [0; 1]; - self.read(opcode, &mut buf)?; - Ok(buf[0]) - } - - /// Read a fixed number of bytes from the sub-Ghz radio. - fn read_n(&mut self, opcode: OpCode) -> Result<[u8; N], Error> { - let mut buf: [u8; N] = [0; N]; - self.read(opcode, &mut buf)?; - Ok(buf) - } - - fn write(&mut self, data: &[u8]) -> Result<(), Error> { - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(data)?; - } - self.poll_not_busy(); - Ok(()) - } - - pub fn write_buffer(&mut self, offset: u8, data: &[u8]) -> Result<(), Error> { - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[OpCode::WriteBuffer as u8, offset])?; - self.spi.blocking_write(data)?; - } - self.poll_not_busy(); - - Ok(()) - } - - /// Read the radio buffer at the given offset. - /// - /// The offset and length of a received packet is provided by - /// [`rx_buffer_status`](Self::rx_buffer_status). - pub fn read_buffer(&mut self, offset: u8, buf: &mut [u8]) -> Result { - let mut status_buf: [u8; 1] = [0]; - - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[OpCode::ReadBuffer as u8, offset])?; - self.spi.blocking_transfer_in_place(&mut status_buf)?; - self.spi.blocking_transfer_in_place(buf)?; - } - self.poll_not_busy(); - - Ok(status_buf[0].into()) - } -} - -// helper to pack register writes into a single buffer to avoid multiple DMA -// transfers -macro_rules! wr_reg { - [$reg:ident, $($data:expr),+] => { - &[ - OpCode::WriteRegister as u8, - Register::$reg.address().to_be_bytes()[0], - Register::$reg.address().to_be_bytes()[1], - $($data),+ - ] - }; -} - -// 5.8.2 -/// Register access -impl<'d> SubGhz<'d, NoDma, NoDma> { - // register write with variable length data - fn write_register(&mut self, register: Register, data: &[u8]) -> Result<(), Error> { - let addr: [u8; 2] = register.address().to_be_bytes(); - - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi - .blocking_write(&[OpCode::WriteRegister as u8, addr[0], addr[1]])?; - self.spi.blocking_write(data)?; - } - self.poll_not_busy(); - - Ok(()) - } - - /// Set the LoRa bit synchronization. - pub fn set_bit_sync(&mut self, bs: BitSync) -> Result<(), Error> { - self.write(wr_reg![GBSYNC, bs.as_bits()]) - } - - /// Set the generic packet control register. - pub fn set_pkt_ctrl(&mut self, pkt_ctrl: PktCtrl) -> Result<(), Error> { - self.write(wr_reg![GPKTCTL1A, pkt_ctrl.as_bits()]) - } - - /// Set the initial value for generic packet whitening. - /// - /// This sets the first 8 bits, the 9th bit is set with - /// [`set_pkt_ctrl`](Self::set_pkt_ctrl). - pub fn set_init_whitening(&mut self, init: u8) -> Result<(), Error> { - self.write(wr_reg![GWHITEINIRL, init]) - } - - /// Set the initial value for generic packet CRC polynomial. - pub fn set_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> { - let bytes: [u8; 2] = polynomial.to_be_bytes(); - self.write(wr_reg![GCRCINIRH, bytes[0], bytes[1]]) - } - - /// Set the generic packet CRC polynomial. - pub fn set_initial_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> { - let bytes: [u8; 2] = polynomial.to_be_bytes(); - self.write(wr_reg![GCRCPOLRH, bytes[0], bytes[1]]) - } - - /// Set the synchronization word registers. - pub fn set_sync_word(&mut self, sync_word: &[u8; 8]) -> Result<(), Error> { - self.write_register(Register::GSYNC7, sync_word) - } - - /// Set the LoRa synchronization word registers. - pub fn set_lora_sync_word(&mut self, sync_word: LoRaSyncWord) -> Result<(), Error> { - let bytes: [u8; 2] = sync_word.bytes(); - self.write(wr_reg![LSYNCH, bytes[0], bytes[1]]) - } - - /// Set the RX gain control. - pub fn set_rx_gain(&mut self, pmode: PMode) -> Result<(), Error> { - self.write(wr_reg![RXGAINC, pmode as u8]) - } - - /// Set the power amplifier over current protection. - pub fn set_pa_ocp(&mut self, ocp: Ocp) -> Result<(), Error> { - self.write(wr_reg![PAOCP, ocp as u8]) - } - - /// Restart the radio RTC. - /// - /// This is used to workaround an erratum for [`set_rx_duty_cycle`]. - /// - /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle - pub fn restart_rtc(&mut self) -> Result<(), Error> { - self.write(wr_reg![RTCCTLR, 0b1]) - } - - /// Set the radio real-time-clock period. - /// - /// This is used to workaround an erratum for [`set_rx_duty_cycle`]. - /// - /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle - pub fn set_rtc_period(&mut self, period: Timeout) -> Result<(), Error> { - let tobits: u32 = period.into_bits(); - self.write(wr_reg![ - RTCPRDR2, - (tobits >> 16) as u8, - (tobits >> 8) as u8, - tobits as u8 - ]) - } - - /// Set the HSE32 crystal OSC_IN load capacitor trimming. - pub fn set_hse_in_trim(&mut self, trim: HseTrim) -> Result<(), Error> { - self.write(wr_reg![HSEINTRIM, trim.into()]) - } - - /// Set the HSE32 crystal OSC_OUT load capacitor trimming. - pub fn set_hse_out_trim(&mut self, trim: HseTrim) -> Result<(), Error> { - self.write(wr_reg![HSEOUTTRIM, trim.into()]) - } - - /// Set the SMPS clock detection enabled. - /// - /// SMPS clock detection must be enabled fore enabling the SMPS. - pub fn set_smps_clock_det_en(&mut self, en: bool) -> Result<(), Error> { - self.write(wr_reg![SMPSC0, (en as u8) << 6]) - } - - /// Set the power current limiting. - pub fn set_pwr_ctrl(&mut self, pwr_ctrl: PwrCtrl) -> Result<(), Error> { - self.write(wr_reg![PC, pwr_ctrl.as_bits()]) - } - - /// Set the maximum SMPS drive capability. - pub fn set_smps_drv(&mut self, drv: SmpsDrv) -> Result<(), Error> { - self.write(wr_reg![SMPSC2, (drv as u8) << 1]) - } - - /// Set the node address. - /// - /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based - /// on node address. - pub fn set_node_addr(&mut self, addr: u8) -> Result<(), Error> { - self.write(wr_reg![NODE, addr]) - } - - /// Set the broadcast address. - /// - /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based - /// on broadcast address. - pub fn set_broadcast_addr(&mut self, addr: u8) -> Result<(), Error> { - self.write(wr_reg![BROADCAST, addr]) - } - - /// Set both the broadcast address and node address. - /// - /// This is a combination of [`set_node_addr`] and [`set_broadcast_addr`] - /// in a single SPI transfer. - /// - /// [`set_node_addr`]: Self::set_node_addr - /// [`set_broadcast_addr`]: Self::set_broadcast_addr - pub fn set_addrs(&mut self, node: u8, broadcast: u8) -> Result<(), Error> { - self.write(wr_reg![NODE, node, broadcast]) - } -} - -// 5.8.3 -/// Operating mode commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Put the radio into sleep mode. - /// - /// This command is only accepted in standby mode. - /// The cfg argument allows some optional functions to be maintained - /// in sleep mode. - /// - /// # Safety - /// - /// 1. After the `set_sleep` command, the sub-GHz radio NSS must not go low - /// for 500 μs. - /// No reason is provided, the reference manual (RM0453 rev 2) simply - /// says "you must". - /// 2. The radio cannot be used while in sleep mode. - /// 3. The radio must be woken up with [`wakeup`] before resuming use. - /// - /// # Example - /// - /// Put the radio into sleep mode. - /// - /// ```no_run - /// # let dp = unsafe { embassy_stm32::pac::Peripherals::steal() }; - /// # let mut sg = embassy_stm32::subghz::SubGhz::new(p.SUBGHZSPI, ...); - /// use embassy_stm32::{ - /// subghz::{wakeup, SleepCfg, StandbyClk}, - /// }; - /// - /// sg.set_standby(StandbyClk::Rc)?; - /// unsafe { sg.set_sleep(SleepCfg::default())? }; - /// embassy_time::Timer::after(embassy_time::Duration::from_micros(500)).await; - /// unsafe { wakeup() }; - /// # Ok::<(), embassy_stm32::subghz::Error>(()) - /// ``` - pub unsafe fn set_sleep(&mut self, cfg: SleepCfg) -> Result<(), Error> { - // poll for busy before, but not after - // radio idles with busy high while in sleep mode - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[OpCode::SetSleep as u8, u8::from(cfg)])?; - } - Ok(()) - } - - /// Put the radio into standby mode. - pub fn set_standby(&mut self, standby_clk: StandbyClk) -> Result<(), Error> { - self.write(&[OpCode::SetStandby as u8, u8::from(standby_clk)]) - } - - /// Put the subghz radio into frequency synthesis mode. - /// - /// The RF-PLL frequency must be set with [`set_rf_frequency`] before using - /// this command. - /// - /// Check the datasheet for more information, this is a test command but - /// I honestly do not see any use for it. Please update this description - /// if you know more than I do. - /// - /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency - pub fn set_fs(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetFs.into()]) - } - - /// Setup the sub-GHz radio for TX. - pub fn set_tx(&mut self, timeout: Timeout) -> Result<(), Error> { - let tobits: u32 = timeout.into_bits(); - self.write(&[ - OpCode::SetTx.into(), - (tobits >> 16) as u8, - (tobits >> 8) as u8, - tobits as u8, - ]) - } - - /// Setup the sub-GHz radio for RX. - pub fn set_rx(&mut self, timeout: Timeout) -> Result<(), Error> { - let tobits: u32 = timeout.into_bits(); - self.write(&[ - OpCode::SetRx.into(), - (tobits >> 16) as u8, - (tobits >> 8) as u8, - tobits as u8, - ]) - } - - /// Allows selection of the receiver event which stops the RX timeout timer. - pub fn set_rx_timeout_stop(&mut self, rx_timeout_stop: RxTimeoutStop) -> Result<(), Error> { - self.write(&[OpCode::SetStopRxTimerOnPreamble.into(), rx_timeout_stop.into()]) - } - - /// Put the radio in non-continuous RX mode. - /// - /// This command must be sent in Standby mode. - /// This command is only functional with FSK and LoRa packet type. - /// - /// The following steps are performed: - /// 1. Save sub-GHz radio configuration. - /// 2. Enter Receive mode and listen for a preamble for the specified `rx_period`. - /// 3. Upon the detection of a preamble, the `rx_period` timeout is stopped - /// and restarted with the value 2 x `rx_period` + `sleep_period`. - /// During this new period, the sub-GHz radio looks for the detection of - /// a synchronization word when in (G)FSK modulation mode, - /// or a header when in LoRa modulation mode. - /// 4. If no packet is received during the listen period defined by - /// 2 x `rx_period` + `sleep_period`, the sleep mode is entered for a - /// duration of `sleep_period`. At the end of the receive period, - /// the sub-GHz radio takes some time to save the context before starting - /// the sleep period. - /// 5. After the sleep period, a new listening period is automatically - /// started. The sub-GHz radio restores the sub-GHz radio configuration - /// and continuous with step 2. - /// - /// The listening mode is terminated in one of the following cases: - /// * if a packet is received during the listening period: the sub-GHz radio - /// issues a [`RxDone`] interrupt and enters standby mode. - /// * if [`set_standby`] is sent during the listening period or after the - /// sub-GHz has been requested to exit sleep mode by sub-GHz radio SPI NSS - /// - /// # Erratum - /// - /// When a preamble is detected the radio should restart the RX timeout - /// with a value of 2 × `rx_period` + `sleep_period`. - /// Instead the radio erroneously uses `sleep_period`. - /// - /// To workaround this use [`restart_rtc`] and [`set_rtc_period`] to - /// reprogram the radio timeout to 2 × `rx_period` + `sleep_period`. - /// - /// Use code similar to this in the [`PreambleDetected`] interrupt handler. - /// - /// ```no_run - /// # let rx_period: Timeout = Timeout::from_millis_sat(100); - /// # let sleep_period: Timeout = Timeout::from_millis_sat(100); - /// # let mut sg = unsafe { stm32wlxx_hal::subghz::SubGhz::steal() }; - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// let period: Timeout = rx_period - /// .saturating_add(rx_period) - /// .saturating_add(sleep_period); - /// - /// sg.set_rtc_period(period)?; - /// sg.restart_rtc()?; - /// # Ok::<(), stm32wlxx_hal::subghz::Error>(()) - /// ``` - /// - /// Please read the erratum for more details. - /// - /// [`PreambleDetected`]: crate::subghz::Irq::PreambleDetected - /// [`restart_rtc`]: crate::subghz::SubGhz::restart_rtc - /// [`RxDone`]: crate::subghz::Irq::RxDone - /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency - /// [`set_rtc_period`]: crate::subghz::SubGhz::set_rtc_period - /// [`set_standby`]: crate::subghz::SubGhz::set_standby - pub fn set_rx_duty_cycle(&mut self, rx_period: Timeout, sleep_period: Timeout) -> Result<(), Error> { - let rx_period_bits: u32 = rx_period.into_bits(); - let sleep_period_bits: u32 = sleep_period.into_bits(); - self.write(&[ - OpCode::SetRxDutyCycle.into(), - (rx_period_bits >> 16) as u8, - (rx_period_bits >> 8) as u8, - rx_period_bits as u8, - (sleep_period_bits >> 16) as u8, - (sleep_period_bits >> 8) as u8, - sleep_period_bits as u8, - ]) - } - - /// Channel Activity Detection (CAD) with LoRa packets. - /// - /// The channel activity detection (CAD) is a specific LoRa operation mode, - /// where the sub-GHz radio searches for a LoRa radio signal. - /// After the search is completed, the Standby mode is automatically - /// entered, CAD is done and IRQ is generated. - /// When a LoRa radio signal is detected, the CAD detected IRQ is also - /// generated. - /// - /// The length of the search must be configured with [`set_cad_params`] - /// prior to calling `set_cad`. - /// - /// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params - pub fn set_cad(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetCad.into()]) - } - - /// Generate a continuous transmit tone at the RF-PLL frequency. - /// - /// The sub-GHz radio remains in continuous transmit tone mode until a mode - /// configuration command is received. - pub fn set_tx_continuous_wave(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetTxContinuousWave as u8]) - } - - /// Generate an infinite preamble at the RF-PLL frequency. - /// - /// The preamble is an alternating 0s and 1s sequence in generic (G)FSK and - /// (G)MSK modulations. - /// The preamble is symbol 0 in LoRa modulation. - /// The sub-GHz radio remains in infinite preamble mode until a mode - /// configuration command is received. - pub fn set_tx_continuous_preamble(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetTxContinuousPreamble as u8]) - } -} - -// 5.8.4 -/// Radio configuration commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Set the packet type (modulation scheme). - pub fn set_packet_type(&mut self, packet_type: PacketType) -> Result<(), Error> { - self.write(&[OpCode::SetPacketType as u8, packet_type as u8]) - } - - /// Get the packet type. - pub fn packet_type(&mut self) -> Result, Error> { - let pkt_type: [u8; 2] = self.read_n(OpCode::GetPacketType)?; - Ok(PacketType::from_raw(pkt_type[1])) - } - - /// Set the radio carrier frequency. - pub fn set_rf_frequency(&mut self, freq: &RfFreq) -> Result<(), Error> { - self.write(freq.as_slice()) - } - - /// Set the transmit output power and the PA ramp-up time. - pub fn set_tx_params(&mut self, params: &TxParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Power amplifier configuration. - /// - /// Used to customize the maximum output power and efficiency. - pub fn set_pa_config(&mut self, pa_config: &PaConfig) -> Result<(), Error> { - self.write(pa_config.as_slice()) - } - - /// Operating mode to enter after a successful packet transmission or - /// packet reception. - pub fn set_tx_rx_fallback_mode(&mut self, fm: FallbackMode) -> Result<(), Error> { - self.write(&[OpCode::SetTxRxFallbackMode as u8, fm as u8]) - } - - /// Set channel activity detection (CAD) parameters. - pub fn set_cad_params(&mut self, params: &CadParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the data buffer base address for the packet handling in TX and RX. - /// - /// There is a single buffer for both TX and RX. - /// The buffer is not memory mapped, it is accessed via the - /// [`read_buffer`](SubGhz::read_buffer) and - /// [`write_buffer`](SubGhz::write_buffer) methods. - pub fn set_buffer_base_address(&mut self, tx: u8, rx: u8) -> Result<(), Error> { - self.write(&[OpCode::SetBufferBaseAddress as u8, tx, rx]) - } - - /// Set the (G)FSK modulation parameters. - pub fn set_fsk_mod_params(&mut self, params: &FskModParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the LoRa modulation parameters. - pub fn set_lora_mod_params(&mut self, params: &LoRaModParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the BPSK modulation parameters. - pub fn set_bpsk_mod_params(&mut self, params: &BpskModParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the generic (FSK) packet parameters. - pub fn set_packet_params(&mut self, params: &GenericPacketParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the BPSK packet parameters. - pub fn set_bpsk_packet_params(&mut self, params: &BpskPacketParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the LoRa packet parameters. - pub fn set_lora_packet_params(&mut self, params: &LoRaPacketParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the number of LoRa symbols to be received before starting the - /// reception of a LoRa packet. - /// - /// Packet reception is started after `n` + 1 symbols are detected. - pub fn set_lora_symb_timeout(&mut self, n: u8) -> Result<(), Error> { - self.write(&[OpCode::SetLoRaSymbTimeout.into(), n]) - } -} - -// 5.8.5 -/// Communication status and information commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Get the radio status. - /// - /// The hardware (or documentation) appears to have many bugs where this - /// will return reserved values. - /// See this thread in the ST community for details: [link] - /// - /// [link]: https://community.st.com/s/question/0D53W00000hR9GQSA0/stm32wl55-getstatus-command-returns-reserved-cmdstatus - pub fn status(&mut self) -> Result { - Ok(self.read_1(OpCode::GetStatus)?.into()) - } - - /// Get the RX buffer status. - /// - /// The return tuple is (status, payload_length, buffer_pointer). - pub fn rx_buffer_status(&mut self) -> Result<(Status, u8, u8), Error> { - let data: [u8; 3] = self.read_n(OpCode::GetRxBufferStatus)?; - Ok((data[0].into(), data[1], data[2])) - } - - /// Returns information on the last received (G)FSK packet. - pub fn fsk_packet_status(&mut self) -> Result { - Ok(FskPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?)) - } - - /// Returns information on the last received LoRa packet. - pub fn lora_packet_status(&mut self) -> Result { - Ok(LoRaPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?)) - } - - /// Get the instantaneous signal strength during packet reception. - /// - /// The units are in dbm. - pub fn rssi_inst(&mut self) -> Result<(Status, Ratio), Error> { - let data: [u8; 2] = self.read_n(OpCode::GetRssiInst)?; - let status: Status = data[0].into(); - let rssi: Ratio = Ratio::new_raw(i16::from(data[1]), -2); - - Ok((status, rssi)) - } - - /// (G)FSK packet stats. - pub fn fsk_stats(&mut self) -> Result, Error> { - let data: [u8; 7] = self.read_n(OpCode::GetStats)?; - Ok(Stats::from_raw_fsk(data)) - } - - /// LoRa packet stats. - pub fn lora_stats(&mut self) -> Result, Error> { - let data: [u8; 7] = self.read_n(OpCode::GetStats)?; - Ok(Stats::from_raw_lora(data)) - } - - /// Reset the stats as reported in [`lora_stats`](SubGhz::lora_stats) and - /// [`fsk_stats`](SubGhz::fsk_stats). - pub fn reset_stats(&mut self) -> Result<(), Error> { - const RESET_STATS: [u8; 7] = [0x00; 7]; - self.write(&RESET_STATS) - } -} - -// 5.8.6 -/// IRQ commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Set the interrupt configuration. - pub fn set_irq_cfg(&mut self, cfg: &CfgIrq) -> Result<(), Error> { - self.write(cfg.as_slice()) - } - - /// Get the IRQ status. - pub fn irq_status(&mut self) -> Result<(Status, u16), Error> { - let data: [u8; 3] = self.read_n(OpCode::GetIrqStatus)?; - let irq_status: u16 = u16::from_be_bytes([data[1], data[2]]); - Ok((data[0].into(), irq_status)) - } - - /// Clear the IRQ status. - pub fn clear_irq_status(&mut self, mask: u16) -> Result<(), Error> { - self.write(&[OpCode::ClrIrqStatus as u8, (mask >> 8) as u8, mask as u8]) - } -} - -// 5.8.7 -/// Miscellaneous commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Calibrate one or several blocks at any time when in standby mode. - pub fn calibrate(&mut self, cal: u8) -> Result<(), Error> { - // bit 7 is reserved and must be kept at reset value. - self.write(&[OpCode::Calibrate as u8, cal & 0x7F]) - } - - /// Calibrate the image at the given frequencies. - /// - /// Requires the radio to be in standby mode. - pub fn calibrate_image(&mut self, cal: CalibrateImage) -> Result<(), Error> { - self.write(&[OpCode::CalibrateImage as u8, cal.0, cal.1]) - } - - /// Set the radio power supply. - pub fn set_regulator_mode(&mut self, reg_mode: RegMode) -> Result<(), Error> { - self.write(&[OpCode::SetRegulatorMode as u8, reg_mode as u8]) - } - - /// Get the radio operational errors. - pub fn op_error(&mut self) -> Result<(Status, u16), Error> { - let data: [u8; 3] = self.read_n(OpCode::GetError)?; - Ok((data[0].into(), u16::from_be_bytes([data[1], data[2]]))) - } - - /// Clear all errors as reported by [`op_error`](SubGhz::op_error). - pub fn clear_error(&mut self) -> Result<(), Error> { - self.write(&[OpCode::ClrError as u8, 0x00]) - } -} - -// 5.8.8 -/// Set TCXO mode command -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Set the TCXO trim and HSE32 ready timeout. - pub fn set_tcxo_mode(&mut self, tcxo_mode: &TcxoMode) -> Result<(), Error> { - self.write(tcxo_mode.as_slice()) - } -} - -/// sub-GHz radio opcodes. -/// -/// See Table 41 "Sub-GHz radio SPI commands overview" -#[repr(u8)] -#[derive(Debug, Clone, Copy)] -#[allow(dead_code)] -pub(crate) enum OpCode { - Calibrate = 0x89, - CalibrateImage = 0x98, - CfgDioIrq = 0x08, - ClrError = 0x07, - ClrIrqStatus = 0x02, - GetError = 0x17, - GetIrqStatus = 0x12, - GetPacketStatus = 0x14, - GetPacketType = 0x11, - GetRssiInst = 0x15, - GetRxBufferStatus = 0x13, - GetStats = 0x10, - GetStatus = 0xC0, - ReadBuffer = 0x1E, - RegRegister = 0x1D, - ResetStats = 0x00, - SetBufferBaseAddress = 0x8F, - SetCad = 0xC5, - SetCadParams = 0x88, - SetFs = 0xC1, - SetLoRaSymbTimeout = 0xA0, - SetModulationParams = 0x8B, - SetPacketParams = 0x8C, - SetPacketType = 0x8A, - SetPaConfig = 0x95, - SetRegulatorMode = 0x96, - SetRfFrequency = 0x86, - SetRx = 0x82, - SetRxDutyCycle = 0x94, - SetSleep = 0x84, - SetStandby = 0x80, - SetStopRxTimerOnPreamble = 0x9F, - SetTcxoMode = 0x97, - SetTx = 0x83, - SetTxContinuousPreamble = 0xD2, - SetTxContinuousWave = 0xD1, - SetTxParams = 0x8E, - SetTxRxFallbackMode = 0x93, - WriteBuffer = 0x0E, - WriteRegister = 0x0D, -} - -impl From for u8 { - fn from(opcode: OpCode) -> Self { - opcode as u8 - } -} - -#[repr(u16)] -#[allow(clippy::upper_case_acronyms)] -pub(crate) enum Register { - /// Generic bit synchronization. - GBSYNC = 0x06AC, - /// Generic packet control. - GPKTCTL1A = 0x06B8, - /// Generic whitening. - GWHITEINIRL = 0x06B9, - /// Generic CRC initial. - GCRCINIRH = 0x06BC, - /// Generic CRC polynomial. - GCRCPOLRH = 0x06BE, - /// Generic synchronization word 7. - GSYNC7 = 0x06C0, - /// Node address. - NODE = 0x06CD, - /// Broadcast address. - BROADCAST = 0x06CE, - /// LoRa synchronization word MSB. - LSYNCH = 0x0740, - /// LoRa synchronization word LSB. - #[allow(dead_code)] - LSYNCL = 0x0741, - /// Receiver gain control. - RXGAINC = 0x08AC, - /// PA over current protection. - PAOCP = 0x08E7, - /// RTC control. - RTCCTLR = 0x0902, - /// RTC period MSB. - RTCPRDR2 = 0x0906, - /// RTC period mid-byte. - #[allow(dead_code)] - RTCPRDR1 = 0x0907, - /// RTC period LSB. - #[allow(dead_code)] - RTCPRDR0 = 0x0908, - /// HSE32 OSC_IN capacitor trim. - HSEINTRIM = 0x0911, - /// HSE32 OSC_OUT capacitor trim. - HSEOUTTRIM = 0x0912, - /// SMPS control 0. - SMPSC0 = 0x0916, - /// Power control. - PC = 0x091A, - /// SMPS control 2. - SMPSC2 = 0x0923, -} - -impl Register { - pub const fn address(self) -> u16 { - self as u16 - } -} diff --git a/embassy-stm32/src/subghz/mod_params.rs b/embassy-stm32/src/subghz/mod_params.rs deleted file mode 100644 index d997ae11..00000000 --- a/embassy-stm32/src/subghz/mod_params.rs +++ /dev/null @@ -1,1045 +0,0 @@ -/// Bandwidth options for [`FskModParams`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FskBandwidth { - /// 4.8 kHz double-sideband - Bw4 = 0x1F, - /// 5.8 kHz double-sideband - Bw5 = 0x17, - /// 7.3 kHz double-sideband - Bw7 = 0x0F, - /// 9.7 kHz double-sideband - Bw9 = 0x1E, - /// 11.7 kHz double-sideband - Bw11 = 0x16, - /// 14.6 kHz double-sideband - Bw14 = 0x0E, - /// 19.5 kHz double-sideband - Bw19 = 0x1D, - /// 23.4 kHz double-sideband - Bw23 = 0x15, - /// 29.3 kHz double-sideband - Bw29 = 0x0D, - /// 39.0 kHz double-sideband - Bw39 = 0x1C, - /// 46.9 kHz double-sideband - Bw46 = 0x14, - /// 58.6 kHz double-sideband - Bw58 = 0x0C, - /// 78.2 kHz double-sideband - Bw78 = 0x1B, - /// 93.8 kHz double-sideband - Bw93 = 0x13, - /// 117.3 kHz double-sideband - Bw117 = 0x0B, - /// 156.2 kHz double-sideband - Bw156 = 0x1A, - /// 187.2 kHz double-sideband - Bw187 = 0x12, - /// 234.3 kHz double-sideband - Bw234 = 0x0A, - /// 312.0 kHz double-sideband - Bw312 = 0x19, - /// 373.6 kHz double-sideband - Bw373 = 0x11, - /// 467.0 kHz double-sideband - Bw467 = 0x09, -} - -impl FskBandwidth { - /// Get the bandwidth in hertz. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBandwidth; - /// - /// assert_eq!(FskBandwidth::Bw4.hertz(), 4_800); - /// assert_eq!(FskBandwidth::Bw5.hertz(), 5_800); - /// assert_eq!(FskBandwidth::Bw7.hertz(), 7_300); - /// assert_eq!(FskBandwidth::Bw9.hertz(), 9_700); - /// assert_eq!(FskBandwidth::Bw11.hertz(), 11_700); - /// assert_eq!(FskBandwidth::Bw14.hertz(), 14_600); - /// assert_eq!(FskBandwidth::Bw19.hertz(), 19_500); - /// assert_eq!(FskBandwidth::Bw23.hertz(), 23_400); - /// assert_eq!(FskBandwidth::Bw29.hertz(), 29_300); - /// assert_eq!(FskBandwidth::Bw39.hertz(), 39_000); - /// assert_eq!(FskBandwidth::Bw46.hertz(), 46_900); - /// assert_eq!(FskBandwidth::Bw58.hertz(), 58_600); - /// assert_eq!(FskBandwidth::Bw78.hertz(), 78_200); - /// assert_eq!(FskBandwidth::Bw93.hertz(), 93_800); - /// assert_eq!(FskBandwidth::Bw117.hertz(), 117_300); - /// assert_eq!(FskBandwidth::Bw156.hertz(), 156_200); - /// assert_eq!(FskBandwidth::Bw187.hertz(), 187_200); - /// assert_eq!(FskBandwidth::Bw234.hertz(), 234_300); - /// assert_eq!(FskBandwidth::Bw312.hertz(), 312_000); - /// assert_eq!(FskBandwidth::Bw373.hertz(), 373_600); - /// assert_eq!(FskBandwidth::Bw467.hertz(), 467_000); - /// ``` - pub const fn hertz(&self) -> u32 { - match self { - FskBandwidth::Bw4 => 4_800, - FskBandwidth::Bw5 => 5_800, - FskBandwidth::Bw7 => 7_300, - FskBandwidth::Bw9 => 9_700, - FskBandwidth::Bw11 => 11_700, - FskBandwidth::Bw14 => 14_600, - FskBandwidth::Bw19 => 19_500, - FskBandwidth::Bw23 => 23_400, - FskBandwidth::Bw29 => 29_300, - FskBandwidth::Bw39 => 39_000, - FskBandwidth::Bw46 => 46_900, - FskBandwidth::Bw58 => 58_600, - FskBandwidth::Bw78 => 78_200, - FskBandwidth::Bw93 => 93_800, - FskBandwidth::Bw117 => 117_300, - FskBandwidth::Bw156 => 156_200, - FskBandwidth::Bw187 => 187_200, - FskBandwidth::Bw234 => 234_300, - FskBandwidth::Bw312 => 312_000, - FskBandwidth::Bw373 => 373_600, - FskBandwidth::Bw467 => 467_000, - } - } - - /// Convert from a raw bit value. - /// - /// Invalid values will be returned in the `Err` variant of the result. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBandwidth; - /// - /// assert_eq!(FskBandwidth::from_bits(0x1F), Ok(FskBandwidth::Bw4)); - /// assert_eq!(FskBandwidth::from_bits(0x17), Ok(FskBandwidth::Bw5)); - /// assert_eq!(FskBandwidth::from_bits(0x0F), Ok(FskBandwidth::Bw7)); - /// assert_eq!(FskBandwidth::from_bits(0x1E), Ok(FskBandwidth::Bw9)); - /// assert_eq!(FskBandwidth::from_bits(0x16), Ok(FskBandwidth::Bw11)); - /// assert_eq!(FskBandwidth::from_bits(0x0E), Ok(FskBandwidth::Bw14)); - /// assert_eq!(FskBandwidth::from_bits(0x1D), Ok(FskBandwidth::Bw19)); - /// assert_eq!(FskBandwidth::from_bits(0x15), Ok(FskBandwidth::Bw23)); - /// assert_eq!(FskBandwidth::from_bits(0x0D), Ok(FskBandwidth::Bw29)); - /// assert_eq!(FskBandwidth::from_bits(0x1C), Ok(FskBandwidth::Bw39)); - /// assert_eq!(FskBandwidth::from_bits(0x14), Ok(FskBandwidth::Bw46)); - /// assert_eq!(FskBandwidth::from_bits(0x0C), Ok(FskBandwidth::Bw58)); - /// assert_eq!(FskBandwidth::from_bits(0x1B), Ok(FskBandwidth::Bw78)); - /// assert_eq!(FskBandwidth::from_bits(0x13), Ok(FskBandwidth::Bw93)); - /// assert_eq!(FskBandwidth::from_bits(0x0B), Ok(FskBandwidth::Bw117)); - /// assert_eq!(FskBandwidth::from_bits(0x1A), Ok(FskBandwidth::Bw156)); - /// assert_eq!(FskBandwidth::from_bits(0x12), Ok(FskBandwidth::Bw187)); - /// assert_eq!(FskBandwidth::from_bits(0x0A), Ok(FskBandwidth::Bw234)); - /// assert_eq!(FskBandwidth::from_bits(0x19), Ok(FskBandwidth::Bw312)); - /// assert_eq!(FskBandwidth::from_bits(0x11), Ok(FskBandwidth::Bw373)); - /// assert_eq!(FskBandwidth::from_bits(0x09), Ok(FskBandwidth::Bw467)); - /// assert_eq!(FskBandwidth::from_bits(0x00), Err(0x00)); - /// ``` - pub const fn from_bits(bits: u8) -> Result { - match bits { - 0x1F => Ok(Self::Bw4), - 0x17 => Ok(Self::Bw5), - 0x0F => Ok(Self::Bw7), - 0x1E => Ok(Self::Bw9), - 0x16 => Ok(Self::Bw11), - 0x0E => Ok(Self::Bw14), - 0x1D => Ok(Self::Bw19), - 0x15 => Ok(Self::Bw23), - 0x0D => Ok(Self::Bw29), - 0x1C => Ok(Self::Bw39), - 0x14 => Ok(Self::Bw46), - 0x0C => Ok(Self::Bw58), - 0x1B => Ok(Self::Bw78), - 0x13 => Ok(Self::Bw93), - 0x0B => Ok(Self::Bw117), - 0x1A => Ok(Self::Bw156), - 0x12 => Ok(Self::Bw187), - 0x0A => Ok(Self::Bw234), - 0x19 => Ok(Self::Bw312), - 0x11 => Ok(Self::Bw373), - 0x09 => Ok(Self::Bw467), - x => Err(x), - } - } -} - -impl Ord for FskBandwidth { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.hertz().cmp(&other.hertz()) - } -} - -impl PartialOrd for FskBandwidth { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.hertz().cmp(&other.hertz())) - } -} - -/// Pulse shaping options for [`FskModParams`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FskPulseShape { - /// No filtering applied. - None = 0b00, - /// Gaussian BT 0.3 - Bt03 = 0x08, - /// Gaussian BT 0.5 - Bt05 = 0x09, - /// Gaussian BT 0.7 - Bt07 = 0x0A, - /// Gaussian BT 1.0 - Bt10 = 0x0B, -} - -/// Bitrate argument for [`FskModParams::set_bitrate`] and -/// [`BpskModParams::set_bitrate`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct FskBitrate { - bits: u32, -} - -impl FskBitrate { - /// Create a new `FskBitrate` from a bitrate in bits per second. - /// - /// This the resulting value will be rounded down, and will saturate if - /// `bps` is outside of the theoretical limits. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBitrate; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(9600); - /// assert_eq!(BITRATE.as_bps(), 9600); - /// ``` - pub const fn from_bps(bps: u32) -> Self { - const MAX: u32 = 0x00FF_FFFF; - if bps == 0 { - Self { bits: MAX } - } else { - let bits: u32 = 32 * 32_000_000 / bps; - if bits > MAX { - Self { bits: MAX } - } else { - Self { bits } - } - } - } - - /// Create a new `FskBitrate` from a raw bit value. - /// - /// bits = 32 × 32 MHz / bitrate - /// - /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` - /// argument will be masked. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBitrate; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_raw(0x7D00); - /// assert_eq!(BITRATE.as_bps(), 32_000); - /// ``` - pub const fn from_raw(bits: u32) -> Self { - Self { - bits: bits & 0x00FF_FFFF, - } - } - - /// Return the bitrate in bits per second, rounded down. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBitrate; - /// - /// const BITS_PER_SEC: u32 = 9600; - /// const BITRATE: FskBitrate = FskBitrate::from_bps(BITS_PER_SEC); - /// assert_eq!(BITRATE.as_bps(), BITS_PER_SEC); - /// ``` - pub const fn as_bps(&self) -> u32 { - if self.bits == 0 { - 0 - } else { - 32 * 32_000_000 / self.bits - } - } - - pub(crate) const fn into_bits(self) -> u32 { - self.bits - } -} - -impl Ord for FskBitrate { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.as_bps().cmp(&other.as_bps()) - } -} - -impl PartialOrd for FskBitrate { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.as_bps().cmp(&other.as_bps())) - } -} - -/// Frequency deviation argument for [`FskModParams::set_fdev`] -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct FskFdev { - bits: u32, -} - -impl FskFdev { - /// Create a new `FskFdev` from a frequency deviation in hertz, rounded - /// down. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskFdev; - /// - /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); - /// assert_eq!(FDEV.as_hertz(), 31_250); - /// ``` - pub const fn from_hertz(hz: u32) -> Self { - Self { - bits: ((hz as u64) * (1 << 25) / 32_000_000) as u32 & 0x00FF_FFFF, - } - } - - /// Create a new `FskFdev` from a raw bit value. - /// - /// bits = fdev × 225 / 32 MHz - /// - /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` - /// argument will be masked. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskFdev; - /// - /// const FDEV: FskFdev = FskFdev::from_raw(0x8000); - /// assert_eq!(FDEV.as_hertz(), 31_250); - /// ``` - pub const fn from_raw(bits: u32) -> Self { - Self { - bits: bits & 0x00FF_FFFF, - } - } - - /// Return the frequency deviation in hertz, rounded down. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskFdev; - /// - /// const HERTZ: u32 = 31_250; - /// const FDEV: FskFdev = FskFdev::from_hertz(HERTZ); - /// assert_eq!(FDEV.as_hertz(), HERTZ); - /// ``` - pub const fn as_hertz(&self) -> u32 { - ((self.bits as u64) * 32_000_000 / (1 << 25)) as u32 - } - - pub(crate) const fn into_bits(self) -> u32 { - self.bits - } -} - -/// (G)FSK modulation parameters. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct FskModParams { - buf: [u8; 9], -} - -impl FskModParams { - /// Create a new `FskModParams` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskModParams; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new(); - /// ``` - pub const fn new() -> FskModParams { - FskModParams { - buf: [ - super::OpCode::SetModulationParams as u8, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - ], - } - .set_bitrate(FskBitrate::from_bps(50_000)) - .set_pulse_shape(FskPulseShape::None) - .set_bandwidth(FskBandwidth::Bw58) - .set_fdev(FskFdev::from_hertz(25_000)) - } - - /// Get the bitrate. - /// - /// # Example - /// - /// Setting the bitrate to 32,000 bits per second. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE); - /// assert_eq!(MOD_PARAMS.bitrate(), BITRATE); - /// ``` - pub const fn bitrate(&self) -> FskBitrate { - let raw: u32 = u32::from_be_bytes([0, self.buf[1], self.buf[2], self.buf[3]]); - FskBitrate::from_raw(raw) - } - - /// Set the bitrate. - /// - /// # Example - /// - /// Setting the bitrate to 32,000 bits per second. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE); - /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x00); - /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x7D); - /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0x00); - /// ``` - #[must_use = "set_bitrate returns a modified FskModParams"] - pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> FskModParams { - let bits: u32 = bitrate.into_bits(); - self.buf[1] = ((bits >> 16) & 0xFF) as u8; - self.buf[2] = ((bits >> 8) & 0xFF) as u8; - self.buf[3] = (bits & 0xFF) as u8; - self - } - - /// Set the pulse shaping. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskModParams, FskPulseShape}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_pulse_shape(FskPulseShape::Bt03); - /// # assert_eq!(MOD_PARAMS.as_slice()[4], 0x08); - /// ``` - #[must_use = "set_pulse_shape returns a modified FskModParams"] - pub const fn set_pulse_shape(mut self, shape: FskPulseShape) -> FskModParams { - self.buf[4] = shape as u8; - self - } - - /// Get the bandwidth. - /// - /// Values that do not correspond to a valid [`FskBandwidth`] will be - /// returned in the `Err` variant of the result. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9); - /// assert_eq!(MOD_PARAMS.bandwidth(), Ok(FskBandwidth::Bw9)); - /// ``` - pub const fn bandwidth(&self) -> Result { - FskBandwidth::from_bits(self.buf[5]) - } - - /// Set the bandwidth. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9); - /// # assert_eq!(MOD_PARAMS.as_slice()[5], 0x1E); - /// ``` - #[must_use = "set_pulse_shape returns a modified FskModParams"] - pub const fn set_bandwidth(mut self, bw: FskBandwidth) -> FskModParams { - self.buf[5] = bw as u8; - self - } - - /// Get the frequency deviation. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams}; - /// - /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV); - /// assert_eq!(MOD_PARAMS.fdev(), FDEV); - /// ``` - pub const fn fdev(&self) -> FskFdev { - let raw: u32 = u32::from_be_bytes([0, self.buf[6], self.buf[7], self.buf[8]]); - FskFdev::from_raw(raw) - } - - /// Set the frequency deviation. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams}; - /// - /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV); - /// # assert_eq!(MOD_PARAMS.as_slice()[6], 0x00); - /// # assert_eq!(MOD_PARAMS.as_slice()[7], 0x80); - /// # assert_eq!(MOD_PARAMS.as_slice()[8], 0x00); - /// ``` - #[must_use = "set_fdev returns a modified FskModParams"] - pub const fn set_fdev(mut self, fdev: FskFdev) -> FskModParams { - let bits: u32 = fdev.into_bits(); - self.buf[6] = ((bits >> 16) & 0xFF) as u8; - self.buf[7] = ((bits >> 8) & 0xFF) as u8; - self.buf[8] = (bits & 0xFF) as u8; - self - } - /// Returns `true` if the modulation parameters are valid. - /// - /// The bandwidth must be chosen so that: - /// - /// [`FskBandwidth`] > [`FskBitrate`] + 2 × [`FskFdev`] + frequency error - /// - /// Where frequency error = 2 × HSE32FREQ error. - /// - /// The datasheet (DS13293 Rev 1) gives these requirements for the HSE32 - /// frequency tolerance: - /// - /// * Initial: ±10 ppm - /// * Over temperature (-20 to 70 °C): ±10 ppm - /// * Aging over 10 years: ±10 ppm - /// - /// # Example - /// - /// Checking valid parameters at compile-time - /// - /// ``` - /// extern crate static_assertions as sa; - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new() - /// .set_bitrate(FskBitrate::from_bps(20_000)) - /// .set_pulse_shape(FskPulseShape::Bt03) - /// .set_bandwidth(FskBandwidth::Bw58) - /// .set_fdev(FskFdev::from_hertz(10_000)); - /// - /// // 30 PPM is wost case (if the HSE32 crystal meets requirements) - /// sa::const_assert!(MOD_PARAMS.is_valid(30)); - /// ``` - #[must_use = "the return value indicates if the modulation parameters are valid"] - pub const fn is_valid(&self, ppm: u8) -> bool { - let bw: u32 = match self.bandwidth() { - Ok(bw) => bw.hertz(), - Err(_) => return false, - }; - let br: u32 = self.bitrate().as_bps(); - let fdev: u32 = self.fdev().as_hertz(); - let hse_err: u32 = 32 * (ppm as u32); - let freq_err: u32 = 2 * hse_err; - - bw > br + 2 * fdev + freq_err - } - - /// Returns `true` if the modulation parameters are valid for a worst-case - /// crystal tolerance. - /// - /// This is equivalent to [`is_valid`](Self::is_valid) with a `ppm` argument - /// of 30. - #[must_use = "the return value indicates if the modulation parameters are valid"] - pub const fn is_valid_worst_case(&self) -> bool { - self.is_valid(30) - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(20_000); - /// const PULSE_SHAPE: FskPulseShape = FskPulseShape::Bt03; - /// const BW: FskBandwidth = FskBandwidth::Bw58; - /// const FDEV: FskFdev = FskFdev::from_hertz(10_000); - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new() - /// .set_bitrate(BITRATE) - /// .set_pulse_shape(PULSE_SHAPE) - /// .set_bandwidth(BW) - /// .set_fdev(FDEV); - /// - /// assert_eq!( - /// MOD_PARAMS.as_slice(), - /// &[0x8B, 0x00, 0xC8, 0x00, 0x08, 0x0C, 0x00, 0x28, 0xF5] - /// ); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for FskModParams { - fn default() -> Self { - Self::new() - } -} - -/// LoRa spreading factor. -/// -/// Argument of [`LoRaModParams::set_sf`]. -/// -/// Higher spreading factors improve receiver sensitivity, but reduce bit rate -/// and increase power consumption. -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum SpreadingFactor { - /// Spreading factor 5. - Sf5 = 0x05, - /// Spreading factor 6. - Sf6 = 0x06, - /// Spreading factor 7. - Sf7 = 0x07, - /// Spreading factor 8. - Sf8 = 0x08, - /// Spreading factor 9. - Sf9 = 0x09, - /// Spreading factor 10. - Sf10 = 0x0A, - /// Spreading factor 11. - Sf11 = 0x0B, - /// Spreading factor 12. - Sf12 = 0x0C, -} - -impl From for u8 { - fn from(sf: SpreadingFactor) -> Self { - sf as u8 - } -} - -/// LoRa bandwidth. -/// -/// Argument of [`LoRaModParams::set_bw`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum LoRaBandwidth { - /// 7.81 kHz - Bw7 = 0x00, - /// 10.42 kHz - Bw10 = 0x08, - /// 15.63 kHz - Bw15 = 0x01, - /// 20.83 kHz - Bw20 = 0x09, - /// 31.25 kHz - Bw31 = 0x02, - /// 41.67 kHz - Bw41 = 0x0A, - /// 62.50 kHz - Bw62 = 0x03, - /// 125 kHz - Bw125 = 0x04, - /// 250 kHz - Bw250 = 0x05, - /// 500 kHz - Bw500 = 0x06, -} - -impl LoRaBandwidth { - /// Get the bandwidth in hertz. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaBandwidth; - /// - /// assert_eq!(LoRaBandwidth::Bw7.hertz(), 7_810); - /// assert_eq!(LoRaBandwidth::Bw10.hertz(), 10_420); - /// assert_eq!(LoRaBandwidth::Bw15.hertz(), 15_630); - /// assert_eq!(LoRaBandwidth::Bw20.hertz(), 20_830); - /// assert_eq!(LoRaBandwidth::Bw31.hertz(), 31_250); - /// assert_eq!(LoRaBandwidth::Bw41.hertz(), 41_670); - /// assert_eq!(LoRaBandwidth::Bw62.hertz(), 62_500); - /// assert_eq!(LoRaBandwidth::Bw125.hertz(), 125_000); - /// assert_eq!(LoRaBandwidth::Bw250.hertz(), 250_000); - /// assert_eq!(LoRaBandwidth::Bw500.hertz(), 500_000); - /// ``` - pub const fn hertz(&self) -> u32 { - match self { - LoRaBandwidth::Bw7 => 7_810, - LoRaBandwidth::Bw10 => 10_420, - LoRaBandwidth::Bw15 => 15_630, - LoRaBandwidth::Bw20 => 20_830, - LoRaBandwidth::Bw31 => 31_250, - LoRaBandwidth::Bw41 => 41_670, - LoRaBandwidth::Bw62 => 62_500, - LoRaBandwidth::Bw125 => 125_000, - LoRaBandwidth::Bw250 => 250_000, - LoRaBandwidth::Bw500 => 500_000, - } - } -} - -impl Ord for LoRaBandwidth { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.hertz().cmp(&other.hertz()) - } -} - -impl PartialOrd for LoRaBandwidth { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.hertz().cmp(&other.hertz())) - } -} - -/// LoRa forward error correction coding rate. -/// -/// Argument of [`LoRaModParams::set_cr`]. -/// -/// A higher coding rate provides better immunity to interference at the expense -/// of longer transmission time. -/// In normal conditions [`CodingRate::Cr45`] provides the best trade off. -/// In case of strong interference, a higher coding rate may be used. -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum CodingRate { - /// No forward error correction coding rate 4/4 - /// - /// Overhead ratio of 1 - Cr44 = 0x00, - /// Forward error correction coding rate 4/5 - /// - /// Overhead ratio of 1.25 - Cr45 = 0x1, - /// Forward error correction coding rate 4/6 - /// - /// Overhead ratio of 1.5 - Cr46 = 0x2, - /// Forward error correction coding rate 4/7 - /// - /// Overhead ratio of 1.75 - Cr47 = 0x3, - /// Forward error correction coding rate 4/8 - /// - /// Overhead ratio of 2 - Cr48 = 0x4, -} - -/// LoRa modulation parameters. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] - -pub struct LoRaModParams { - buf: [u8; 5], -} - -impl LoRaModParams { - /// Create a new `LoRaModParams` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaModParams; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new(); - /// assert_eq!(MOD_PARAMS, LoRaModParams::default()); - /// ``` - pub const fn new() -> LoRaModParams { - LoRaModParams { - buf: [ - super::OpCode::SetModulationParams as u8, - 0x05, // valid spreading factor - 0x00, - 0x00, - 0x00, - ], - } - } - - /// Set the spreading factor. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{LoRaModParams, SpreadingFactor}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_sf(SpreadingFactor::Sf7); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x00, 0x00, 0x00]); - /// ``` - #[must_use = "set_sf returns a modified LoRaModParams"] - pub const fn set_sf(mut self, sf: SpreadingFactor) -> Self { - self.buf[1] = sf as u8; - self - } - - /// Set the bandwidth. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{LoRaBandwidth, LoRaModParams}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_bw(LoRaBandwidth::Bw125); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x04, 0x00, 0x00]); - /// ``` - #[must_use = "set_bw returns a modified LoRaModParams"] - pub const fn set_bw(mut self, bw: LoRaBandwidth) -> Self { - self.buf[2] = bw as u8; - self - } - - /// Set the forward error correction coding rate. - /// - /// See [`CodingRate`] for more information. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CodingRate, LoRaModParams}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_cr(CodingRate::Cr45); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x01, 0x00]); - /// ``` - #[must_use = "set_cr returns a modified LoRaModParams"] - pub const fn set_cr(mut self, cr: CodingRate) -> Self { - self.buf[3] = cr as u8; - self - } - - /// Set low data rate optimization enable. - /// - /// For low data rates (typically high SF or low BW) and very long payloads - /// (may last several seconds), the low data rate optimization (LDRO) can be - /// enabled. - /// This reduces the number of bits per symbol to the given SF minus 2, - /// to allow the receiver to have a better tracking of the LoRa receive - /// signal. - /// Depending on the payload length, the low data rate optimization is - /// usually recommended when the LoRa symbol time is equal or above - /// 16.38 ms. - /// When using LoRa modulation, the total frequency drift over the packet - /// time must be kept lower than Freq_drift_max: - /// - /// Freq_drift_max = BW / (3 × 2SF) - /// - /// When possible, enabling the low data rate optimization, relaxes the - /// total frequency drift over the packet time by 16: - /// - /// Freq_drift_optimise_max = 16 × Freq_drift_max - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaModParams; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_ldro_en(true); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x00, 0x01]); - /// ``` - #[must_use = "set_ldro_en returns a modified LoRaModParams"] - pub const fn set_ldro_en(mut self, en: bool) -> Self { - self.buf[4] = en as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CodingRate, LoRaBandwidth, LoRaModParams, SpreadingFactor}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new() - /// .set_sf(SpreadingFactor::Sf7) - /// .set_bw(LoRaBandwidth::Bw125) - /// .set_cr(CodingRate::Cr45) - /// .set_ldro_en(false); - /// - /// assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x04, 0x01, 0x00]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for LoRaModParams { - fn default() -> Self { - Self::new() - } -} - -/// BPSK modulation parameters. -/// -/// **Note:** There is no method to set the pulse shape because there is only -/// one valid pulse shape (Gaussian BT 0.5). -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct BpskModParams { - buf: [u8; 5], -} - -impl BpskModParams { - /// Create a new `BpskModParams` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::BpskModParams; - /// - /// const MOD_PARAMS: BpskModParams = BpskModParams::new(); - /// assert_eq!(MOD_PARAMS, BpskModParams::default()); - /// ``` - pub const fn new() -> BpskModParams { - const OPCODE: u8 = super::OpCode::SetModulationParams as u8; - BpskModParams { - buf: [OPCODE, 0x1A, 0x0A, 0xAA, 0x16], - } - } - - /// Set the bitrate. - /// - /// # Example - /// - /// Setting the bitrate to 600 bits per second. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(600); - /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE); - /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x1A); - /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x0A); - /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0xAA); - /// ``` - #[must_use = "set_bitrate returns a modified BpskModParams"] - pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> BpskModParams { - let bits: u32 = bitrate.into_bits(); - self.buf[1] = ((bits >> 16) & 0xFF) as u8; - self.buf[2] = ((bits >> 8) & 0xFF) as u8; - self.buf[3] = (bits & 0xFF) as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(100); - /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE); - /// assert_eq!(MOD_PARAMS.as_slice(), [0x8B, 0x9C, 0x40, 0x00, 0x16]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for BpskModParams { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod test { - use super::{FskBandwidth, FskBitrate, FskFdev, LoRaBandwidth}; - - #[test] - fn fsk_bw_ord() { - assert!((FskBandwidth::Bw4 as u8) > (FskBandwidth::Bw5 as u8)); - assert!(FskBandwidth::Bw4 < FskBandwidth::Bw5); - assert!(FskBandwidth::Bw5 > FskBandwidth::Bw4); - } - - #[test] - fn lora_bw_ord() { - assert!((LoRaBandwidth::Bw10 as u8) > (LoRaBandwidth::Bw15 as u8)); - assert!(LoRaBandwidth::Bw10 < LoRaBandwidth::Bw15); - assert!(LoRaBandwidth::Bw15 > LoRaBandwidth::Bw10); - } - - #[test] - fn fsk_bitrate_ord() { - assert!(FskBitrate::from_bps(9600) > FskBitrate::from_bps(4800)); - assert!(FskBitrate::from_bps(4800) < FskBitrate::from_bps(9600)); - } - - #[test] - fn fsk_bitrate_as_bps_limits() { - const ZERO: FskBitrate = FskBitrate::from_raw(0); - const ONE: FskBitrate = FskBitrate::from_raw(1); - const MAX: FskBitrate = FskBitrate::from_raw(u32::MAX); - - assert_eq!(ZERO.as_bps(), 0); - assert_eq!(ONE.as_bps(), 1_024_000_000); - assert_eq!(MAX.as_bps(), 61); - } - - #[test] - fn fsk_bitrate_from_bps_limits() { - const ZERO: FskBitrate = FskBitrate::from_bps(0); - const ONE: FskBitrate = FskBitrate::from_bps(1); - const MAX: FskBitrate = FskBitrate::from_bps(u32::MAX); - - assert_eq!(ZERO.as_bps(), 61); - assert_eq!(ONE.as_bps(), 61); - assert_eq!(MAX.as_bps(), 0); - } - - #[test] - fn fsk_fdev_ord() { - assert!(FskFdev::from_hertz(30_000) > FskFdev::from_hertz(20_000)); - assert!(FskFdev::from_hertz(20_000) < FskFdev::from_hertz(30_000)); - } - - #[test] - fn fsk_fdev_as_hertz_limits() { - const ZERO: FskFdev = FskFdev::from_raw(0); - const ONE: FskFdev = FskFdev::from_raw(1); - const MAX: FskFdev = FskFdev::from_raw(u32::MAX); - - assert_eq!(ZERO.as_hertz(), 0); - assert_eq!(ONE.as_hertz(), 0); - assert_eq!(MAX.as_hertz(), 15_999_999); - } - - #[test] - fn fsk_fdev_from_hertz_limits() { - const ZERO: FskFdev = FskFdev::from_hertz(0); - const ONE: FskFdev = FskFdev::from_hertz(1); - const MAX: FskFdev = FskFdev::from_hertz(u32::MAX); - - assert_eq!(ZERO.as_hertz(), 0); - assert_eq!(ONE.as_hertz(), 0); - assert_eq!(MAX.as_hertz(), 6_967_294); - } -} diff --git a/embassy-stm32/src/subghz/ocp.rs b/embassy-stm32/src/subghz/ocp.rs deleted file mode 100644 index 81e89c21..00000000 --- a/embassy-stm32/src/subghz/ocp.rs +++ /dev/null @@ -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, -} diff --git a/embassy-stm32/src/subghz/op_error.rs b/embassy-stm32/src/subghz/op_error.rs deleted file mode 100644 index b17b9920..00000000 --- a/embassy-stm32/src/subghz/op_error.rs +++ /dev/null @@ -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) - } -} diff --git a/embassy-stm32/src/subghz/pa_config.rs b/embassy-stm32/src/subghz/pa_config.rs deleted file mode 100644 index 875827bd..00000000 --- a/embassy-stm32/src/subghz/pa_config.rs +++ /dev/null @@ -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 { - 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); - } -} diff --git a/embassy-stm32/src/subghz/packet_params.rs b/embassy-stm32/src/subghz/packet_params.rs deleted file mode 100644 index db1fb88d..00000000 --- a/embassy-stm32/src/subghz/packet_params.rs +++ /dev/null @@ -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() - } -} diff --git a/embassy-stm32/src/subghz/packet_status.rs b/embassy-stm32/src/subghz/packet_status.rs deleted file mode 100644 index b3acd73c..00000000 --- a/embassy-stm32/src/subghz/packet_status.rs +++ /dev/null @@ -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 { - 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 { - 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 { - 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 { - 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 { - 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() - } -} diff --git a/embassy-stm32/src/subghz/packet_type.rs b/embassy-stm32/src/subghz/packet_type.rs deleted file mode 100644 index 88c62bb6..00000000 --- a/embassy-stm32/src/subghz/packet_type.rs +++ /dev/null @@ -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 { - match bits { - 0 => Ok(PacketType::Fsk), - 1 => Ok(PacketType::LoRa), - 2 => Ok(PacketType::Bpsk), - 3 => Ok(PacketType::Msk), - _ => Err(bits), - } - } -} diff --git a/embassy-stm32/src/subghz/pkt_ctrl.rs b/embassy-stm32/src/subghz/pkt_ctrl.rs deleted file mode 100644 index 265833e3..00000000 --- a/embassy-stm32/src/subghz/pkt_ctrl.rs +++ /dev/null @@ -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 for u8 { - fn from(pc: PktCtrl) -> Self { - pc.val - } -} - -impl Default for PktCtrl { - fn default() -> Self { - Self::RESET - } -} diff --git a/embassy-stm32/src/subghz/pmode.rs b/embassy-stm32/src/subghz/pmode.rs deleted file mode 100644 index 0c07f319..00000000 --- a/embassy-stm32/src/subghz/pmode.rs +++ /dev/null @@ -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, -} diff --git a/embassy-stm32/src/subghz/pwr_ctrl.rs b/embassy-stm32/src/subghz/pwr_ctrl.rs deleted file mode 100644 index 974bddeb..00000000 --- a/embassy-stm32/src/subghz/pwr_ctrl.rs +++ /dev/null @@ -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 for u8 { - fn from(bs: PwrCtrl) -> Self { - bs.val - } -} - -impl Default for PwrCtrl { - fn default() -> Self { - Self::RESET - } -} diff --git a/embassy-stm32/src/subghz/reg_mode.rs b/embassy-stm32/src/subghz/reg_mode.rs deleted file mode 100644 index b8322695..00000000 --- a/embassy-stm32/src/subghz/reg_mode.rs +++ /dev/null @@ -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 - } -} diff --git a/embassy-stm32/src/subghz/rf_frequency.rs b/embassy-stm32/src/subghz/rf_frequency.rs deleted file mode 100644 index 3de2f50c..00000000 --- a/embassy-stm32/src/subghz/rf_frequency.rs +++ /dev/null @@ -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: - /// - /// RFPLL = 32e6 × bits / 225 - /// - /// # 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 = RFPLL * 225 / 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); - } -} diff --git a/embassy-stm32/src/subghz/rx_timeout_stop.rs b/embassy-stm32/src/subghz/rx_timeout_stop.rs deleted file mode 100644 index 1d4aaece..00000000 --- a/embassy-stm32/src/subghz/rx_timeout_stop.rs +++ /dev/null @@ -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 for u8 { - fn from(rx_ts: RxTimeoutStop) -> Self { - rx_ts as u8 - } -} diff --git a/embassy-stm32/src/subghz/sleep_cfg.rs b/embassy-stm32/src/subghz/sleep_cfg.rs deleted file mode 100644 index 0a50e970..00000000 --- a/embassy-stm32/src/subghz/sleep_cfg.rs +++ /dev/null @@ -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 for u8 { - fn from(sc: SleepCfg) -> Self { - sc.0 - } -} - -impl Default for SleepCfg { - fn default() -> Self { - Self::new() - } -} diff --git a/embassy-stm32/src/subghz/smps.rs b/embassy-stm32/src/subghz/smps.rs deleted file mode 100644 index 81615ea7..00000000 --- a/embassy-stm32/src/subghz/smps.rs +++ /dev/null @@ -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 - } -} diff --git a/embassy-stm32/src/subghz/standby_clk.rs b/embassy-stm32/src/subghz/standby_clk.rs deleted file mode 100644 index c130bbee..00000000 --- a/embassy-stm32/src/subghz/standby_clk.rs +++ /dev/null @@ -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 for u8 { - fn from(sc: StandbyClk) -> Self { - sc as u8 - } -} diff --git a/embassy-stm32/src/subghz/stats.rs b/embassy-stm32/src/subghz/stats.rs deleted file mode 100644 index 41b7a200..00000000 --- a/embassy-stm32/src/subghz/stats.rs +++ /dev/null @@ -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 { - status: Status, - pkt_rx: u16, - pkt_crc: u16, - pkt_len_or_hdr_err: u16, - ty: ModType, -} - -impl Stats { - const fn from_buf(buf: [u8; 7], ty: ModType) -> Stats { - 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 = 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 = 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 = 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 { - /// 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 = Stats::from_raw_fsk(example_data_from_radio); - /// ``` - pub const fn from_raw_fsk(buf: [u8; 7]) -> Stats { - 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 = 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 { - /// 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 = Stats::from_raw_lora(example_data_from_radio); - /// ``` - pub const fn from_raw_lora(buf: [u8; 7]) -> Stats { - 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 = 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 { - 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 = 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); - } -} diff --git a/embassy-stm32/src/subghz/status.rs b/embassy-stm32/src/subghz/status.rs deleted file mode 100644 index b84034f6..00000000 --- a/embassy-stm32/src/subghz/status.rs +++ /dev/null @@ -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 { - 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 { - 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 for Status { - fn from(x: u8) -> Self { - Status(x) - } -} -impl From 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::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::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()) - } -} diff --git a/embassy-stm32/src/subghz/tcxo_mode.rs b/embassy-stm32/src/subghz/tcxo_mode.rs deleted file mode 100644 index 698dee0a..00000000 --- a/embassy-stm32/src/subghz/tcxo_mode.rs +++ /dev/null @@ -1,170 +0,0 @@ -use super::Timeout; - -/// TCXO trim. -/// -/// **Note:** To use VDDTCXO, the VDDRF 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 VDDTCXO, the VDDRF 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() - } -} diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs deleted file mode 100644 index 0ae49dd9..00000000 --- a/embassy-stm32/src/subghz/timeout.rs +++ /dev/null @@ -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> { - // 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 for Duration { - fn from(to: Timeout) -> Self { - to.as_duration() - } -} - -impl From for [u8; 3] { - fn from(to: Timeout) -> Self { - to.as_bytes() - } -} - -#[cfg(feature = "time")] -impl From 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())) - ); - } -} diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs deleted file mode 100644 index 03bdb1ea..00000000 --- a/embassy-stm32/src/subghz/tx_params.rs +++ /dev/null @@ -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 for u8 { - fn from(rt: RampTime) -> Self { - rt as u8 - } -} - -impl From 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 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() - } -} diff --git a/embassy-stm32/src/subghz/value_error.rs b/embassy-stm32/src/subghz/value_error.rs deleted file mode 100644 index 6a0b489a..00000000 --- a/embassy-stm32/src/subghz/value_error.rs +++ /dev/null @@ -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 { - value: T, - limit: T, - over: bool, -} - -impl ValueError { - /// 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 = ValueError::too_high(101u8, 100u8); - /// assert!(ERROR.over()); - /// assert!(!ERROR.under()); - /// ``` - pub const fn too_high(value: T, limit: T) -> ValueError { - 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 = ValueError::too_low(200u8, 201u8); - /// assert!(ERROR.under()); - /// assert!(!ERROR.over()); - /// ``` - pub const fn too_low(value: T, limit: T) -> ValueError { - ValueError { - value, - limit, - over: false, - } - } - - /// Get the value that caused the error. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::ValueError; - /// - /// const ERROR: ValueError = 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 = 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 = 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 = ValueError::too_low(200u8, 201u8); - /// assert!(ERROR.under()); - /// assert!(!ERROR.over()); - /// ``` - pub const fn under(&self) -> bool { - !self.over - } -} - -impl core::fmt::Display for ValueError -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) - } - } -} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 59f30a9b..77be46ff 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index ca022e25..e9d2127c 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -14,7 +14,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }