diff options
| author | ceekdee <[email protected]> | 2023-04-27 11:05:33 -0500 |
|---|---|---|
| committer | ceekdee <[email protected]> | 2023-04-27 11:05:33 -0500 |
| commit | 9d610c68663d806601bbe8855e63a52e8e610d14 (patch) | |
| tree | 7ecae429d3ac69d9240ce1f8792cb2cc6cd4b89c | |
| parent | 03d6363d5af5dcaf21b52734994a466ca593d2b6 (diff) | |
Remove legacy LoRa drivers.
45 files changed, 6 insertions, 9701 deletions
| @@ -35,7 +35,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac | |||
| 35 | The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. | 35 | The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. |
| 36 | 36 | ||
| 37 | - **LoRa** - | 37 | - **LoRa** - |
| 38 | <a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers. | 38 | <a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking. |
| 39 | 39 | ||
| 40 | - **USB** - | 40 | - **USB** - |
| 41 | <a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. | 41 | <a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. |
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 692a82040..aa0d2911d 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 | |||
| 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" | 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" |
| 10 | features = ["time", "defmt"] | 10 | features = ["time", "defmt"] |
| 11 | flavors = [ | 11 | flavors = [ |
| 12 | { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, | ||
| 13 | { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x"] }, | ||
| 14 | { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, | 12 | { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, |
| 15 | ] | 13 | ] |
| 16 | 14 | ||
| 17 | [lib] | 15 | [lib] |
| 18 | 16 | ||
| 19 | [features] | 17 | [features] |
| 20 | sx126x = [] | ||
| 21 | sx127x = [] | ||
| 22 | stm32wl = ["dep:embassy-stm32"] | 18 | stm32wl = ["dep:embassy-stm32"] |
| 23 | time = [] | 19 | time = [] |
| 24 | defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] | 20 | defmt = ["dep:defmt", "lorawan-device/defmt"] |
| 25 | external-lora-phy = ["dep:lora-phy"] | 21 | external-lora-phy = ["dep:lora-phy"] |
| 26 | 22 | ||
| 27 | [dependencies] | 23 | [dependencies] |
| @@ -41,4 +37,3 @@ bit_field = { version = "0.10" } | |||
| 41 | 37 | ||
| 42 | lora-phy = { version = "1", optional = true } | 38 | lora-phy = { version = "1", optional = true } |
| 43 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } | 39 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } |
| 44 | lorawan = { version = "0.7.3", default-features = false } | ||
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index c01d3f71a..c391a8029 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs | |||
| @@ -1,24 +1,13 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![feature(async_fn_in_trait, impl_trait_projections)] | 2 | #![feature(async_fn_in_trait, impl_trait_projections)] |
| 3 | #![allow(incomplete_features)] | 3 | #![allow(incomplete_features)] |
| 4 | //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device | 4 | //! embassy-lora holds LoRa-specific functionality. |
| 5 | //! crate's async LoRaWAN MAC implementation. | ||
| 6 | 5 | ||
| 7 | pub(crate) mod fmt; | 6 | pub(crate) mod fmt; |
| 8 | #[cfg(feature = "external-lora-phy")] | 7 | #[cfg(feature = "external-lora-phy")] |
| 9 | /// interface variants required by the external lora crate | 8 | /// interface variants required by the external lora physical layer crate (lora-phy) |
| 10 | pub mod iv; | 9 | pub mod iv; |
| 11 | 10 | ||
| 12 | #[cfg(feature = "stm32wl")] | ||
| 13 | #[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] | ||
| 14 | pub mod stm32wl; | ||
| 15 | #[cfg(feature = "sx126x")] | ||
| 16 | #[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] | ||
| 17 | pub mod sx126x; | ||
| 18 | #[cfg(feature = "sx127x")] | ||
| 19 | #[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] | ||
| 20 | pub mod sx127x; | ||
| 21 | |||
| 22 | #[cfg(feature = "time")] | 11 | #[cfg(feature = "time")] |
| 23 | use embassy_time::{Duration, Instant, Timer}; | 12 | use embassy_time::{Duration, Instant, Timer}; |
| 24 | 13 | ||
diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs deleted file mode 100644 index dae9a195c..000000000 --- a/embassy-lora/src/stm32wl/mod.rs +++ /dev/null | |||
| @@ -1,291 +0,0 @@ | |||
| 1 | //! A radio driver integration for the radio found on STM32WL family devices. | ||
| 2 | #![allow(deprecated)] | ||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | ||
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; | ||
| 9 | use embassy_stm32::subghz::{ | ||
| 10 | CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, | ||
| 11 | LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, | ||
| 12 | Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, | ||
| 13 | }; | ||
| 14 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 15 | use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; | ||
| 16 | use lorawan_device::async_device::Timings; | ||
| 17 | |||
| 18 | #[derive(Debug, Copy, Clone)] | ||
| 19 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 20 | pub enum State { | ||
| 21 | Idle, | ||
| 22 | Txing, | ||
| 23 | Rxing, | ||
| 24 | } | ||
| 25 | |||
| 26 | #[derive(Debug, Copy, Clone)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | pub struct RadioError; | ||
| 29 | |||
| 30 | static IRQ_WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 31 | |||
| 32 | /// The radio peripheral keeping the radio state and owning the radio IRQ. | ||
| 33 | pub struct SubGhzRadio<'d, RS> { | ||
| 34 | radio: SubGhz<'d, NoDma, NoDma>, | ||
| 35 | switch: RS, | ||
| 36 | irq: PeripheralRef<'d, SUBGHZ_RADIO>, | ||
| 37 | } | ||
| 38 | |||
| 39 | #[derive(Default)] | ||
| 40 | #[non_exhaustive] | ||
| 41 | pub struct SubGhzRadioConfig { | ||
| 42 | pub reg_mode: RegMode, | ||
| 43 | pub calibrate_image: CalibrateImage, | ||
| 44 | pub pa_config: PaConfig, | ||
| 45 | pub tx_params: TxParams, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { | ||
| 49 | /// Create a new instance of a SubGhz radio for LoRaWAN. | ||
| 50 | pub fn new( | ||
| 51 | mut radio: SubGhz<'d, NoDma, NoDma>, | ||
| 52 | switch: RS, | ||
| 53 | irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd, | ||
| 54 | config: SubGhzRadioConfig, | ||
| 55 | ) -> Result<Self, RadioError> { | ||
| 56 | into_ref!(irq); | ||
| 57 | |||
| 58 | radio.reset(); | ||
| 59 | |||
| 60 | irq.disable(); | ||
| 61 | irq.set_handler(|_| { | ||
| 62 | IRQ_WAKER.wake(); | ||
| 63 | unsafe { SUBGHZ_RADIO::steal().disable() }; | ||
| 64 | }); | ||
| 65 | |||
| 66 | configure_radio(&mut radio, config)?; | ||
| 67 | |||
| 68 | Ok(Self { radio, switch, irq }) | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form | ||
| 72 | /// the upcoming RX window start. | ||
| 73 | async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> { | ||
| 74 | trace!("TX request: {:?}", config); | ||
| 75 | self.switch.set_tx(); | ||
| 76 | |||
| 77 | self.radio | ||
| 78 | .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; | ||
| 79 | |||
| 80 | self.set_lora_mod_params(config.rf)?; | ||
| 81 | |||
| 82 | let packet_params = LoRaPacketParams::new() | ||
| 83 | .set_preamble_len(8) | ||
| 84 | .set_header_type(HeaderType::Variable) | ||
| 85 | .set_payload_len(buf.len() as u8) | ||
| 86 | .set_crc_en(true) | ||
| 87 | .set_invert_iq(false); | ||
| 88 | |||
| 89 | self.radio.set_lora_packet_params(&packet_params)?; | ||
| 90 | |||
| 91 | let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout); | ||
| 92 | self.radio.set_irq_cfg(&irq_cfg)?; | ||
| 93 | |||
| 94 | self.radio.set_buffer_base_address(0, 0)?; | ||
| 95 | self.radio.write_buffer(0, buf)?; | ||
| 96 | |||
| 97 | // The maximum airtime for any LoRaWAN package is 2793.5ms. | ||
| 98 | // The value of 4000ms is copied from C driver and gives us a good safety margin. | ||
| 99 | self.radio.set_tx(Timeout::from_millis_sat(4000))?; | ||
| 100 | trace!("TX started"); | ||
| 101 | |||
| 102 | loop { | ||
| 103 | let (_status, irq_status) = self.irq_wait().await; | ||
| 104 | |||
| 105 | if irq_status & Irq::TxDone.mask() != 0 { | ||
| 106 | trace!("TX done"); | ||
| 107 | return Ok(0); | ||
| 108 | } | ||
| 109 | |||
| 110 | if irq_status & Irq::Timeout.mask() != 0 { | ||
| 111 | return Err(RadioError); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> { | ||
| 117 | let mod_params = LoRaModParams::new() | ||
| 118 | .set_sf(convert_spreading_factor(&config.spreading_factor)) | ||
| 119 | .set_bw(convert_bandwidth(&config.bandwidth)) | ||
| 120 | .set_cr(CodingRate::Cr45) | ||
| 121 | .set_ldro_en(matches!( | ||
| 122 | (config.spreading_factor, config.bandwidth), | ||
| 123 | (SpreadingFactor::_12, Bandwidth::_125KHz) | ||
| 124 | | (SpreadingFactor::_12, Bandwidth::_250KHz) | ||
| 125 | | (SpreadingFactor::_11, Bandwidth::_125KHz) | ||
| 126 | )); | ||
| 127 | self.radio.set_lora_mod_params(&mod_params) | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Perform a radio receive operation with the radio config and receive buffer. The receive buffer must | ||
| 131 | /// be able to hold a single LoRaWAN packet. | ||
| 132 | async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { | ||
| 133 | assert!(buf.len() >= 255); | ||
| 134 | trace!("RX request: {:?}", config); | ||
| 135 | self.switch.set_rx(); | ||
| 136 | |||
| 137 | self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; | ||
| 138 | |||
| 139 | self.set_lora_mod_params(config)?; | ||
| 140 | |||
| 141 | let packet_params = LoRaPacketParams::new() | ||
| 142 | .set_preamble_len(8) | ||
| 143 | .set_header_type(HeaderType::Variable) | ||
| 144 | .set_payload_len(0xFF) | ||
| 145 | .set_crc_en(false) | ||
| 146 | .set_invert_iq(true); | ||
| 147 | self.radio.set_lora_packet_params(&packet_params)?; | ||
| 148 | |||
| 149 | let irq_cfg = CfgIrq::new() | ||
| 150 | .irq_enable_all(Irq::RxDone) | ||
| 151 | .irq_enable_all(Irq::PreambleDetected) | ||
| 152 | .irq_enable_all(Irq::HeaderValid) | ||
| 153 | .irq_enable_all(Irq::HeaderErr) | ||
| 154 | .irq_enable_all(Irq::Err) | ||
| 155 | .irq_enable_all(Irq::Timeout); | ||
| 156 | self.radio.set_irq_cfg(&irq_cfg)?; | ||
| 157 | |||
| 158 | self.radio.set_buffer_base_address(0, 0)?; | ||
| 159 | |||
| 160 | // NOTE: Upper layer handles timeout by cancelling the future | ||
| 161 | self.radio.set_rx(Timeout::DISABLED)?; | ||
| 162 | |||
| 163 | trace!("RX started"); | ||
| 164 | |||
| 165 | loop { | ||
| 166 | let (_status, irq_status) = self.irq_wait().await; | ||
| 167 | |||
| 168 | if irq_status & Irq::RxDone.mask() != 0 { | ||
| 169 | let (_status, len, ptr) = self.radio.rx_buffer_status()?; | ||
| 170 | let packet_status = self.radio.lora_packet_status()?; | ||
| 171 | let rssi = packet_status.rssi_pkt().to_integer(); | ||
| 172 | let snr = packet_status.snr_pkt().to_integer(); | ||
| 173 | self.radio.read_buffer(ptr, &mut buf[..len as usize])?; | ||
| 174 | self.radio.set_standby(StandbyClk::Rc)?; | ||
| 175 | |||
| 176 | #[cfg(feature = "defmt")] | ||
| 177 | trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); | ||
| 178 | |||
| 179 | #[cfg(feature = "log")] | ||
| 180 | trace!("RX done: {:02x?}", &mut buf[..len as usize]); | ||
| 181 | return Ok((len as usize, RxQuality::new(rssi, snr as i8))); | ||
| 182 | } | ||
| 183 | |||
| 184 | if irq_status & Irq::Timeout.mask() != 0 { | ||
| 185 | return Err(RadioError); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | async fn irq_wait(&mut self) -> (Status, u16) { | ||
| 191 | poll_fn(|cx| { | ||
| 192 | self.irq.unpend(); | ||
| 193 | self.irq.enable(); | ||
| 194 | IRQ_WAKER.register(cx.waker()); | ||
| 195 | |||
| 196 | let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); | ||
| 197 | self.radio | ||
| 198 | .clear_irq_status(irq_status) | ||
| 199 | .expect("error clearing irq status"); | ||
| 200 | |||
| 201 | trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status); | ||
| 202 | |||
| 203 | if irq_status == 0 { | ||
| 204 | Poll::Pending | ||
| 205 | } else { | ||
| 206 | Poll::Ready((status, irq_status)) | ||
| 207 | } | ||
| 208 | }) | ||
| 209 | .await | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> { | ||
| 214 | trace!("Configuring STM32WL SUBGHZ radio"); | ||
| 215 | |||
| 216 | radio.set_regulator_mode(config.reg_mode)?; | ||
| 217 | radio.set_standby(StandbyClk::Rc)?; | ||
| 218 | |||
| 219 | let tcxo_mode = TcxoMode::new() | ||
| 220 | .set_txco_trim(TcxoTrim::Volts1pt7) | ||
| 221 | .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100))); | ||
| 222 | radio.set_tcxo_mode(&tcxo_mode)?; | ||
| 223 | // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control". | ||
| 224 | // The STM32CUBE C driver also does this. | ||
| 225 | radio.set_hse_in_trim(HseTrim::MIN)?; | ||
| 226 | |||
| 227 | // Re-calibrate everything after setting the TXCO config. | ||
| 228 | radio.calibrate(0x7F)?; | ||
| 229 | radio.calibrate_image(config.calibrate_image)?; | ||
| 230 | |||
| 231 | radio.set_pa_config(&config.pa_config)?; | ||
| 232 | radio.set_tx_params(&config.tx_params)?; | ||
| 233 | radio.set_pa_ocp(Ocp::Max140m)?; | ||
| 234 | |||
| 235 | radio.set_packet_type(PacketType::LoRa)?; | ||
| 236 | radio.set_lora_sync_word(LoRaSyncWord::Public)?; | ||
| 237 | |||
| 238 | trace!("Done initializing STM32WL SUBGHZ radio"); | ||
| 239 | Ok(()) | ||
| 240 | } | ||
| 241 | |||
| 242 | impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { | ||
| 243 | type PhyError = RadioError; | ||
| 244 | |||
| 245 | async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> { | ||
| 246 | self.do_tx(config, buf).await | ||
| 247 | } | ||
| 248 | |||
| 249 | async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { | ||
| 250 | self.do_rx(config, buf).await | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | impl From<embassy_stm32::spi::Error> for RadioError { | ||
| 255 | fn from(_: embassy_stm32::spi::Error) -> Self { | ||
| 256 | RadioError | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | impl<'d, RS> Timings for SubGhzRadio<'d, RS> { | ||
| 261 | fn get_rx_window_offset_ms(&self) -> i32 { | ||
| 262 | -3 | ||
| 263 | } | ||
| 264 | fn get_rx_window_duration_ms(&self) -> u32 { | ||
| 265 | 1003 | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | pub trait RadioSwitch { | ||
| 270 | fn set_rx(&mut self); | ||
| 271 | fn set_tx(&mut self); | ||
| 272 | } | ||
| 273 | |||
| 274 | fn convert_spreading_factor(sf: &SpreadingFactor) -> SF { | ||
| 275 | match sf { | ||
| 276 | SpreadingFactor::_7 => SF::Sf7, | ||
| 277 | SpreadingFactor::_8 => SF::Sf8, | ||
| 278 | SpreadingFactor::_9 => SF::Sf9, | ||
| 279 | SpreadingFactor::_10 => SF::Sf10, | ||
| 280 | SpreadingFactor::_11 => SF::Sf11, | ||
| 281 | SpreadingFactor::_12 => SF::Sf12, | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth { | ||
| 286 | match bw { | ||
| 287 | Bandwidth::_125KHz => LoRaBandwidth::Bw125, | ||
| 288 | Bandwidth::_250KHz => LoRaBandwidth::Bw250, | ||
| 289 | Bandwidth::_500KHz => LoRaBandwidth::Bw500, | ||
| 290 | } | ||
| 291 | } | ||
diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs deleted file mode 100644 index 2f0b8c8e3..000000000 --- a/embassy-lora/src/sx126x/mod.rs +++ /dev/null | |||
| @@ -1,137 +0,0 @@ | |||
| 1 | use defmt::Format; | ||
| 2 | use embedded_hal::digital::v2::OutputPin; | ||
| 3 | use embedded_hal_async::digital::Wait; | ||
| 4 | use embedded_hal_async::spi::*; | ||
| 5 | use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig}; | ||
| 6 | use lorawan_device::async_device::Timings; | ||
| 7 | |||
| 8 | mod sx126x_lora; | ||
| 9 | use sx126x_lora::LoRa; | ||
| 10 | |||
| 11 | use self::sx126x_lora::mod_params::RadioError; | ||
| 12 | |||
| 13 | /// Semtech Sx126x LoRa peripheral | ||
| 14 | pub struct Sx126xRadio<SPI, CTRL, WAIT, BUS> | ||
| 15 | where | ||
| 16 | SPI: SpiBus<u8, Error = BUS> + 'static, | ||
| 17 | CTRL: OutputPin + 'static, | ||
| 18 | WAIT: Wait + 'static, | ||
| 19 | BUS: Error + Format + 'static, | ||
| 20 | { | ||
| 21 | pub lora: LoRa<SPI, CTRL, WAIT>, | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<SPI, CTRL, WAIT, BUS> Sx126xRadio<SPI, CTRL, WAIT, BUS> | ||
| 25 | where | ||
| 26 | SPI: SpiBus<u8, Error = BUS> + 'static, | ||
| 27 | CTRL: OutputPin + 'static, | ||
| 28 | WAIT: Wait + 'static, | ||
| 29 | BUS: Error + Format + 'static, | ||
| 30 | { | ||
| 31 | pub async fn new( | ||
| 32 | spi: SPI, | ||
| 33 | cs: CTRL, | ||
| 34 | reset: CTRL, | ||
| 35 | antenna_rx: CTRL, | ||
| 36 | antenna_tx: CTRL, | ||
| 37 | dio1: WAIT, | ||
| 38 | busy: WAIT, | ||
| 39 | enable_public_network: bool, | ||
| 40 | ) -> Result<Self, RadioError<BUS>> { | ||
| 41 | let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy); | ||
| 42 | lora.init().await?; | ||
| 43 | lora.set_lora_modem(enable_public_network).await?; | ||
| 44 | Ok(Self { lora }) | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<SPI, CTRL, WAIT, BUS> Timings for Sx126xRadio<SPI, CTRL, WAIT, BUS> | ||
| 49 | where | ||
| 50 | SPI: SpiBus<u8, Error = BUS> + 'static, | ||
| 51 | CTRL: OutputPin + 'static, | ||
| 52 | WAIT: Wait + 'static, | ||
| 53 | BUS: Error + Format + 'static, | ||
| 54 | { | ||
| 55 | fn get_rx_window_offset_ms(&self) -> i32 { | ||
| 56 | -50 | ||
| 57 | } | ||
| 58 | fn get_rx_window_duration_ms(&self) -> u32 { | ||
| 59 | 1050 | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | impl<SPI, CTRL, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CTRL, WAIT, BUS> | ||
| 64 | where | ||
| 65 | SPI: SpiBus<u8, Error = BUS> + 'static, | ||
| 66 | CTRL: OutputPin + 'static, | ||
| 67 | WAIT: Wait + 'static, | ||
| 68 | BUS: Error + Format + 'static, | ||
| 69 | { | ||
| 70 | type PhyError = RadioError<BUS>; | ||
| 71 | |||
| 72 | async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result<u32, Self::PhyError> { | ||
| 73 | trace!("TX START"); | ||
| 74 | self.lora | ||
| 75 | .set_tx_config( | ||
| 76 | config.pw, | ||
| 77 | config.rf.spreading_factor.into(), | ||
| 78 | config.rf.bandwidth.into(), | ||
| 79 | config.rf.coding_rate.into(), | ||
| 80 | 8, | ||
| 81 | false, | ||
| 82 | true, | ||
| 83 | false, | ||
| 84 | 0, | ||
| 85 | false, | ||
| 86 | ) | ||
| 87 | .await?; | ||
| 88 | self.lora.set_max_payload_length(buffer.len() as u8).await?; | ||
| 89 | self.lora.set_channel(config.rf.frequency).await?; | ||
| 90 | self.lora.send(buffer, 0xffffff).await?; | ||
| 91 | self.lora.process_irq(None, None, None).await?; | ||
| 92 | trace!("TX DONE"); | ||
| 93 | return Ok(0); | ||
| 94 | } | ||
| 95 | |||
| 96 | async fn rx( | ||
| 97 | &mut self, | ||
| 98 | config: RfConfig, | ||
| 99 | receiving_buffer: &mut [u8], | ||
| 100 | ) -> Result<(usize, RxQuality), Self::PhyError> { | ||
| 101 | trace!("RX START"); | ||
| 102 | self.lora | ||
| 103 | .set_rx_config( | ||
| 104 | config.spreading_factor.into(), | ||
| 105 | config.bandwidth.into(), | ||
| 106 | config.coding_rate.into(), | ||
| 107 | 8, | ||
| 108 | 4, | ||
| 109 | false, | ||
| 110 | 0u8, | ||
| 111 | true, | ||
| 112 | false, | ||
| 113 | 0, | ||
| 114 | true, | ||
| 115 | true, | ||
| 116 | ) | ||
| 117 | .await?; | ||
| 118 | self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; | ||
| 119 | self.lora.set_channel(config.frequency).await?; | ||
| 120 | self.lora.rx(90 * 1000).await?; | ||
| 121 | let mut received_len = 0u8; | ||
| 122 | self.lora | ||
| 123 | .process_irq(Some(receiving_buffer), Some(&mut received_len), None) | ||
| 124 | .await?; | ||
| 125 | trace!("RX DONE"); | ||
| 126 | |||
| 127 | let packet_status = self.lora.get_latest_packet_status(); | ||
| 128 | let mut rssi = 0i16; | ||
| 129 | let mut snr = 0i8; | ||
| 130 | if packet_status.is_some() { | ||
| 131 | rssi = packet_status.unwrap().rssi as i16; | ||
| 132 | snr = packet_status.unwrap().snr; | ||
| 133 | } | ||
| 134 | |||
| 135 | Ok((received_len as usize, RxQuality::new(rssi, snr))) | ||
| 136 | } | ||
| 137 | } | ||
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 a7b9e1486..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ /dev/null | |||
| @@ -1,256 +0,0 @@ | |||
| 1 | use embassy_time::{Duration, Timer}; | ||
| 2 | use embedded_hal::digital::v2::OutputPin; | ||
| 3 | use embedded_hal_async::digital::Wait; | ||
| 4 | use embedded_hal_async::spi::SpiBus; | ||
| 5 | |||
| 6 | use super::mod_params::RadioError::*; | ||
| 7 | use super::mod_params::*; | ||
| 8 | use super::LoRa; | ||
| 9 | |||
| 10 | // Defines the time required for the TCXO to wakeup [ms]. | ||
| 11 | const BRD_TCXO_WAKEUP_TIME: u32 = 10; | ||
| 12 | |||
| 13 | // Provides board-specific functionality for Semtech SX126x-based boards. | ||
| 14 | |||
| 15 | impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT> | ||
| 16 | where | ||
| 17 | SPI: SpiBus<u8, Error = BUS>, | ||
| 18 | CTRL: OutputPin, | ||
| 19 | WAIT: Wait, | ||
| 20 | { | ||
| 21 | // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. | ||
| 22 | pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 23 | Ok(()) // no operation currently | ||
| 24 | } | ||
| 25 | |||
| 26 | // Initialize the TCXO power pin | ||
| 27 | pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 28 | let timeout = self.brd_get_board_tcxo_wakeup_time() << 6; | ||
| 29 | self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout) | ||
| 30 | .await?; | ||
| 31 | Ok(()) | ||
| 32 | } | ||
| 33 | |||
| 34 | // Initialize RF switch control pins | ||
| 35 | pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 36 | self.sub_set_dio2_as_rf_switch_ctrl(true).await?; | ||
| 37 | Ok(()) | ||
| 38 | } | ||
| 39 | |||
| 40 | // Initialize the radio debug pins | ||
| 41 | pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 42 | Ok(()) // no operation currently | ||
| 43 | } | ||
| 44 | |||
| 45 | // Hardware reset of the radio | ||
| 46 | pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 47 | Timer::after(Duration::from_millis(10)).await; | ||
| 48 | self.reset.set_low().map_err(|_| Reset)?; | ||
| 49 | Timer::after(Duration::from_millis(20)).await; | ||
| 50 | self.reset.set_high().map_err(|_| Reset)?; | ||
| 51 | Timer::after(Duration::from_millis(10)).await; | ||
| 52 | Ok(()) | ||
| 53 | } | ||
| 54 | |||
| 55 | // Wait while the busy pin is high | ||
| 56 | pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 57 | self.busy.wait_for_low().await.map_err(|_| Busy)?; | ||
| 58 | Ok(()) | ||
| 59 | } | ||
| 60 | |||
| 61 | // Wake up the radio | ||
| 62 | pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 63 | self.cs.set_low().map_err(|_| CS)?; | ||
| 64 | self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?; | ||
| 65 | self.spi.write(&[0x00]).await.map_err(SPI)?; | ||
| 66 | self.cs.set_high().map_err(|_| CS)?; | ||
| 67 | |||
| 68 | self.brd_wait_on_busy().await?; | ||
| 69 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 70 | Ok(()) | ||
| 71 | } | ||
| 72 | |||
| 73 | // Send a command that writes data to the radio | ||
| 74 | pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> { | ||
| 75 | self.sub_check_device_ready().await?; | ||
| 76 | |||
| 77 | self.cs.set_low().map_err(|_| CS)?; | ||
| 78 | self.spi.write(&[op_code.value()]).await.map_err(SPI)?; | ||
| 79 | self.spi.write(buffer).await.map_err(SPI)?; | ||
| 80 | self.cs.set_high().map_err(|_| CS)?; | ||
| 81 | |||
| 82 | if op_code != OpCode::SetSleep { | ||
| 83 | self.brd_wait_on_busy().await?; | ||
| 84 | } | ||
| 85 | Ok(()) | ||
| 86 | } | ||
| 87 | |||
| 88 | // Send a command that reads data from the radio, filling the provided buffer and returning a status | ||
| 89 | pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> { | ||
| 90 | let mut status = [0u8]; | ||
| 91 | let mut input = [0u8]; | ||
| 92 | |||
| 93 | self.sub_check_device_ready().await?; | ||
| 94 | |||
| 95 | self.cs.set_low().map_err(|_| CS)?; | ||
| 96 | self.spi.write(&[op_code.value()]).await.map_err(SPI)?; | ||
| 97 | self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?; | ||
| 98 | for i in 0..buffer.len() { | ||
| 99 | self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; | ||
| 100 | buffer[i] = input[0]; | ||
| 101 | } | ||
| 102 | self.cs.set_high().map_err(|_| CS)?; | ||
| 103 | |||
| 104 | self.brd_wait_on_busy().await?; | ||
| 105 | |||
| 106 | Ok(status[0]) | ||
| 107 | } | ||
| 108 | |||
| 109 | // Write one or more bytes of data to the radio memory | ||
| 110 | pub(super) async fn brd_write_registers( | ||
| 111 | &mut self, | ||
| 112 | start_register: Register, | ||
| 113 | buffer: &[u8], | ||
| 114 | ) -> Result<(), RadioError<BUS>> { | ||
| 115 | self.sub_check_device_ready().await?; | ||
| 116 | |||
| 117 | self.cs.set_low().map_err(|_| CS)?; | ||
| 118 | self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?; | ||
| 119 | self.spi | ||
| 120 | .write(&[ | ||
| 121 | ((start_register.addr() & 0xFF00) >> 8) as u8, | ||
| 122 | (start_register.addr() & 0x00FF) as u8, | ||
| 123 | ]) | ||
| 124 | .await | ||
| 125 | .map_err(SPI)?; | ||
| 126 | self.spi.write(buffer).await.map_err(SPI)?; | ||
| 127 | self.cs.set_high().map_err(|_| CS)?; | ||
| 128 | |||
| 129 | self.brd_wait_on_busy().await?; | ||
| 130 | Ok(()) | ||
| 131 | } | ||
| 132 | |||
| 133 | // Read one or more bytes of data from the radio memory | ||
| 134 | pub(super) async fn brd_read_registers( | ||
| 135 | &mut self, | ||
| 136 | start_register: Register, | ||
| 137 | buffer: &mut [u8], | ||
| 138 | ) -> Result<(), RadioError<BUS>> { | ||
| 139 | let mut input = [0u8]; | ||
| 140 | |||
| 141 | self.sub_check_device_ready().await?; | ||
| 142 | |||
| 143 | self.cs.set_low().map_err(|_| CS)?; | ||
| 144 | self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?; | ||
| 145 | self.spi | ||
| 146 | .write(&[ | ||
| 147 | ((start_register.addr() & 0xFF00) >> 8) as u8, | ||
| 148 | (start_register.addr() & 0x00FF) as u8, | ||
| 149 | 0x00u8, | ||
| 150 | ]) | ||
| 151 | .await | ||
| 152 | .map_err(SPI)?; | ||
| 153 | for i in 0..buffer.len() { | ||
| 154 | self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; | ||
| 155 | buffer[i] = input[0]; | ||
| 156 | } | ||
| 157 | self.cs.set_high().map_err(|_| CS)?; | ||
| 158 | |||
| 159 | self.brd_wait_on_busy().await?; | ||
| 160 | Ok(()) | ||
| 161 | } | ||
| 162 | |||
| 163 | // Write data to the buffer holding the payload in the radio | ||
| 164 | pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> { | ||
| 165 | self.sub_check_device_ready().await?; | ||
| 166 | |||
| 167 | self.cs.set_low().map_err(|_| CS)?; | ||
| 168 | self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?; | ||
| 169 | self.spi.write(&[offset]).await.map_err(SPI)?; | ||
| 170 | self.spi.write(buffer).await.map_err(SPI)?; | ||
| 171 | self.cs.set_high().map_err(|_| CS)?; | ||
| 172 | |||
| 173 | self.brd_wait_on_busy().await?; | ||
| 174 | Ok(()) | ||
| 175 | } | ||
| 176 | |||
| 177 | // Read data from the buffer holding the payload in the radio | ||
| 178 | pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> { | ||
| 179 | let mut input = [0u8]; | ||
| 180 | |||
| 181 | self.sub_check_device_ready().await?; | ||
| 182 | |||
| 183 | self.cs.set_low().map_err(|_| CS)?; | ||
| 184 | self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?; | ||
| 185 | self.spi.write(&[offset]).await.map_err(SPI)?; | ||
| 186 | self.spi.write(&[0x00]).await.map_err(SPI)?; | ||
| 187 | for i in 0..buffer.len() { | ||
| 188 | self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; | ||
| 189 | buffer[i] = input[0]; | ||
| 190 | } | ||
| 191 | self.cs.set_high().map_err(|_| CS)?; | ||
| 192 | |||
| 193 | self.brd_wait_on_busy().await?; | ||
| 194 | Ok(()) | ||
| 195 | } | ||
| 196 | |||
| 197 | // Set the radio output power | ||
| 198 | pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> { | ||
| 199 | self.sub_set_tx_params(power, RampTime::Ramp40Us).await?; | ||
| 200 | Ok(()) | ||
| 201 | } | ||
| 202 | |||
| 203 | // Get the radio type | ||
| 204 | pub(super) fn brd_get_radio_type(&mut self) -> RadioType { | ||
| 205 | RadioType::SX1262 | ||
| 206 | } | ||
| 207 | |||
| 208 | // Quiesce the antenna(s). | ||
| 209 | pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 210 | self.antenna_tx.set_low().map_err(|_| AntTx)?; | ||
| 211 | self.antenna_rx.set_low().map_err(|_| AntRx)?; | ||
| 212 | Ok(()) | ||
| 213 | } | ||
| 214 | |||
| 215 | // Prepare the antenna(s) for a receive operation | ||
| 216 | pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 217 | self.antenna_tx.set_low().map_err(|_| AntTx)?; | ||
| 218 | self.antenna_rx.set_high().map_err(|_| AntRx)?; | ||
| 219 | Ok(()) | ||
| 220 | } | ||
| 221 | |||
| 222 | // Prepare the antenna(s) for a send operation | ||
| 223 | pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 224 | self.antenna_rx.set_low().map_err(|_| AntRx)?; | ||
| 225 | self.antenna_tx.set_high().map_err(|_| AntTx)?; | ||
| 226 | Ok(()) | ||
| 227 | } | ||
| 228 | |||
| 229 | // Check if the given RF frequency is supported by the hardware | ||
| 230 | pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> { | ||
| 231 | Ok(true) | ||
| 232 | } | ||
| 233 | |||
| 234 | // Get the duration required for the TCXO to wakeup [ms]. | ||
| 235 | pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 { | ||
| 236 | BRD_TCXO_WAKEUP_TIME | ||
| 237 | } | ||
| 238 | |||
| 239 | /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process | ||
| 240 | pub(super) async fn brd_get_dio1_pin_state( | ||
| 241 | &mut self, | ||
| 242 | ) -> Result<u32, RadioError<BUS>> { | ||
| 243 | Ok(0) | ||
| 244 | } | ||
| 245 | */ | ||
| 246 | |||
| 247 | // Get the current radio operatiing mode | ||
| 248 | pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode { | ||
| 249 | self.operating_mode | ||
| 250 | } | ||
| 251 | |||
| 252 | // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts. | ||
| 253 | pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) { | ||
| 254 | self.operating_mode = mode; | ||
| 255 | } | ||
| 256 | } | ||
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 280f26d51..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ /dev/null | |||
| @@ -1,732 +0,0 @@ | |||
| 1 | #![allow(dead_code)] | ||
| 2 | |||
| 3 | use embassy_time::{Duration, Timer}; | ||
| 4 | use embedded_hal::digital::v2::OutputPin; | ||
| 5 | use embedded_hal_async::digital::Wait; | ||
| 6 | use embedded_hal_async::spi::SpiBus; | ||
| 7 | |||
| 8 | mod board_specific; | ||
| 9 | pub mod mod_params; | ||
| 10 | mod subroutine; | ||
| 11 | |||
| 12 | use mod_params::RadioError::*; | ||
| 13 | use mod_params::*; | ||
| 14 | |||
| 15 | // Syncwords for public and private networks | ||
| 16 | const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444; | ||
| 17 | const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424; | ||
| 18 | |||
| 19 | // Maximum number of registers that can be added to the retention list | ||
| 20 | const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4; | ||
| 21 | |||
| 22 | // Possible LoRa bandwidths | ||
| 23 | const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz]; | ||
| 24 | |||
| 25 | // Radio complete wakeup time with margin for temperature compensation [ms] | ||
| 26 | const RADIO_WAKEUP_TIME: u32 = 3; | ||
| 27 | |||
| 28 | /// Provides high-level access to Semtech SX126x-based boards | ||
| 29 | pub struct LoRa<SPI, CTRL, WAIT> { | ||
| 30 | spi: SPI, | ||
| 31 | cs: CTRL, | ||
| 32 | reset: CTRL, | ||
| 33 | antenna_rx: CTRL, | ||
| 34 | antenna_tx: CTRL, | ||
| 35 | dio1: WAIT, | ||
| 36 | busy: WAIT, | ||
| 37 | operating_mode: RadioMode, | ||
| 38 | rx_continuous: bool, | ||
| 39 | max_payload_length: u8, | ||
| 40 | modulation_params: Option<ModulationParams>, | ||
| 41 | packet_type: PacketType, | ||
| 42 | packet_params: Option<PacketParams>, | ||
| 43 | packet_status: Option<PacketStatus>, | ||
| 44 | image_calibrated: bool, | ||
| 45 | frequency_error: u32, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT> | ||
| 49 | where | ||
| 50 | SPI: SpiBus<u8, Error = BUS>, | ||
| 51 | CTRL: OutputPin, | ||
| 52 | WAIT: Wait, | ||
| 53 | { | ||
| 54 | /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () | ||
| 55 | pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self { | ||
| 56 | Self { | ||
| 57 | spi, | ||
| 58 | cs, | ||
| 59 | reset, | ||
| 60 | antenna_rx, | ||
| 61 | antenna_tx, | ||
| 62 | dio1, | ||
| 63 | busy, | ||
| 64 | operating_mode: RadioMode::Sleep, | ||
| 65 | rx_continuous: false, | ||
| 66 | max_payload_length: 0xFFu8, | ||
| 67 | modulation_params: None, | ||
| 68 | packet_type: PacketType::LoRa, | ||
| 69 | packet_params: None, | ||
| 70 | packet_status: None, | ||
| 71 | image_calibrated: false, | ||
| 72 | frequency_error: 0u32, // where is volatile FrequencyError modified ??? | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Initialize the radio | ||
| 77 | pub async fn init(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 78 | self.sub_init().await?; | ||
| 79 | self.sub_set_standby(StandbyMode::RC).await?; | ||
| 80 | self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?; | ||
| 81 | self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?; | ||
| 82 | self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?; | ||
| 83 | self.sub_set_dio_irq_params( | ||
| 84 | IrqMask::All.value(), | ||
| 85 | IrqMask::All.value(), | ||
| 86 | IrqMask::None.value(), | ||
| 87 | IrqMask::None.value(), | ||
| 88 | ) | ||
| 89 | .await?; | ||
| 90 | self.add_register_to_retention_list(Register::RxGain.addr()).await?; | ||
| 91 | self.add_register_to_retention_list(Register::TxModulation.addr()) | ||
| 92 | .await?; | ||
| 93 | Ok(()) | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Return current radio state | ||
| 97 | pub fn get_status(&mut self) -> RadioState { | ||
| 98 | match self.brd_get_operating_mode() { | ||
| 99 | RadioMode::Transmit => RadioState::TxRunning, | ||
| 100 | RadioMode::Receive => RadioState::RxRunning, | ||
| 101 | RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting, | ||
| 102 | _ => RadioState::Idle, | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired) | ||
| 107 | pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> { | ||
| 108 | self.sub_set_packet_type(PacketType::LoRa).await?; | ||
| 109 | if enable_public_network { | ||
| 110 | self.brd_write_registers( | ||
| 111 | Register::LoRaSyncword, | ||
| 112 | &[ | ||
| 113 | ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8, | ||
| 114 | (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8, | ||
| 115 | ], | ||
| 116 | ) | ||
| 117 | .await?; | ||
| 118 | } else { | ||
| 119 | self.brd_write_registers( | ||
| 120 | Register::LoRaSyncword, | ||
| 121 | &[ | ||
| 122 | ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8, | ||
| 123 | (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8, | ||
| 124 | ], | ||
| 125 | ) | ||
| 126 | .await?; | ||
| 127 | } | ||
| 128 | |||
| 129 | Ok(()) | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Sets the channel frequency | ||
| 133 | pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> { | ||
| 134 | self.sub_set_rf_frequency(frequency).await?; | ||
| 135 | Ok(()) | ||
| 136 | } | ||
| 137 | |||
| 138 | /* Checks if the channel is free for the given time. This is currently not implemented until a substitute | ||
| 139 | for switching to the FSK modem is found. | ||
| 140 | |||
| 141 | pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool; | ||
| 142 | */ | ||
| 143 | |||
| 144 | /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand. | ||
| 145 | /// After calling this function either set_rx_config() or set_tx_config() must be called. | ||
| 146 | pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> { | ||
| 147 | self.sub_set_dio_irq_params( | ||
| 148 | IrqMask::None.value(), | ||
| 149 | IrqMask::None.value(), | ||
| 150 | IrqMask::None.value(), | ||
| 151 | IrqMask::None.value(), | ||
| 152 | ) | ||
| 153 | .await?; | ||
| 154 | |||
| 155 | let result = self.sub_get_random().await?; | ||
| 156 | Ok(result) | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand. | ||
| 160 | /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] | ||
| 161 | /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] | ||
| 162 | /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] | ||
| 163 | /// preamble_length length in symbols (the hardware adds 4 more symbols) | ||
| 164 | /// symb_timeout RxSingle timeout value in symbols | ||
| 165 | /// fixed_len fixed length packets [0: variable, 1: fixed] | ||
| 166 | /// payload_len payload length when fixed length is used | ||
| 167 | /// crc_on [0: OFF, 1: ON] | ||
| 168 | /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] | ||
| 169 | /// hop_period number of symbols between each hop | ||
| 170 | /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] | ||
| 171 | /// rx_continuous reception mode [false: single mode, true: continuous mode] | ||
| 172 | pub async fn set_rx_config( | ||
| 173 | &mut self, | ||
| 174 | spreading_factor: SpreadingFactor, | ||
| 175 | bandwidth: Bandwidth, | ||
| 176 | coding_rate: CodingRate, | ||
| 177 | preamble_length: u16, | ||
| 178 | symb_timeout: u16, | ||
| 179 | fixed_len: bool, | ||
| 180 | payload_len: u8, | ||
| 181 | crc_on: bool, | ||
| 182 | _freq_hop_on: bool, | ||
| 183 | _hop_period: u8, | ||
| 184 | iq_inverted: bool, | ||
| 185 | rx_continuous: bool, | ||
| 186 | ) -> Result<(), RadioError<BUS>> { | ||
| 187 | let mut symb_timeout_final = symb_timeout; | ||
| 188 | |||
| 189 | self.rx_continuous = rx_continuous; | ||
| 190 | if self.rx_continuous { | ||
| 191 | symb_timeout_final = 0; | ||
| 192 | } | ||
| 193 | if fixed_len { | ||
| 194 | self.max_payload_length = payload_len; | ||
| 195 | } else { | ||
| 196 | self.max_payload_length = 0xFFu8; | ||
| 197 | } | ||
| 198 | |||
| 199 | self.sub_set_stop_rx_timer_on_preamble_detect(false).await?; | ||
| 200 | |||
| 201 | let mut low_data_rate_optimize = 0x00u8; | ||
| 202 | if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) | ||
| 203 | && (bandwidth == Bandwidth::_125KHz)) | ||
| 204 | || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) | ||
| 205 | { | ||
| 206 | low_data_rate_optimize = 0x01u8; | ||
| 207 | } | ||
| 208 | |||
| 209 | let modulation_params = ModulationParams { | ||
| 210 | spreading_factor: spreading_factor, | ||
| 211 | bandwidth: bandwidth, | ||
| 212 | coding_rate: coding_rate, | ||
| 213 | low_data_rate_optimize: low_data_rate_optimize, | ||
| 214 | }; | ||
| 215 | |||
| 216 | let mut preamble_length_final = preamble_length; | ||
| 217 | if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) | ||
| 218 | && (preamble_length < 12) | ||
| 219 | { | ||
| 220 | preamble_length_final = 12; | ||
| 221 | } | ||
| 222 | |||
| 223 | let packet_params = PacketParams { | ||
| 224 | preamble_length: preamble_length_final, | ||
| 225 | implicit_header: fixed_len, | ||
| 226 | payload_length: self.max_payload_length, | ||
| 227 | crc_on: crc_on, | ||
| 228 | iq_inverted: iq_inverted, | ||
| 229 | }; | ||
| 230 | |||
| 231 | self.modulation_params = Some(modulation_params); | ||
| 232 | self.packet_params = Some(packet_params); | ||
| 233 | |||
| 234 | self.standby().await?; | ||
| 235 | self.sub_set_modulation_params().await?; | ||
| 236 | self.sub_set_packet_params().await?; | ||
| 237 | self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?; | ||
| 238 | |||
| 239 | // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) | ||
| 240 | let mut iq_polarity = [0x00u8]; | ||
| 241 | self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?; | ||
| 242 | if iq_inverted { | ||
| 243 | self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))]) | ||
| 244 | .await?; | ||
| 245 | } else { | ||
| 246 | self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)]) | ||
| 247 | .await?; | ||
| 248 | } | ||
| 249 | Ok(()) | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Set the transmission parameters for the LoRa modem (only). | ||
| 253 | /// power output power [dBm] | ||
| 254 | /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] | ||
| 255 | /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] | ||
| 256 | /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] | ||
| 257 | /// preamble_length length in symbols (the hardware adds 4 more symbols) | ||
| 258 | /// fixed_len fixed length packets [0: variable, 1: fixed] | ||
| 259 | /// crc_on [0: OFF, 1: ON] | ||
| 260 | /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] | ||
| 261 | /// hop_period number of symbols between each hop | ||
| 262 | /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] | ||
| 263 | pub async fn set_tx_config( | ||
| 264 | &mut self, | ||
| 265 | power: i8, | ||
| 266 | spreading_factor: SpreadingFactor, | ||
| 267 | bandwidth: Bandwidth, | ||
| 268 | coding_rate: CodingRate, | ||
| 269 | preamble_length: u16, | ||
| 270 | fixed_len: bool, | ||
| 271 | crc_on: bool, | ||
| 272 | _freq_hop_on: bool, | ||
| 273 | _hop_period: u8, | ||
| 274 | iq_inverted: bool, | ||
| 275 | ) -> Result<(), RadioError<BUS>> { | ||
| 276 | let mut low_data_rate_optimize = 0x00u8; | ||
| 277 | if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) | ||
| 278 | && (bandwidth == Bandwidth::_125KHz)) | ||
| 279 | || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) | ||
| 280 | { | ||
| 281 | low_data_rate_optimize = 0x01u8; | ||
| 282 | } | ||
| 283 | |||
| 284 | let modulation_params = ModulationParams { | ||
| 285 | spreading_factor: spreading_factor, | ||
| 286 | bandwidth: bandwidth, | ||
| 287 | coding_rate: coding_rate, | ||
| 288 | low_data_rate_optimize: low_data_rate_optimize, | ||
| 289 | }; | ||
| 290 | |||
| 291 | let mut preamble_length_final = preamble_length; | ||
| 292 | if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) | ||
| 293 | && (preamble_length < 12) | ||
| 294 | { | ||
| 295 | preamble_length_final = 12; | ||
| 296 | } | ||
| 297 | |||
| 298 | let packet_params = PacketParams { | ||
| 299 | preamble_length: preamble_length_final, | ||
| 300 | implicit_header: fixed_len, | ||
| 301 | payload_length: self.max_payload_length, | ||
| 302 | crc_on: crc_on, | ||
| 303 | iq_inverted: iq_inverted, | ||
| 304 | }; | ||
| 305 | |||
| 306 | self.modulation_params = Some(modulation_params); | ||
| 307 | self.packet_params = Some(packet_params); | ||
| 308 | |||
| 309 | self.standby().await?; | ||
| 310 | self.sub_set_modulation_params().await?; | ||
| 311 | self.sub_set_packet_params().await?; | ||
| 312 | |||
| 313 | // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) | ||
| 314 | |||
| 315 | let mut tx_modulation = [0x00u8]; | ||
| 316 | self.brd_read_registers(Register::TxModulation, &mut tx_modulation) | ||
| 317 | .await?; | ||
| 318 | if bandwidth == Bandwidth::_500KHz { | ||
| 319 | self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))]) | ||
| 320 | .await?; | ||
| 321 | } else { | ||
| 322 | self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)]) | ||
| 323 | .await?; | ||
| 324 | } | ||
| 325 | |||
| 326 | self.brd_set_rf_tx_power(power).await?; | ||
| 327 | Ok(()) | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported] | ||
| 331 | pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> { | ||
| 332 | Ok(self.brd_check_rf_frequency(frequency).await?) | ||
| 333 | } | ||
| 334 | |||
| 335 | /// 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) | ||
| 336 | /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] | ||
| 337 | /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] | ||
| 338 | /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] | ||
| 339 | /// preamble_length length in symbols (the hardware adds 4 more symbols) | ||
| 340 | /// fixed_len fixed length packets [0: variable, 1: fixed] | ||
| 341 | /// payload_len sets payload length when fixed length is used | ||
| 342 | /// crc_on [0: OFF, 1: ON] | ||
| 343 | pub fn get_time_on_air( | ||
| 344 | &mut self, | ||
| 345 | spreading_factor: SpreadingFactor, | ||
| 346 | bandwidth: Bandwidth, | ||
| 347 | coding_rate: CodingRate, | ||
| 348 | preamble_length: u16, | ||
| 349 | fixed_len: bool, | ||
| 350 | payload_len: u8, | ||
| 351 | crc_on: bool, | ||
| 352 | ) -> Result<u32, RadioError<BUS>> { | ||
| 353 | let numerator = 1000 | ||
| 354 | * Self::get_lora_time_on_air_numerator( | ||
| 355 | spreading_factor, | ||
| 356 | bandwidth, | ||
| 357 | coding_rate, | ||
| 358 | preamble_length, | ||
| 359 | fixed_len, | ||
| 360 | payload_len, | ||
| 361 | crc_on, | ||
| 362 | ); | ||
| 363 | let denominator = bandwidth.value_in_hz(); | ||
| 364 | if denominator == 0 { | ||
| 365 | Err(RadioError::InvalidBandwidth) | ||
| 366 | } else { | ||
| 367 | Ok((numerator + denominator - 1) / denominator) | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms] | ||
| 372 | pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> { | ||
| 373 | if self.packet_params.is_some() { | ||
| 374 | self.sub_set_dio_irq_params( | ||
| 375 | IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), | ||
| 376 | IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), | ||
| 377 | IrqMask::None.value(), | ||
| 378 | IrqMask::None.value(), | ||
| 379 | ) | ||
| 380 | .await?; | ||
| 381 | |||
| 382 | let mut packet_params = self.packet_params.as_mut().unwrap(); | ||
| 383 | packet_params.payload_length = buffer.len() as u8; | ||
| 384 | self.sub_set_packet_params().await?; | ||
| 385 | self.sub_send_payload(buffer, timeout).await?; | ||
| 386 | Ok(()) | ||
| 387 | } else { | ||
| 388 | Err(RadioError::PacketParamsMissing) | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 392 | /// Set the radio in sleep mode | ||
| 393 | pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 394 | self.sub_set_sleep(SleepParams { | ||
| 395 | wakeup_rtc: false, | ||
| 396 | reset: false, | ||
| 397 | warm_start: true, | ||
| 398 | }) | ||
| 399 | .await?; | ||
| 400 | Timer::after(Duration::from_millis(2)).await; | ||
| 401 | Ok(()) | ||
| 402 | } | ||
| 403 | |||
| 404 | /// Set the radio in standby mode | ||
| 405 | pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 406 | self.sub_set_standby(StandbyMode::RC).await?; | ||
| 407 | Ok(()) | ||
| 408 | } | ||
| 409 | |||
| 410 | /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)] | ||
| 411 | pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||
| 412 | self.sub_set_dio_irq_params( | ||
| 413 | IrqMask::All.value(), | ||
| 414 | IrqMask::All.value(), | ||
| 415 | IrqMask::None.value(), | ||
| 416 | IrqMask::None.value(), | ||
| 417 | ) | ||
| 418 | .await?; | ||
| 419 | |||
| 420 | if self.rx_continuous { | ||
| 421 | self.sub_set_rx(0xFFFFFF).await?; | ||
| 422 | } else { | ||
| 423 | self.sub_set_rx(timeout << 6).await?; | ||
| 424 | } | ||
| 425 | |||
| 426 | Ok(()) | ||
| 427 | } | ||
| 428 | |||
| 429 | /// Start a Channel Activity Detection | ||
| 430 | pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 431 | self.sub_set_dio_irq_params( | ||
| 432 | IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), | ||
| 433 | IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), | ||
| 434 | IrqMask::None.value(), | ||
| 435 | IrqMask::None.value(), | ||
| 436 | ) | ||
| 437 | .await?; | ||
| 438 | self.sub_set_cad().await?; | ||
| 439 | Ok(()) | ||
| 440 | } | ||
| 441 | |||
| 442 | /// Sets the radio in continuous wave transmission mode | ||
| 443 | /// frequency channel RF frequency | ||
| 444 | /// power output power [dBm] | ||
| 445 | /// timeout transmission mode timeout [s] | ||
| 446 | pub async fn set_tx_continuous_wave( | ||
| 447 | &mut self, | ||
| 448 | frequency: u32, | ||
| 449 | power: i8, | ||
| 450 | _timeout: u16, | ||
| 451 | ) -> Result<(), RadioError<BUS>> { | ||
| 452 | self.sub_set_rf_frequency(frequency).await?; | ||
| 453 | self.brd_set_rf_tx_power(power).await?; | ||
| 454 | self.sub_set_tx_continuous_wave().await?; | ||
| 455 | |||
| 456 | Ok(()) | ||
| 457 | } | ||
| 458 | |||
| 459 | /// Read the current RSSI value for the LoRa modem (only) [dBm] | ||
| 460 | pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> { | ||
| 461 | let value = self.sub_get_rssi_inst().await?; | ||
| 462 | Ok(value as i16) | ||
| 463 | } | ||
| 464 | |||
| 465 | /// Write one or more radio registers with a buffer of a given size, starting at the first register address | ||
| 466 | pub async fn write_registers_from_buffer( | ||
| 467 | &mut self, | ||
| 468 | start_register: Register, | ||
| 469 | buffer: &[u8], | ||
| 470 | ) -> Result<(), RadioError<BUS>> { | ||
| 471 | self.brd_write_registers(start_register, buffer).await?; | ||
| 472 | Ok(()) | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Read one or more radio registers into a buffer of a given size, starting at the first register address | ||
| 476 | pub async fn read_registers_into_buffer( | ||
| 477 | &mut self, | ||
| 478 | start_register: Register, | ||
| 479 | buffer: &mut [u8], | ||
| 480 | ) -> Result<(), RadioError<BUS>> { | ||
| 481 | self.brd_read_registers(start_register, buffer).await?; | ||
| 482 | Ok(()) | ||
| 483 | } | ||
| 484 | |||
| 485 | /// Set the maximum payload length (in bytes) for a LoRa modem (only). | ||
| 486 | pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> { | ||
| 487 | if self.packet_params.is_some() { | ||
| 488 | let packet_params = self.packet_params.as_mut().unwrap(); | ||
| 489 | self.max_payload_length = max; | ||
| 490 | packet_params.payload_length = max; | ||
| 491 | self.sub_set_packet_params().await?; | ||
| 492 | Ok(()) | ||
| 493 | } else { | ||
| 494 | Err(RadioError::PacketParamsMissing) | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | /// Get the time required for the board plus radio to get out of sleep [ms] | ||
| 499 | pub fn get_wakeup_time(&mut self) -> u32 { | ||
| 500 | self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME | ||
| 501 | } | ||
| 502 | |||
| 503 | /// Process the radio irq | ||
| 504 | pub async fn process_irq( | ||
| 505 | &mut self, | ||
| 506 | receiving_buffer: Option<&mut [u8]>, | ||
| 507 | received_len: Option<&mut u8>, | ||
| 508 | cad_activity_detected: Option<&mut bool>, | ||
| 509 | ) -> Result<(), RadioError<BUS>> { | ||
| 510 | loop { | ||
| 511 | trace!("process_irq loop entered"); | ||
| 512 | |||
| 513 | let de = self.sub_get_device_errors().await?; | ||
| 514 | trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", | ||
| 515 | 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); | ||
| 516 | let st = self.sub_get_status().await?; | ||
| 517 | trace!( | ||
| 518 | "radio status: cmd_status: {:x}, chip_mode: {:x}", | ||
| 519 | st.cmd_status, | ||
| 520 | st.chip_mode | ||
| 521 | ); | ||
| 522 | |||
| 523 | self.dio1.wait_for_high().await.map_err(|_| DIO1)?; | ||
| 524 | let operating_mode = self.brd_get_operating_mode(); | ||
| 525 | let irq_flags = self.sub_get_irq_status().await?; | ||
| 526 | self.sub_clear_irq_status(irq_flags).await?; | ||
| 527 | trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); | ||
| 528 | |||
| 529 | // check for errors and unexpected interrupt masks (based on operation mode) | ||
| 530 | if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { | ||
| 531 | if !self.rx_continuous { | ||
| 532 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 533 | } | ||
| 534 | return Err(RadioError::HeaderError); | ||
| 535 | } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() { | ||
| 536 | if operating_mode == RadioMode::Receive { | ||
| 537 | if !self.rx_continuous { | ||
| 538 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 539 | } | ||
| 540 | return Err(RadioError::CRCErrorOnReceive); | ||
| 541 | } else { | ||
| 542 | return Err(RadioError::CRCErrorUnexpected); | ||
| 543 | } | ||
| 544 | } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() { | ||
| 545 | if operating_mode == RadioMode::Transmit { | ||
| 546 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 547 | return Err(RadioError::TransmitTimeout); | ||
| 548 | } else if operating_mode == RadioMode::Receive { | ||
| 549 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 550 | return Err(RadioError::ReceiveTimeout); | ||
| 551 | } else { | ||
| 552 | return Err(RadioError::TimeoutUnexpected); | ||
| 553 | } | ||
| 554 | } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value()) | ||
| 555 | && (operating_mode != RadioMode::Transmit) | ||
| 556 | { | ||
| 557 | return Err(RadioError::TransmitDoneUnexpected); | ||
| 558 | } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value()) | ||
| 559 | && (operating_mode != RadioMode::Receive) | ||
| 560 | { | ||
| 561 | return Err(RadioError::ReceiveDoneUnexpected); | ||
| 562 | } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value()) | ||
| 563 | || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value())) | ||
| 564 | && (operating_mode != RadioMode::ChannelActivityDetection) | ||
| 565 | { | ||
| 566 | return Err(RadioError::CADUnexpected); | ||
| 567 | } | ||
| 568 | |||
| 569 | if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { | ||
| 570 | trace!("HeaderValid"); | ||
| 571 | } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { | ||
| 572 | trace!("PreambleDetected"); | ||
| 573 | } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { | ||
| 574 | trace!("SyncwordValid"); | ||
| 575 | } | ||
| 576 | |||
| 577 | // handle completions | ||
| 578 | if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() { | ||
| 579 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 580 | return Ok(()); | ||
| 581 | } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() { | ||
| 582 | if !self.rx_continuous { | ||
| 583 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 584 | |||
| 585 | // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3) | ||
| 586 | self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?; | ||
| 587 | let mut evt_clr = [0x00u8]; | ||
| 588 | self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?; | ||
| 589 | evt_clr[0] |= 1 << 1; | ||
| 590 | self.brd_write_registers(Register::EvtClr, &evt_clr).await?; | ||
| 591 | } | ||
| 592 | |||
| 593 | if receiving_buffer.is_some() && received_len.is_some() { | ||
| 594 | *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?; | ||
| 595 | } | ||
| 596 | self.packet_status = self.sub_get_packet_status().await?.into(); | ||
| 597 | return Ok(()); | ||
| 598 | } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() { | ||
| 599 | if cad_activity_detected.is_some() { | ||
| 600 | *(cad_activity_detected.unwrap()) = | ||
| 601 | (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value(); | ||
| 602 | } | ||
| 603 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 604 | return Ok(()); | ||
| 605 | } | ||
| 606 | |||
| 607 | // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid | ||
| 608 | // are in that category), loop to wait again | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | // SX126x-specific functions | ||
| 613 | |||
| 614 | /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms] | ||
| 615 | pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||
| 616 | self.sub_set_dio_irq_params( | ||
| 617 | IrqMask::All.value(), | ||
| 618 | IrqMask::All.value(), | ||
| 619 | IrqMask::None.value(), | ||
| 620 | IrqMask::None.value(), | ||
| 621 | ) | ||
| 622 | .await?; | ||
| 623 | |||
| 624 | if self.rx_continuous { | ||
| 625 | self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous | ||
| 626 | } else { | ||
| 627 | self.sub_set_rx_boosted(timeout << 6).await?; | ||
| 628 | } | ||
| 629 | |||
| 630 | Ok(()) | ||
| 631 | } | ||
| 632 | |||
| 633 | /// Set the Rx duty cycle management parameters (SX126x radios only) | ||
| 634 | /// rx_time structure describing reception timeout value | ||
| 635 | /// sleep_time structure describing sleep timeout value | ||
| 636 | pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> { | ||
| 637 | self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?; | ||
| 638 | Ok(()) | ||
| 639 | } | ||
| 640 | |||
| 641 | pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> { | ||
| 642 | self.packet_status | ||
| 643 | } | ||
| 644 | |||
| 645 | // Utilities | ||
| 646 | |||
| 647 | async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> { | ||
| 648 | let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize]; | ||
| 649 | |||
| 650 | // Read the address and registers already added to the list | ||
| 651 | self.brd_read_registers(Register::RetentionList, &mut buffer).await?; | ||
| 652 | |||
| 653 | let number_of_registers = buffer[0]; | ||
| 654 | for i in 0..number_of_registers { | ||
| 655 | if register_address | ||
| 656 | == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16) | ||
| 657 | { | ||
| 658 | return Ok(()); // register already in list | ||
| 659 | } | ||
| 660 | } | ||
| 661 | |||
| 662 | if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION { | ||
| 663 | buffer[0] += 1; // increment number of registers | ||
| 664 | |||
| 665 | buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8; | ||
| 666 | buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8; | ||
| 667 | self.brd_write_registers(Register::RetentionList, &buffer).await?; | ||
| 668 | |||
| 669 | Ok(()) | ||
| 670 | } else { | ||
| 671 | Err(RadioError::RetentionListExceeded) | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | fn get_lora_time_on_air_numerator( | ||
| 676 | spreading_factor: SpreadingFactor, | ||
| 677 | bandwidth: Bandwidth, | ||
| 678 | coding_rate: CodingRate, | ||
| 679 | preamble_length: u16, | ||
| 680 | fixed_len: bool, | ||
| 681 | payload_len: u8, | ||
| 682 | crc_on: bool, | ||
| 683 | ) -> u32 { | ||
| 684 | let cell_denominator; | ||
| 685 | let cr_denominator = (coding_rate.value() as i32) + 4; | ||
| 686 | |||
| 687 | // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 | ||
| 688 | let mut preamble_length_final = preamble_length; | ||
| 689 | if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) | ||
| 690 | && (preamble_length < 12) | ||
| 691 | { | ||
| 692 | preamble_length_final = 12; | ||
| 693 | } | ||
| 694 | |||
| 695 | let mut low_data_rate_optimize = false; | ||
| 696 | if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) | ||
| 697 | && (bandwidth == Bandwidth::_125KHz)) | ||
| 698 | || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) | ||
| 699 | { | ||
| 700 | low_data_rate_optimize = true; | ||
| 701 | } | ||
| 702 | |||
| 703 | let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 }) | ||
| 704 | - (4 * spreading_factor.value() as i32) | ||
| 705 | + (if fixed_len { 0 } else { 20 }); | ||
| 706 | |||
| 707 | if spreading_factor.value() <= 6 { | ||
| 708 | cell_denominator = 4 * (spreading_factor.value() as i32); | ||
| 709 | } else { | ||
| 710 | cell_numerator += 8; | ||
| 711 | if low_data_rate_optimize { | ||
| 712 | cell_denominator = 4 * ((spreading_factor.value() as i32) - 2); | ||
| 713 | } else { | ||
| 714 | cell_denominator = 4 * (spreading_factor.value() as i32); | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | if cell_numerator < 0 { | ||
| 719 | cell_numerator = 0; | ||
| 720 | } | ||
| 721 | |||
| 722 | let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator) | ||
| 723 | + (preamble_length_final as i32) | ||
| 724 | + 12; | ||
| 725 | |||
| 726 | if spreading_factor.value() <= 6 { | ||
| 727 | intermediate = intermediate + 2; | ||
| 728 | } | ||
| 729 | |||
| 730 | (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32 | ||
| 731 | } | ||
| 732 | } | ||
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 e270b2a09..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs +++ /dev/null | |||
| @@ -1,469 +0,0 @@ | |||
| 1 | use core::fmt::Debug; | ||
| 2 | |||
| 3 | use lorawan_device::async_device::radio as device; | ||
| 4 | |||
| 5 | #[allow(clippy::upper_case_acronyms)] | ||
| 6 | #[derive(Debug)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub enum RadioError<BUS> { | ||
| 9 | SPI(BUS), | ||
| 10 | CS, | ||
| 11 | Reset, | ||
| 12 | AntRx, | ||
| 13 | AntTx, | ||
| 14 | Busy, | ||
| 15 | DIO1, | ||
| 16 | PayloadSizeMismatch(usize, usize), | ||
| 17 | RetentionListExceeded, | ||
| 18 | InvalidBandwidth, | ||
| 19 | ModulationParamsMissing, | ||
| 20 | PacketParamsMissing, | ||
| 21 | HeaderError, | ||
| 22 | CRCErrorUnexpected, | ||
| 23 | CRCErrorOnReceive, | ||
| 24 | TransmitTimeout, | ||
| 25 | ReceiveTimeout, | ||
| 26 | TimeoutUnexpected, | ||
| 27 | TransmitDoneUnexpected, | ||
| 28 | ReceiveDoneUnexpected, | ||
| 29 | CADUnexpected, | ||
| 30 | } | ||
| 31 | |||
| 32 | pub struct RadioSystemError { | ||
| 33 | pub rc_64khz_calibration: bool, | ||
| 34 | pub rc_13mhz_calibration: bool, | ||
| 35 | pub pll_calibration: bool, | ||
| 36 | pub adc_calibration: bool, | ||
| 37 | pub image_calibration: bool, | ||
| 38 | pub xosc_start: bool, | ||
| 39 | pub pll_lock: bool, | ||
| 40 | pub pa_ramp: bool, | ||
| 41 | } | ||
| 42 | |||
| 43 | #[derive(Clone, Copy, PartialEq)] | ||
| 44 | pub enum PacketType { | ||
| 45 | GFSK = 0x00, | ||
| 46 | LoRa = 0x01, | ||
| 47 | None = 0x0F, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl PacketType { | ||
| 51 | pub const fn value(self) -> u8 { | ||
| 52 | self as u8 | ||
| 53 | } | ||
| 54 | pub fn to_enum(value: u8) -> Self { | ||
| 55 | if value == 0x00 { | ||
| 56 | PacketType::GFSK | ||
| 57 | } else if value == 0x01 { | ||
| 58 | PacketType::LoRa | ||
| 59 | } else { | ||
| 60 | PacketType::None | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | #[derive(Clone, Copy)] | ||
| 66 | pub struct PacketStatus { | ||
| 67 | pub rssi: i8, | ||
| 68 | pub snr: i8, | ||
| 69 | pub signal_rssi: i8, | ||
| 70 | pub freq_error: u32, | ||
| 71 | } | ||
| 72 | |||
| 73 | #[derive(Clone, Copy, PartialEq)] | ||
| 74 | pub enum RadioType { | ||
| 75 | SX1261, | ||
| 76 | SX1262, | ||
| 77 | } | ||
| 78 | |||
| 79 | #[derive(Clone, Copy, PartialEq)] | ||
| 80 | pub enum RadioMode { | ||
| 81 | Sleep = 0x00, // sleep mode | ||
| 82 | StandbyRC = 0x01, // standby mode with RC oscillator | ||
| 83 | StandbyXOSC = 0x02, // standby mode with XOSC oscillator | ||
| 84 | FrequencySynthesis = 0x03, // frequency synthesis mode | ||
| 85 | Transmit = 0x04, // transmit mode | ||
| 86 | Receive = 0x05, // receive mode | ||
| 87 | ReceiveDutyCycle = 0x06, // receive duty cycle mode | ||
| 88 | ChannelActivityDetection = 0x07, // channel activity detection mode | ||
| 89 | } | ||
| 90 | |||
| 91 | impl RadioMode { | ||
| 92 | /// Returns the value of the mode. | ||
| 93 | pub const fn value(self) -> u8 { | ||
| 94 | self as u8 | ||
| 95 | } | ||
| 96 | pub fn to_enum(value: u8) -> Self { | ||
| 97 | if value == 0x00 { | ||
| 98 | RadioMode::Sleep | ||
| 99 | } else if value == 0x01 { | ||
| 100 | RadioMode::StandbyRC | ||
| 101 | } else if value == 0x02 { | ||
| 102 | RadioMode::StandbyXOSC | ||
| 103 | } else if value == 0x03 { | ||
| 104 | RadioMode::FrequencySynthesis | ||
| 105 | } else if value == 0x04 { | ||
| 106 | RadioMode::Transmit | ||
| 107 | } else if value == 0x05 { | ||
| 108 | RadioMode::Receive | ||
| 109 | } else if value == 0x06 { | ||
| 110 | RadioMode::ReceiveDutyCycle | ||
| 111 | } else if value == 0x07 { | ||
| 112 | RadioMode::ChannelActivityDetection | ||
| 113 | } else { | ||
| 114 | RadioMode::Sleep | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | pub enum RadioState { | ||
| 120 | Idle = 0x00, | ||
| 121 | RxRunning = 0x01, | ||
| 122 | TxRunning = 0x02, | ||
| 123 | ChannelActivityDetecting = 0x03, | ||
| 124 | } | ||
| 125 | |||
| 126 | impl RadioState { | ||
| 127 | /// Returns the value of the state. | ||
| 128 | pub fn value(self) -> u8 { | ||
| 129 | self as u8 | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | pub struct RadioStatus { | ||
| 134 | pub cmd_status: u8, | ||
| 135 | pub chip_mode: u8, | ||
| 136 | } | ||
| 137 | |||
| 138 | impl RadioStatus { | ||
| 139 | pub fn value(self) -> u8 { | ||
| 140 | (self.chip_mode << 4) | (self.cmd_status << 1) | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | #[derive(Clone, Copy)] | ||
| 145 | pub enum IrqMask { | ||
| 146 | None = 0x0000, | ||
| 147 | TxDone = 0x0001, | ||
| 148 | RxDone = 0x0002, | ||
| 149 | PreambleDetected = 0x0004, | ||
| 150 | SyncwordValid = 0x0008, | ||
| 151 | HeaderValid = 0x0010, | ||
| 152 | HeaderError = 0x0020, | ||
| 153 | CRCError = 0x0040, | ||
| 154 | CADDone = 0x0080, | ||
| 155 | CADActivityDetected = 0x0100, | ||
| 156 | RxTxTimeout = 0x0200, | ||
| 157 | All = 0xFFFF, | ||
| 158 | } | ||
| 159 | |||
| 160 | impl IrqMask { | ||
| 161 | pub fn value(self) -> u16 { | ||
| 162 | self as u16 | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | #[derive(Clone, Copy)] | ||
| 167 | pub enum Register { | ||
| 168 | PacketParams = 0x0704, // packet configuration | ||
| 169 | PayloadLength = 0x0702, // payload size | ||
| 170 | SynchTimeout = 0x0706, // recalculated number of symbols | ||
| 171 | Syncword = 0x06C0, // Syncword values | ||
| 172 | LoRaSyncword = 0x0740, // LoRa Syncword value | ||
| 173 | GeneratedRandomNumber = 0x0819, //32-bit generated random number | ||
| 174 | AnaLNA = 0x08E2, // disable the LNA | ||
| 175 | AnaMixer = 0x08E5, // disable the mixer | ||
| 176 | RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted) | ||
| 177 | XTATrim = 0x0911, // device internal trimming capacitor | ||
| 178 | OCP = 0x08E7, // over current protection max value | ||
| 179 | RetentionList = 0x029F, // retention list | ||
| 180 | IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) | ||
| 181 | TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) | ||
| 182 | TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) | ||
| 183 | RTCCtrl = 0x0902, // RTC control | ||
| 184 | EvtClr = 0x0944, // event clear | ||
| 185 | } | ||
| 186 | |||
| 187 | impl Register { | ||
| 188 | pub fn addr(self) -> u16 { | ||
| 189 | self as u16 | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | #[derive(Clone, Copy, PartialEq)] | ||
| 194 | pub enum OpCode { | ||
| 195 | GetStatus = 0xC0, | ||
| 196 | WriteRegister = 0x0D, | ||
| 197 | ReadRegister = 0x1D, | ||
| 198 | WriteBuffer = 0x0E, | ||
| 199 | ReadBuffer = 0x1E, | ||
| 200 | SetSleep = 0x84, | ||
| 201 | SetStandby = 0x80, | ||
| 202 | SetFS = 0xC1, | ||
| 203 | SetTx = 0x83, | ||
| 204 | SetRx = 0x82, | ||
| 205 | SetRxDutyCycle = 0x94, | ||
| 206 | SetCAD = 0xC5, | ||
| 207 | SetTxContinuousWave = 0xD1, | ||
| 208 | SetTxContinuousPremable = 0xD2, | ||
| 209 | SetPacketType = 0x8A, | ||
| 210 | GetPacketType = 0x11, | ||
| 211 | SetRFFrequency = 0x86, | ||
| 212 | SetTxParams = 0x8E, | ||
| 213 | SetPAConfig = 0x95, | ||
| 214 | SetCADParams = 0x88, | ||
| 215 | SetBufferBaseAddress = 0x8F, | ||
| 216 | SetModulationParams = 0x8B, | ||
| 217 | SetPacketParams = 0x8C, | ||
| 218 | GetRxBufferStatus = 0x13, | ||
| 219 | GetPacketStatus = 0x14, | ||
| 220 | GetRSSIInst = 0x15, | ||
| 221 | GetStats = 0x10, | ||
| 222 | ResetStats = 0x00, | ||
| 223 | CfgDIOIrq = 0x08, | ||
| 224 | GetIrqStatus = 0x12, | ||
| 225 | ClrIrqStatus = 0x02, | ||
| 226 | Calibrate = 0x89, | ||
| 227 | CalibrateImage = 0x98, | ||
| 228 | SetRegulatorMode = 0x96, | ||
| 229 | GetErrors = 0x17, | ||
| 230 | ClrErrors = 0x07, | ||
| 231 | SetTCXOMode = 0x97, | ||
| 232 | SetTxFallbackMode = 0x93, | ||
| 233 | SetRFSwitchMode = 0x9D, | ||
| 234 | SetStopRxTimerOnPreamble = 0x9F, | ||
| 235 | SetLoRaSymbTimeout = 0xA0, | ||
| 236 | } | ||
| 237 | |||
| 238 | impl OpCode { | ||
| 239 | pub fn value(self) -> u8 { | ||
| 240 | self as u8 | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | pub struct SleepParams { | ||
| 245 | pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC | ||
| 246 | pub reset: bool, | ||
| 247 | pub warm_start: bool, | ||
| 248 | } | ||
| 249 | |||
| 250 | impl SleepParams { | ||
| 251 | pub fn value(self) -> u8 { | ||
| 252 | ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8) | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | #[derive(Clone, Copy, PartialEq)] | ||
| 257 | pub enum StandbyMode { | ||
| 258 | RC = 0x00, | ||
| 259 | XOSC = 0x01, | ||
| 260 | } | ||
| 261 | |||
| 262 | impl StandbyMode { | ||
| 263 | pub fn value(self) -> u8 { | ||
| 264 | self as u8 | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | #[derive(Clone, Copy)] | ||
| 269 | pub enum RegulatorMode { | ||
| 270 | UseLDO = 0x00, | ||
| 271 | UseDCDC = 0x01, | ||
| 272 | } | ||
| 273 | |||
| 274 | impl RegulatorMode { | ||
| 275 | pub fn value(self) -> u8 { | ||
| 276 | self as u8 | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | #[derive(Clone, Copy)] | ||
| 281 | pub struct CalibrationParams { | ||
| 282 | pub rc64k_enable: bool, // calibrate RC64K clock | ||
| 283 | pub rc13m_enable: bool, // calibrate RC13M clock | ||
| 284 | pub pll_enable: bool, // calibrate PLL | ||
| 285 | pub adc_pulse_enable: bool, // calibrate ADC Pulse | ||
| 286 | pub adc_bulkn_enable: bool, // calibrate ADC bulkN | ||
| 287 | pub adc_bulkp_enable: bool, // calibrate ADC bulkP | ||
| 288 | pub img_enable: bool, | ||
| 289 | } | ||
| 290 | |||
| 291 | impl CalibrationParams { | ||
| 292 | pub fn value(self) -> u8 { | ||
| 293 | ((self.img_enable as u8) << 6) | ||
| 294 | | ((self.adc_bulkp_enable as u8) << 5) | ||
| 295 | | ((self.adc_bulkn_enable as u8) << 4) | ||
| 296 | | ((self.adc_pulse_enable as u8) << 3) | ||
| 297 | | ((self.pll_enable as u8) << 2) | ||
| 298 | | ((self.rc13m_enable as u8) << 1) | ||
| 299 | | ((self.rc64k_enable as u8) << 0) | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | #[derive(Clone, Copy)] | ||
| 304 | pub enum TcxoCtrlVoltage { | ||
| 305 | Ctrl1V6 = 0x00, | ||
| 306 | Ctrl1V7 = 0x01, | ||
| 307 | Ctrl1V8 = 0x02, | ||
| 308 | Ctrl2V2 = 0x03, | ||
| 309 | Ctrl2V4 = 0x04, | ||
| 310 | Ctrl2V7 = 0x05, | ||
| 311 | Ctrl3V0 = 0x06, | ||
| 312 | Ctrl3V3 = 0x07, | ||
| 313 | } | ||
| 314 | |||
| 315 | impl TcxoCtrlVoltage { | ||
| 316 | pub fn value(self) -> u8 { | ||
| 317 | self as u8 | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | #[derive(Clone, Copy)] | ||
| 322 | pub enum RampTime { | ||
| 323 | Ramp10Us = 0x00, | ||
| 324 | Ramp20Us = 0x01, | ||
| 325 | Ramp40Us = 0x02, | ||
| 326 | Ramp80Us = 0x03, | ||
| 327 | Ramp200Us = 0x04, | ||
| 328 | Ramp800Us = 0x05, | ||
| 329 | Ramp1700Us = 0x06, | ||
| 330 | Ramp3400Us = 0x07, | ||
| 331 | } | ||
| 332 | |||
| 333 | impl RampTime { | ||
| 334 | pub fn value(self) -> u8 { | ||
| 335 | self as u8 | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | #[derive(Clone, Copy, PartialEq)] | ||
| 340 | pub enum SpreadingFactor { | ||
| 341 | _5 = 0x05, | ||
| 342 | _6 = 0x06, | ||
| 343 | _7 = 0x07, | ||
| 344 | _8 = 0x08, | ||
| 345 | _9 = 0x09, | ||
| 346 | _10 = 0x0A, | ||
| 347 | _11 = 0x0B, | ||
| 348 | _12 = 0x0C, | ||
| 349 | } | ||
| 350 | |||
| 351 | impl SpreadingFactor { | ||
| 352 | pub fn value(self) -> u8 { | ||
| 353 | self as u8 | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | impl From<device::SpreadingFactor> for SpreadingFactor { | ||
| 358 | fn from(sf: device::SpreadingFactor) -> Self { | ||
| 359 | match sf { | ||
| 360 | device::SpreadingFactor::_7 => SpreadingFactor::_7, | ||
| 361 | device::SpreadingFactor::_8 => SpreadingFactor::_8, | ||
| 362 | device::SpreadingFactor::_9 => SpreadingFactor::_9, | ||
| 363 | device::SpreadingFactor::_10 => SpreadingFactor::_10, | ||
| 364 | device::SpreadingFactor::_11 => SpreadingFactor::_11, | ||
| 365 | device::SpreadingFactor::_12 => SpreadingFactor::_12, | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | #[derive(Clone, Copy, PartialEq)] | ||
| 371 | pub enum Bandwidth { | ||
| 372 | _500KHz = 0x06, | ||
| 373 | _250KHz = 0x05, | ||
| 374 | _125KHz = 0x04, | ||
| 375 | } | ||
| 376 | |||
| 377 | impl Bandwidth { | ||
| 378 | pub fn value(self) -> u8 { | ||
| 379 | self as u8 | ||
| 380 | } | ||
| 381 | |||
| 382 | pub fn value_in_hz(self) -> u32 { | ||
| 383 | match self { | ||
| 384 | Bandwidth::_125KHz => 125000u32, | ||
| 385 | Bandwidth::_250KHz => 250000u32, | ||
| 386 | Bandwidth::_500KHz => 500000u32, | ||
| 387 | } | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | impl From<device::Bandwidth> for Bandwidth { | ||
| 392 | fn from(bw: device::Bandwidth) -> Self { | ||
| 393 | match bw { | ||
| 394 | device::Bandwidth::_500KHz => Bandwidth::_500KHz, | ||
| 395 | device::Bandwidth::_250KHz => Bandwidth::_250KHz, | ||
| 396 | device::Bandwidth::_125KHz => Bandwidth::_125KHz, | ||
| 397 | } | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | #[derive(Clone, Copy)] | ||
| 402 | pub enum CodingRate { | ||
| 403 | _4_5 = 0x01, | ||
| 404 | _4_6 = 0x02, | ||
| 405 | _4_7 = 0x03, | ||
| 406 | _4_8 = 0x04, | ||
| 407 | } | ||
| 408 | |||
| 409 | impl CodingRate { | ||
| 410 | pub fn value(self) -> u8 { | ||
| 411 | self as u8 | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | impl From<device::CodingRate> for CodingRate { | ||
| 416 | fn from(cr: device::CodingRate) -> Self { | ||
| 417 | match cr { | ||
| 418 | device::CodingRate::_4_5 => CodingRate::_4_5, | ||
| 419 | device::CodingRate::_4_6 => CodingRate::_4_6, | ||
| 420 | device::CodingRate::_4_7 => CodingRate::_4_7, | ||
| 421 | device::CodingRate::_4_8 => CodingRate::_4_8, | ||
| 422 | } | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | #[derive(Clone, Copy)] | ||
| 427 | pub struct ModulationParams { | ||
| 428 | pub spreading_factor: SpreadingFactor, | ||
| 429 | pub bandwidth: Bandwidth, | ||
| 430 | pub coding_rate: CodingRate, | ||
| 431 | pub low_data_rate_optimize: u8, | ||
| 432 | } | ||
| 433 | |||
| 434 | #[derive(Clone, Copy)] | ||
| 435 | pub struct PacketParams { | ||
| 436 | pub preamble_length: u16, // number of LoRa symbols in the preamble | ||
| 437 | 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) | ||
| 438 | pub payload_length: u8, | ||
| 439 | pub crc_on: bool, | ||
| 440 | pub iq_inverted: bool, | ||
| 441 | } | ||
| 442 | |||
| 443 | #[derive(Clone, Copy)] | ||
| 444 | pub enum CADSymbols { | ||
| 445 | _1 = 0x00, | ||
| 446 | _2 = 0x01, | ||
| 447 | _4 = 0x02, | ||
| 448 | _8 = 0x03, | ||
| 449 | _16 = 0x04, | ||
| 450 | } | ||
| 451 | |||
| 452 | impl CADSymbols { | ||
| 453 | pub fn value(self) -> u8 { | ||
| 454 | self as u8 | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | #[derive(Clone, Copy)] | ||
| 459 | pub enum CADExitMode { | ||
| 460 | CADOnly = 0x00, | ||
| 461 | CADRx = 0x01, | ||
| 462 | CADLBT = 0x10, | ||
| 463 | } | ||
| 464 | |||
| 465 | impl CADExitMode { | ||
| 466 | pub fn value(self) -> u8 { | ||
| 467 | self as u8 | ||
| 468 | } | ||
| 469 | } | ||
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 2e78b919b..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ /dev/null | |||
| @@ -1,674 +0,0 @@ | |||
| 1 | use embedded_hal::digital::v2::OutputPin; | ||
| 2 | use embedded_hal_async::digital::Wait; | ||
| 3 | use embedded_hal_async::spi::SpiBus; | ||
| 4 | |||
| 5 | use super::mod_params::*; | ||
| 6 | use super::LoRa; | ||
| 7 | |||
| 8 | // Internal frequency of the radio | ||
| 9 | const SX126X_XTAL_FREQ: u32 = 32000000; | ||
| 10 | |||
| 11 | // Scaling factor used to perform fixed-point operations | ||
| 12 | const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14; | ||
| 13 | |||
| 14 | // PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT | ||
| 15 | const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT); | ||
| 16 | |||
| 17 | // Maximum value for parameter symbNum | ||
| 18 | const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; | ||
| 19 | |||
| 20 | // Provides board-specific functionality for Semtech SX126x-based boards | ||
| 21 | |||
| 22 | impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT> | ||
| 23 | where | ||
| 24 | SPI: SpiBus<u8, Error = BUS>, | ||
| 25 | CTRL: OutputPin, | ||
| 26 | WAIT: Wait, | ||
| 27 | { | ||
| 28 | // Initialize the radio driver | ||
| 29 | pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 30 | self.brd_reset().await?; | ||
| 31 | self.brd_wakeup().await?; | ||
| 32 | self.sub_set_standby(StandbyMode::RC).await?; | ||
| 33 | self.brd_io_tcxo_init().await?; | ||
| 34 | self.brd_io_rf_switch_init().await?; | ||
| 35 | self.image_calibrated = false; | ||
| 36 | Ok(()) | ||
| 37 | } | ||
| 38 | |||
| 39 | // Wakeup the radio if it is in Sleep mode and check that Busy is low | ||
| 40 | pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 41 | let operating_mode = self.brd_get_operating_mode(); | ||
| 42 | if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { | ||
| 43 | self.brd_wakeup().await?; | ||
| 44 | } | ||
| 45 | self.brd_wait_on_busy().await?; | ||
| 46 | Ok(()) | ||
| 47 | } | ||
| 48 | |||
| 49 | // Save the payload to be sent in the radio buffer | ||
| 50 | pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> { | ||
| 51 | self.brd_write_buffer(0x00, payload).await?; | ||
| 52 | Ok(()) | ||
| 53 | } | ||
| 54 | |||
| 55 | // Read the payload received. | ||
| 56 | pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> { | ||
| 57 | let (size, offset) = self.sub_get_rx_buffer_status().await?; | ||
| 58 | if (size as usize) > buffer.len() { | ||
| 59 | Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len())) | ||
| 60 | } else { | ||
| 61 | self.brd_read_buffer(offset, buffer).await?; | ||
| 62 | Ok(size) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | // Send a payload | ||
| 67 | pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> { | ||
| 68 | self.sub_set_payload(payload).await?; | ||
| 69 | self.sub_set_tx(timeout).await?; | ||
| 70 | Ok(()) | ||
| 71 | } | ||
| 72 | |||
| 73 | // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command. | ||
| 74 | // | ||
| 75 | // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of | ||
| 76 | // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts | ||
| 77 | // generated during this process will not cause undesired side-effects in the software. | ||
| 78 | // | ||
| 79 | // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing. | ||
| 80 | pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> { | ||
| 81 | let mut reg_ana_lna_buffer_original = [0x00u8]; | ||
| 82 | let mut reg_ana_mixer_buffer_original = [0x00u8]; | ||
| 83 | let mut reg_ana_lna_buffer = [0x00u8]; | ||
| 84 | let mut reg_ana_mixer_buffer = [0x00u8]; | ||
| 85 | let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8]; | ||
| 86 | |||
| 87 | self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original) | ||
| 88 | .await?; | ||
| 89 | reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0)); | ||
| 90 | self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?; | ||
| 91 | |||
| 92 | self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original) | ||
| 93 | .await?; | ||
| 94 | reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7)); | ||
| 95 | self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer) | ||
| 96 | .await?; | ||
| 97 | |||
| 98 | // Set radio in continuous reception | ||
| 99 | self.sub_set_rx(0xFFFFFFu32).await?; | ||
| 100 | |||
| 101 | self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer) | ||
| 102 | .await?; | ||
| 103 | |||
| 104 | self.sub_set_standby(StandbyMode::RC).await?; | ||
| 105 | |||
| 106 | self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original) | ||
| 107 | .await?; | ||
| 108 | self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original) | ||
| 109 | .await?; | ||
| 110 | |||
| 111 | Ok(Self::convert_u8_buffer_to_u32(&number_buffer)) | ||
| 112 | } | ||
| 113 | |||
| 114 | // Set the radio in sleep mode | ||
| 115 | pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> { | ||
| 116 | self.brd_ant_sleep()?; | ||
| 117 | |||
| 118 | if !sleep_config.warm_start { | ||
| 119 | self.image_calibrated = false; | ||
| 120 | } | ||
| 121 | |||
| 122 | self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()]) | ||
| 123 | .await?; | ||
| 124 | self.brd_set_operating_mode(RadioMode::Sleep); | ||
| 125 | Ok(()) | ||
| 126 | } | ||
| 127 | |||
| 128 | // Set the radio in configuration mode | ||
| 129 | pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> { | ||
| 130 | self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?; | ||
| 131 | if mode == StandbyMode::RC { | ||
| 132 | self.brd_set_operating_mode(RadioMode::StandbyRC); | ||
| 133 | } else { | ||
| 134 | self.brd_set_operating_mode(RadioMode::StandbyXOSC); | ||
| 135 | } | ||
| 136 | |||
| 137 | self.brd_ant_sleep()?; | ||
| 138 | Ok(()) | ||
| 139 | } | ||
| 140 | |||
| 141 | // Set the radio in FS mode | ||
| 142 | pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 143 | // antenna settings ??? | ||
| 144 | self.brd_write_command(OpCode::SetFS, &[]).await?; | ||
| 145 | self.brd_set_operating_mode(RadioMode::FrequencySynthesis); | ||
| 146 | Ok(()) | ||
| 147 | } | ||
| 148 | |||
| 149 | // Set the radio in transmission mode with timeout specified | ||
| 150 | pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||
| 151 | let buffer = [ | ||
| 152 | Self::timeout_1(timeout), | ||
| 153 | Self::timeout_2(timeout), | ||
| 154 | Self::timeout_3(timeout), | ||
| 155 | ]; | ||
| 156 | |||
| 157 | self.brd_ant_set_tx()?; | ||
| 158 | |||
| 159 | self.brd_set_operating_mode(RadioMode::Transmit); | ||
| 160 | self.brd_write_command(OpCode::SetTx, &buffer).await?; | ||
| 161 | Ok(()) | ||
| 162 | } | ||
| 163 | |||
| 164 | // Set the radio in reception mode with timeout specified | ||
| 165 | pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||
| 166 | let buffer = [ | ||
| 167 | Self::timeout_1(timeout), | ||
| 168 | Self::timeout_2(timeout), | ||
| 169 | Self::timeout_3(timeout), | ||
| 170 | ]; | ||
| 171 | |||
| 172 | self.brd_ant_set_rx()?; | ||
| 173 | |||
| 174 | self.brd_set_operating_mode(RadioMode::Receive); | ||
| 175 | self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; | ||
| 176 | self.brd_write_command(OpCode::SetRx, &buffer).await?; | ||
| 177 | Ok(()) | ||
| 178 | } | ||
| 179 | |||
| 180 | // Set the radio in reception mode with Boosted LNA gain and timeout specified | ||
| 181 | pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||
| 182 | let buffer = [ | ||
| 183 | Self::timeout_1(timeout), | ||
| 184 | Self::timeout_2(timeout), | ||
| 185 | Self::timeout_3(timeout), | ||
| 186 | ]; | ||
| 187 | |||
| 188 | self.brd_ant_set_rx()?; | ||
| 189 | |||
| 190 | self.brd_set_operating_mode(RadioMode::Receive); | ||
| 191 | // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity | ||
| 192 | self.brd_write_registers(Register::RxGain, &[0x96u8]).await?; | ||
| 193 | self.brd_write_command(OpCode::SetRx, &buffer).await?; | ||
| 194 | Ok(()) | ||
| 195 | } | ||
| 196 | |||
| 197 | // Set the Rx duty cycle management parameters | ||
| 198 | pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> { | ||
| 199 | let buffer = [ | ||
| 200 | ((rx_time >> 16) & 0xFF) as u8, | ||
| 201 | ((rx_time >> 8) & 0xFF) as u8, | ||
| 202 | (rx_time & 0xFF) as u8, | ||
| 203 | ((sleep_time >> 16) & 0xFF) as u8, | ||
| 204 | ((sleep_time >> 8) & 0xFF) as u8, | ||
| 205 | (sleep_time & 0xFF) as u8, | ||
| 206 | ]; | ||
| 207 | |||
| 208 | // antenna settings ??? | ||
| 209 | |||
| 210 | self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?; | ||
| 211 | self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle); | ||
| 212 | Ok(()) | ||
| 213 | } | ||
| 214 | |||
| 215 | // Set the radio in CAD mode | ||
| 216 | pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 217 | self.brd_ant_set_rx()?; | ||
| 218 | |||
| 219 | self.brd_write_command(OpCode::SetCAD, &[]).await?; | ||
| 220 | self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); | ||
| 221 | Ok(()) | ||
| 222 | } | ||
| 223 | |||
| 224 | // Set the radio in continuous wave transmission mode | ||
| 225 | pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 226 | self.brd_ant_set_tx()?; | ||
| 227 | |||
| 228 | self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; | ||
| 229 | self.brd_set_operating_mode(RadioMode::Transmit); | ||
| 230 | Ok(()) | ||
| 231 | } | ||
| 232 | |||
| 233 | // Set the radio in continuous preamble transmission mode | ||
| 234 | pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 235 | self.brd_ant_set_tx()?; | ||
| 236 | |||
| 237 | self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; | ||
| 238 | self.brd_set_operating_mode(RadioMode::Transmit); | ||
| 239 | Ok(()) | ||
| 240 | } | ||
| 241 | |||
| 242 | // Decide which interrupt will stop the internal radio rx timer. | ||
| 243 | // false timer stop after header/syncword detection | ||
| 244 | // true timer stop after preamble detection | ||
| 245 | pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect( | ||
| 246 | &mut self, | ||
| 247 | enable: bool, | ||
| 248 | ) -> Result<(), RadioError<BUS>> { | ||
| 249 | self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8]) | ||
| 250 | .await?; | ||
| 251 | Ok(()) | ||
| 252 | } | ||
| 253 | |||
| 254 | // Set the number of symbols the radio will wait to validate a reception | ||
| 255 | pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> { | ||
| 256 | let mut exp = 0u8; | ||
| 257 | let mut reg; | ||
| 258 | let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1; | ||
| 259 | while mant > 31 { | ||
| 260 | mant = (mant + 3) >> 2; | ||
| 261 | exp += 1; | ||
| 262 | } | ||
| 263 | reg = mant << ((2 * exp) + 1); | ||
| 264 | |||
| 265 | self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?; | ||
| 266 | |||
| 267 | if symb_num != 0 { | ||
| 268 | reg = exp + (mant << 3); | ||
| 269 | self.brd_write_registers(Register::SynchTimeout, &[reg]).await?; | ||
| 270 | } | ||
| 271 | |||
| 272 | Ok(()) | ||
| 273 | } | ||
| 274 | |||
| 275 | // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled | ||
| 276 | pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> { | ||
| 277 | self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()]) | ||
| 278 | .await?; | ||
| 279 | Ok(()) | ||
| 280 | } | ||
| 281 | |||
| 282 | // Calibrate the given radio block | ||
| 283 | pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> { | ||
| 284 | self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()]) | ||
| 285 | .await?; | ||
| 286 | Ok(()) | ||
| 287 | } | ||
| 288 | |||
| 289 | // Calibrate the image rejection based on the given frequency | ||
| 290 | pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> { | ||
| 291 | let mut cal_freq = [0x00u8, 0x00u8]; | ||
| 292 | |||
| 293 | if freq > 900000000 { | ||
| 294 | cal_freq[0] = 0xE1; | ||
| 295 | cal_freq[1] = 0xE9; | ||
| 296 | } else if freq > 850000000 { | ||
| 297 | cal_freq[0] = 0xD7; | ||
| 298 | cal_freq[1] = 0xDB; | ||
| 299 | } else if freq > 770000000 { | ||
| 300 | cal_freq[0] = 0xC1; | ||
| 301 | cal_freq[1] = 0xC5; | ||
| 302 | } else if freq > 460000000 { | ||
| 303 | cal_freq[0] = 0x75; | ||
| 304 | cal_freq[1] = 0x81; | ||
| 305 | } else if freq > 425000000 { | ||
| 306 | cal_freq[0] = 0x6B; | ||
| 307 | cal_freq[1] = 0x6F; | ||
| 308 | } | ||
| 309 | self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?; | ||
| 310 | Ok(()) | ||
| 311 | } | ||
| 312 | |||
| 313 | // Activate the extention of the timeout when a long preamble is used | ||
| 314 | pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> { | ||
| 315 | Ok(()) // no operation currently | ||
| 316 | } | ||
| 317 | |||
| 318 | // Set the transmission parameters | ||
| 319 | // hp_max 0 for sx1261, 7 for sx1262 | ||
| 320 | // device_sel 1 for sx1261, 0 for sx1262 | ||
| 321 | // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT | ||
| 322 | pub(super) async fn sub_set_pa_config( | ||
| 323 | &mut self, | ||
| 324 | pa_duty_cycle: u8, | ||
| 325 | hp_max: u8, | ||
| 326 | device_sel: u8, | ||
| 327 | pa_lut: u8, | ||
| 328 | ) -> Result<(), RadioError<BUS>> { | ||
| 329 | self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut]) | ||
| 330 | .await?; | ||
| 331 | Ok(()) | ||
| 332 | } | ||
| 333 | |||
| 334 | // Define into which mode the chip goes after a TX / RX done | ||
| 335 | pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> { | ||
| 336 | self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode]) | ||
| 337 | .await?; | ||
| 338 | Ok(()) | ||
| 339 | } | ||
| 340 | |||
| 341 | // Set the IRQ mask and DIO masks | ||
| 342 | pub(super) async fn sub_set_dio_irq_params( | ||
| 343 | &mut self, | ||
| 344 | irq_mask: u16, | ||
| 345 | dio1_mask: u16, | ||
| 346 | dio2_mask: u16, | ||
| 347 | dio3_mask: u16, | ||
| 348 | ) -> Result<(), RadioError<BUS>> { | ||
| 349 | let mut buffer = [0x00u8; 8]; | ||
| 350 | |||
| 351 | buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8; | ||
| 352 | buffer[1] = (irq_mask & 0x00FF) as u8; | ||
| 353 | buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8; | ||
| 354 | buffer[3] = (dio1_mask & 0x00FF) as u8; | ||
| 355 | buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8; | ||
| 356 | buffer[5] = (dio2_mask & 0x00FF) as u8; | ||
| 357 | buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8; | ||
| 358 | buffer[7] = (dio3_mask & 0x00FF) as u8; | ||
| 359 | self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?; | ||
| 360 | Ok(()) | ||
| 361 | } | ||
| 362 | |||
| 363 | // Return the current IRQ status | ||
| 364 | pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> { | ||
| 365 | let mut irq_status = [0x00u8, 0x00u8]; | ||
| 366 | self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?; | ||
| 367 | Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16)) | ||
| 368 | } | ||
| 369 | |||
| 370 | // Indicate if DIO2 is used to control an RF Switch | ||
| 371 | pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> { | ||
| 372 | self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?; | ||
| 373 | Ok(()) | ||
| 374 | } | ||
| 375 | |||
| 376 | // Indicate if the radio main clock is supplied from a TCXO | ||
| 377 | // tcxo_voltage voltage used to control the TCXO on/off from DIO3 | ||
| 378 | // timeout duration given to the TCXO to go to 32MHz | ||
| 379 | pub(super) async fn sub_set_dio3_as_tcxo_ctrl( | ||
| 380 | &mut self, | ||
| 381 | tcxo_voltage: TcxoCtrlVoltage, | ||
| 382 | timeout: u32, | ||
| 383 | ) -> Result<(), RadioError<BUS>> { | ||
| 384 | let buffer = [ | ||
| 385 | tcxo_voltage.value() & 0x07, | ||
| 386 | Self::timeout_1(timeout), | ||
| 387 | Self::timeout_2(timeout), | ||
| 388 | Self::timeout_3(timeout), | ||
| 389 | ]; | ||
| 390 | self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?; | ||
| 391 | |||
| 392 | Ok(()) | ||
| 393 | } | ||
| 394 | |||
| 395 | // Set the RF frequency (Hz) | ||
| 396 | pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> { | ||
| 397 | let mut buffer = [0x00u8; 4]; | ||
| 398 | |||
| 399 | if !self.image_calibrated { | ||
| 400 | self.sub_calibrate_image(frequency).await?; | ||
| 401 | self.image_calibrated = true; | ||
| 402 | } | ||
| 403 | |||
| 404 | let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency); | ||
| 405 | |||
| 406 | buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8; | ||
| 407 | buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8; | ||
| 408 | buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8; | ||
| 409 | buffer[3] = (freq_in_pll_steps & 0xFF) as u8; | ||
| 410 | self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?; | ||
| 411 | Ok(()) | ||
| 412 | } | ||
| 413 | |||
| 414 | // 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. | ||
| 415 | pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> { | ||
| 416 | self.packet_type = packet_type; | ||
| 417 | self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()]) | ||
| 418 | .await?; | ||
| 419 | Ok(()) | ||
| 420 | } | ||
| 421 | |||
| 422 | // Get the current radio protocol (LoRa or GFSK) | ||
| 423 | pub(super) fn sub_get_packet_type(&mut self) -> PacketType { | ||
| 424 | self.packet_type | ||
| 425 | } | ||
| 426 | |||
| 427 | // Set the transmission parameters | ||
| 428 | // power RF output power [-18..13] dBm | ||
| 429 | // ramp_time transmission ramp up time | ||
| 430 | pub(super) async fn sub_set_tx_params( | ||
| 431 | &mut self, | ||
| 432 | mut power: i8, | ||
| 433 | ramp_time: RampTime, | ||
| 434 | ) -> Result<(), RadioError<BUS>> { | ||
| 435 | if self.brd_get_radio_type() == RadioType::SX1261 { | ||
| 436 | if power == 15 { | ||
| 437 | self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?; | ||
| 438 | } else { | ||
| 439 | self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?; | ||
| 440 | } | ||
| 441 | |||
| 442 | if power >= 14 { | ||
| 443 | power = 14; | ||
| 444 | } else if power < -17 { | ||
| 445 | power = -17; | ||
| 446 | } | ||
| 447 | } else { | ||
| 448 | // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) | ||
| 449 | let mut tx_clamp_cfg = [0x00u8]; | ||
| 450 | self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?; | ||
| 451 | tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1); | ||
| 452 | self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?; | ||
| 453 | |||
| 454 | self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?; | ||
| 455 | |||
| 456 | if power > 22 { | ||
| 457 | power = 22; | ||
| 458 | } else if power < -9 { | ||
| 459 | power = -9; | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | // power conversion of negative number from i8 to u8 ??? | ||
| 464 | self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()]) | ||
| 465 | .await?; | ||
| 466 | Ok(()) | ||
| 467 | } | ||
| 468 | |||
| 469 | // Set the modulation parameters | ||
| 470 | pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 471 | if self.modulation_params.is_some() { | ||
| 472 | let mut buffer = [0x00u8; 4]; | ||
| 473 | |||
| 474 | // Since this driver only supports LoRa, ensure the packet type is set accordingly | ||
| 475 | self.sub_set_packet_type(PacketType::LoRa).await?; | ||
| 476 | |||
| 477 | let modulation_params = self.modulation_params.unwrap(); | ||
| 478 | buffer[0] = modulation_params.spreading_factor.value(); | ||
| 479 | buffer[1] = modulation_params.bandwidth.value(); | ||
| 480 | buffer[2] = modulation_params.coding_rate.value(); | ||
| 481 | buffer[3] = modulation_params.low_data_rate_optimize; | ||
| 482 | |||
| 483 | self.brd_write_command(OpCode::SetModulationParams, &buffer).await?; | ||
| 484 | Ok(()) | ||
| 485 | } else { | ||
| 486 | Err(RadioError::ModulationParamsMissing) | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | // Set the packet parameters | ||
| 491 | pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 492 | if self.packet_params.is_some() { | ||
| 493 | let mut buffer = [0x00u8; 6]; | ||
| 494 | |||
| 495 | // Since this driver only supports LoRa, ensure the packet type is set accordingly | ||
| 496 | self.sub_set_packet_type(PacketType::LoRa).await?; | ||
| 497 | |||
| 498 | let packet_params = self.packet_params.unwrap(); | ||
| 499 | buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8; | ||
| 500 | buffer[1] = (packet_params.preamble_length & 0xFF) as u8; | ||
| 501 | buffer[2] = packet_params.implicit_header as u8; | ||
| 502 | buffer[3] = packet_params.payload_length; | ||
| 503 | buffer[4] = packet_params.crc_on as u8; | ||
| 504 | buffer[5] = packet_params.iq_inverted as u8; | ||
| 505 | |||
| 506 | self.brd_write_command(OpCode::SetPacketParams, &buffer).await?; | ||
| 507 | Ok(()) | ||
| 508 | } else { | ||
| 509 | Err(RadioError::PacketParamsMissing) | ||
| 510 | } | ||
| 511 | } | ||
| 512 | |||
| 513 | // Set the channel activity detection (CAD) parameters | ||
| 514 | // symbols number of symbols to use for CAD operations | ||
| 515 | // det_peak limit for detection of SNR peak used in the CAD | ||
| 516 | // det_min minimum symbol recognition for CAD | ||
| 517 | // exit_mode operation to be done at the end of CAD action | ||
| 518 | // timeout timeout value to abort the CAD activity | ||
| 519 | |||
| 520 | pub(super) async fn sub_set_cad_params( | ||
| 521 | &mut self, | ||
| 522 | symbols: CADSymbols, | ||
| 523 | det_peak: u8, | ||
| 524 | det_min: u8, | ||
| 525 | exit_mode: CADExitMode, | ||
| 526 | timeout: u32, | ||
| 527 | ) -> Result<(), RadioError<BUS>> { | ||
| 528 | let mut buffer = [0x00u8; 7]; | ||
| 529 | |||
| 530 | buffer[0] = symbols.value(); | ||
| 531 | buffer[1] = det_peak; | ||
| 532 | buffer[2] = det_min; | ||
| 533 | buffer[3] = exit_mode.value(); | ||
| 534 | buffer[4] = Self::timeout_1(timeout); | ||
| 535 | buffer[5] = Self::timeout_2(timeout); | ||
| 536 | buffer[6] = Self::timeout_3(timeout); | ||
| 537 | |||
| 538 | self.brd_write_command(OpCode::SetCADParams, &buffer).await?; | ||
| 539 | self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); | ||
| 540 | Ok(()) | ||
| 541 | } | ||
| 542 | |||
| 543 | // Set the data buffer base address for transmission and reception | ||
| 544 | pub(super) async fn sub_set_buffer_base_address( | ||
| 545 | &mut self, | ||
| 546 | tx_base_address: u8, | ||
| 547 | rx_base_address: u8, | ||
| 548 | ) -> Result<(), RadioError<BUS>> { | ||
| 549 | self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address]) | ||
| 550 | .await?; | ||
| 551 | Ok(()) | ||
| 552 | } | ||
| 553 | |||
| 554 | // Get the current radio status | ||
| 555 | pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> { | ||
| 556 | let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?; | ||
| 557 | Ok(RadioStatus { | ||
| 558 | cmd_status: (status & (0x07 << 1)) >> 1, | ||
| 559 | chip_mode: (status & (0x07 << 4)) >> 4, | ||
| 560 | }) | ||
| 561 | } | ||
| 562 | |||
| 563 | // Get the instantaneous RSSI value for the last packet received | ||
| 564 | pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> { | ||
| 565 | let mut buffer = [0x00u8]; | ||
| 566 | self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; | ||
| 567 | let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ??? | ||
| 568 | Ok(rssi) | ||
| 569 | } | ||
| 570 | |||
| 571 | // Get the last received packet buffer status | ||
| 572 | pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> { | ||
| 573 | if self.packet_params.is_some() { | ||
| 574 | let mut status = [0x00u8; 2]; | ||
| 575 | let mut payload_length_buffer = [0x00u8]; | ||
| 576 | |||
| 577 | self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?; | ||
| 578 | if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header { | ||
| 579 | self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer) | ||
| 580 | .await?; | ||
| 581 | } else { | ||
| 582 | payload_length_buffer[0] = status[0]; | ||
| 583 | } | ||
| 584 | |||
| 585 | let payload_length = payload_length_buffer[0]; | ||
| 586 | let offset = status[1]; | ||
| 587 | |||
| 588 | Ok((payload_length, offset)) | ||
| 589 | } else { | ||
| 590 | Err(RadioError::PacketParamsMissing) | ||
| 591 | } | ||
| 592 | } | ||
| 593 | |||
| 594 | // Get the last received packet payload status | ||
| 595 | pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> { | ||
| 596 | let mut status = [0x00u8; 3]; | ||
| 597 | self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; | ||
| 598 | |||
| 599 | // check this ??? | ||
| 600 | let rssi = ((-(status[0] as i32)) >> 1) as i8; | ||
| 601 | let snr = ((status[1] as i8) + 2) >> 2; | ||
| 602 | let signal_rssi = ((-(status[2] as i32)) >> 1) as i8; | ||
| 603 | let freq_error = self.frequency_error; | ||
| 604 | |||
| 605 | Ok(PacketStatus { | ||
| 606 | rssi, | ||
| 607 | snr, | ||
| 608 | signal_rssi, | ||
| 609 | freq_error, | ||
| 610 | }) | ||
| 611 | } | ||
| 612 | |||
| 613 | // Get the possible system errors | ||
| 614 | pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> { | ||
| 615 | let mut errors = [0x00u8; 2]; | ||
| 616 | self.brd_read_command(OpCode::GetErrors, &mut errors).await?; | ||
| 617 | |||
| 618 | Ok(RadioSystemError { | ||
| 619 | rc_64khz_calibration: (errors[1] & (1 << 0)) != 0, | ||
| 620 | rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0, | ||
| 621 | pll_calibration: (errors[1] & (1 << 2)) != 0, | ||
| 622 | adc_calibration: (errors[1] & (1 << 3)) != 0, | ||
| 623 | image_calibration: (errors[1] & (1 << 4)) != 0, | ||
| 624 | xosc_start: (errors[1] & (1 << 5)) != 0, | ||
| 625 | pll_lock: (errors[1] & (1 << 6)) != 0, | ||
| 626 | pa_ramp: (errors[0] & (1 << 0)) != 0, | ||
| 627 | }) | ||
| 628 | } | ||
| 629 | |||
| 630 | // Clear all the errors in the device | ||
| 631 | pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> { | ||
| 632 | self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?; | ||
| 633 | Ok(()) | ||
| 634 | } | ||
| 635 | |||
| 636 | // Clear the IRQs | ||
| 637 | pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> { | ||
| 638 | let mut buffer = [0x00u8, 0x00u8]; | ||
| 639 | buffer[0] = ((irq >> 8) & 0xFF) as u8; | ||
| 640 | buffer[1] = (irq & 0xFF) as u8; | ||
| 641 | self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?; | ||
| 642 | Ok(()) | ||
| 643 | } | ||
| 644 | |||
| 645 | // Utility functions | ||
| 646 | |||
| 647 | fn timeout_1(timeout: u32) -> u8 { | ||
| 648 | ((timeout >> 16) & 0xFF) as u8 | ||
| 649 | } | ||
| 650 | fn timeout_2(timeout: u32) -> u8 { | ||
| 651 | ((timeout >> 8) & 0xFF) as u8 | ||
| 652 | } | ||
| 653 | fn timeout_3(timeout: u32) -> u8 { | ||
| 654 | (timeout & 0xFF) as u8 | ||
| 655 | } | ||
| 656 | |||
| 657 | // check this ??? | ||
| 658 | fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 { | ||
| 659 | let b0 = buffer[0] as u32; | ||
| 660 | let b1 = buffer[1] as u32; | ||
| 661 | let b2 = buffer[2] as u32; | ||
| 662 | let b3 = buffer[3] as u32; | ||
| 663 | (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 | ||
| 664 | } | ||
| 665 | |||
| 666 | fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 { | ||
| 667 | // Get integer and fractional parts of the frequency computed with a PLL step scaled value | ||
| 668 | let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED; | ||
| 669 | let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED); | ||
| 670 | |||
| 671 | (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT) | ||
| 672 | + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED) | ||
| 673 | } | ||
| 674 | } | ||
diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs deleted file mode 100644 index 4e8dc2232..000000000 --- a/embassy-lora/src/sx127x/mod.rs +++ /dev/null | |||
| @@ -1,192 +0,0 @@ | |||
| 1 | use embedded_hal::digital::v2::OutputPin; | ||
| 2 | use embedded_hal_async::digital::Wait; | ||
| 3 | use embedded_hal_async::spi::*; | ||
| 4 | use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; | ||
| 5 | use lorawan_device::async_device::Timings; | ||
| 6 | |||
| 7 | mod sx127x_lora; | ||
| 8 | use sx127x_lora::{Error as RadioError, LoRa, RadioMode, IRQ}; | ||
| 9 | |||
| 10 | /// Trait representing a radio switch for boards using the Sx127x radio. One some | ||
| 11 | /// boards, this will be a dummy implementation that does nothing. | ||
| 12 | pub trait RadioSwitch { | ||
| 13 | fn set_tx(&mut self); | ||
| 14 | fn set_rx(&mut self); | ||
| 15 | } | ||
| 16 | |||
| 17 | /// Semtech Sx127x radio peripheral | ||
| 18 | pub struct Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 19 | where | ||
| 20 | SPI: SpiBus<u8, Error = E> + 'static, | ||
| 21 | E: 'static, | ||
| 22 | CS: OutputPin + 'static, | ||
| 23 | RESET: OutputPin + 'static, | ||
| 24 | I: Wait + 'static, | ||
| 25 | RFS: RadioSwitch + 'static, | ||
| 26 | { | ||
| 27 | radio: LoRa<SPI, CS, RESET>, | ||
| 28 | rfs: RFS, | ||
| 29 | irq: I, | ||
| 30 | } | ||
| 31 | |||
| 32 | #[derive(Debug, Copy, Clone)] | ||
| 33 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 34 | pub enum State { | ||
| 35 | Idle, | ||
| 36 | Txing, | ||
| 37 | Rxing, | ||
| 38 | } | ||
| 39 | |||
| 40 | impl<SPI, CS, RESET, E, I, RFS> Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 41 | where | ||
| 42 | SPI: SpiBus<u8, Error = E> + 'static, | ||
| 43 | CS: OutputPin + 'static, | ||
| 44 | RESET: OutputPin + 'static, | ||
| 45 | I: Wait + 'static, | ||
| 46 | RFS: RadioSwitch + 'static, | ||
| 47 | E: 'static, | ||
| 48 | { | ||
| 49 | pub async fn new( | ||
| 50 | spi: SPI, | ||
| 51 | cs: CS, | ||
| 52 | reset: RESET, | ||
| 53 | irq: I, | ||
| 54 | rfs: RFS, | ||
| 55 | ) -> Result<Self, RadioError<E, CS::Error, RESET::Error>> { | ||
| 56 | let mut radio = LoRa::new(spi, cs, reset); | ||
| 57 | radio.reset().await?; | ||
| 58 | Ok(Self { radio, irq, rfs }) | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl<SPI, CS, RESET, E, I, RFS> Timings for Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 63 | where | ||
| 64 | SPI: SpiBus<u8, Error = E> + 'static, | ||
| 65 | CS: OutputPin + 'static, | ||
| 66 | RESET: OutputPin + 'static, | ||
| 67 | I: Wait + 'static, | ||
| 68 | RFS: RadioSwitch + 'static, | ||
| 69 | { | ||
| 70 | fn get_rx_window_offset_ms(&self) -> i32 { | ||
| 71 | -3 | ||
| 72 | } | ||
| 73 | fn get_rx_window_duration_ms(&self) -> u32 { | ||
| 74 | 1003 | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | impl<SPI, CS, RESET, E, I, RFS> PhyRxTx for Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 79 | where | ||
| 80 | SPI: SpiBus<u8, Error = E> + 'static, | ||
| 81 | CS: OutputPin + 'static, | ||
| 82 | E: 'static, | ||
| 83 | RESET: OutputPin + 'static, | ||
| 84 | I: Wait + 'static, | ||
| 85 | RFS: RadioSwitch + 'static, | ||
| 86 | { | ||
| 87 | type PhyError = Sx127xError; | ||
| 88 | |||
| 89 | async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> { | ||
| 90 | trace!("TX START"); | ||
| 91 | self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); | ||
| 92 | self.rfs.set_tx(); | ||
| 93 | self.radio.set_tx_power(14, 0).await?; | ||
| 94 | self.radio.set_frequency(config.rf.frequency).await?; | ||
| 95 | // TODO: Modify radio to support other coding rates | ||
| 96 | self.radio.set_coding_rate_4(5).await?; | ||
| 97 | self.radio | ||
| 98 | .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth)) | ||
| 99 | .await?; | ||
| 100 | self.radio | ||
| 101 | .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor)) | ||
| 102 | .await?; | ||
| 103 | |||
| 104 | self.radio.set_preamble_length(8).await?; | ||
| 105 | self.radio.set_lora_pa_ramp().await?; | ||
| 106 | self.radio.set_lora_sync_word().await?; | ||
| 107 | self.radio.set_invert_iq(false).await?; | ||
| 108 | self.radio.set_crc(true).await?; | ||
| 109 | |||
| 110 | self.radio.set_dio0_tx_done().await?; | ||
| 111 | |||
| 112 | self.radio.transmit_start(buf).await?; | ||
| 113 | |||
| 114 | loop { | ||
| 115 | self.irq.wait_for_rising_edge().await.unwrap(); | ||
| 116 | self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); | ||
| 117 | let irq = self.radio.clear_irq().await.ok().unwrap(); | ||
| 118 | if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { | ||
| 119 | trace!("TX DONE"); | ||
| 120 | return Ok(0); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { | ||
| 126 | self.rfs.set_rx(); | ||
| 127 | self.radio.reset_payload_length().await?; | ||
| 128 | self.radio.set_frequency(config.frequency).await?; | ||
| 129 | // TODO: Modify radio to support other coding rates | ||
| 130 | self.radio.set_coding_rate_4(5).await?; | ||
| 131 | self.radio | ||
| 132 | .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) | ||
| 133 | .await?; | ||
| 134 | self.radio | ||
| 135 | .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) | ||
| 136 | .await?; | ||
| 137 | |||
| 138 | self.radio.set_preamble_length(8).await?; | ||
| 139 | self.radio.set_lora_sync_word().await?; | ||
| 140 | self.radio.set_invert_iq(true).await?; | ||
| 141 | self.radio.set_crc(true).await?; | ||
| 142 | |||
| 143 | self.radio.set_dio0_rx_done().await?; | ||
| 144 | self.radio.set_mode(RadioMode::RxContinuous).await?; | ||
| 145 | |||
| 146 | loop { | ||
| 147 | self.irq.wait_for_rising_edge().await.unwrap(); | ||
| 148 | self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); | ||
| 149 | let irq = self.radio.clear_irq().await.ok().unwrap(); | ||
| 150 | if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { | ||
| 151 | let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16; | ||
| 152 | let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8; | ||
| 153 | let response = if let Ok(size) = self.radio.read_packet_size().await { | ||
| 154 | self.radio.read_packet(buf).await?; | ||
| 155 | Ok((size, RxQuality::new(rssi, snr))) | ||
| 156 | } else { | ||
| 157 | Ok((0, RxQuality::new(rssi, snr))) | ||
| 158 | }; | ||
| 159 | trace!("RX DONE"); | ||
| 160 | return response; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 167 | pub struct Sx127xError; | ||
| 168 | |||
| 169 | impl<A, B, C> From<sx127x_lora::Error<A, B, C>> for Sx127xError { | ||
| 170 | fn from(_: sx127x_lora::Error<A, B, C>) -> Self { | ||
| 171 | Sx127xError | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | fn spreading_factor_to_u8(sf: SpreadingFactor) -> u8 { | ||
| 176 | match sf { | ||
| 177 | SpreadingFactor::_7 => 7, | ||
| 178 | SpreadingFactor::_8 => 8, | ||
| 179 | SpreadingFactor::_9 => 9, | ||
| 180 | SpreadingFactor::_10 => 10, | ||
| 181 | SpreadingFactor::_11 => 11, | ||
| 182 | SpreadingFactor::_12 => 12, | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | fn bandwidth_to_i64(bw: Bandwidth) -> i64 { | ||
| 187 | match bw { | ||
| 188 | Bandwidth::_125KHz => 125_000, | ||
| 189 | Bandwidth::_250KHz => 250_000, | ||
| 190 | Bandwidth::_500KHz => 500_000, | ||
| 191 | } | ||
| 192 | } | ||
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 aacc9da22..000000000 --- a/embassy-lora/src/sx127x/sx127x_lora/mod.rs +++ /dev/null | |||
| @@ -1,539 +0,0 @@ | |||
| 1 | // Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 | ||
| 2 | // license | ||
| 3 | // | ||
| 4 | // Modifications made to make the driver work with the rust-lorawan link layer. | ||
| 5 | |||
| 6 | #![allow(dead_code)] | ||
| 7 | |||
| 8 | use bit_field::BitField; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use embedded_hal::digital::v2::OutputPin; | ||
| 11 | use embedded_hal_async::spi::SpiBus; | ||
| 12 | |||
| 13 | mod register; | ||
| 14 | pub use self::register::IRQ; | ||
| 15 | use self::register::{PaConfig, Register}; | ||
| 16 | |||
| 17 | /// Provides high-level access to Semtech SX1276/77/78/79 based boards connected to a Raspberry Pi | ||
| 18 | pub struct LoRa<SPI, CS, RESET> { | ||
| 19 | spi: SPI, | ||
| 20 | cs: CS, | ||
| 21 | reset: RESET, | ||
| 22 | pub explicit_header: bool, | ||
| 23 | pub mode: RadioMode, | ||
| 24 | } | ||
| 25 | |||
| 26 | #[allow(clippy::upper_case_acronyms)] | ||
| 27 | #[derive(Debug)] | ||
| 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 29 | pub enum Error<SPI, CS, RESET> { | ||
| 30 | Uninformative, | ||
| 31 | VersionMismatch(u8), | ||
| 32 | CS(CS), | ||
| 33 | Reset(RESET), | ||
| 34 | SPI(SPI), | ||
| 35 | Transmitting, | ||
| 36 | } | ||
| 37 | |||
| 38 | use Error::*; | ||
| 39 | |||
| 40 | use super::sx127x_lora::register::{FskDataModulationShaping, FskRampUpRamDown}; | ||
| 41 | |||
| 42 | #[cfg(not(feature = "version_0x09"))] | ||
| 43 | const VERSION_CHECK: u8 = 0x12; | ||
| 44 | |||
| 45 | #[cfg(feature = "version_0x09")] | ||
| 46 | const VERSION_CHECK: u8 = 0x09; | ||
| 47 | |||
| 48 | impl<SPI, CS, RESET, E> LoRa<SPI, CS, RESET> | ||
| 49 | where | ||
| 50 | SPI: SpiBus<u8, Error = E>, | ||
| 51 | CS: OutputPin, | ||
| 52 | RESET: OutputPin, | ||
| 53 | { | ||
| 54 | /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time. | ||
| 55 | /// This also preforms a hardware reset of the module and then puts it in standby. | ||
| 56 | pub fn new(spi: SPI, cs: CS, reset: RESET) -> Self { | ||
| 57 | Self { | ||
| 58 | spi, | ||
| 59 | cs, | ||
| 60 | reset, | ||
| 61 | explicit_header: true, | ||
| 62 | mode: RadioMode::Sleep, | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | pub async fn reset(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 67 | self.reset.set_low().map_err(Reset)?; | ||
| 68 | Timer::after(Duration::from_millis(10)).await; | ||
| 69 | self.reset.set_high().map_err(Reset)?; | ||
| 70 | Timer::after(Duration::from_millis(10)).await; | ||
| 71 | let version = self.read_register(Register::RegVersion.addr()).await?; | ||
| 72 | if version == VERSION_CHECK { | ||
| 73 | self.set_mode(RadioMode::Sleep).await?; | ||
| 74 | self.write_register(Register::RegFifoTxBaseAddr.addr(), 0).await?; | ||
| 75 | self.write_register(Register::RegFifoRxBaseAddr.addr(), 0).await?; | ||
| 76 | let lna = self.read_register(Register::RegLna.addr()).await?; | ||
| 77 | self.write_register(Register::RegLna.addr(), lna | 0x03).await?; | ||
| 78 | self.write_register(Register::RegModemConfig3.addr(), 0x04).await?; | ||
| 79 | self.set_tcxo(true).await?; | ||
| 80 | self.set_mode(RadioMode::Stdby).await?; | ||
| 81 | self.cs.set_high().map_err(CS)?; | ||
| 82 | Ok(()) | ||
| 83 | } else { | ||
| 84 | Err(Error::VersionMismatch(version)) | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | pub async fn set_dio0_tx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 89 | self.write_register(Register::RegIrqFlagsMask.addr(), 0b1111_0111) | ||
| 90 | .await?; | ||
| 91 | let mapping = self.read_register(Register::RegDioMapping1.addr()).await?; | ||
| 92 | self.write_register(Register::RegDioMapping1.addr(), (mapping & 0x3F) | 0x40) | ||
| 93 | .await | ||
| 94 | } | ||
| 95 | |||
| 96 | pub async fn set_dio0_rx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 97 | self.write_register(Register::RegIrqFlagsMask.addr(), 0b0001_1111) | ||
| 98 | .await?; | ||
| 99 | let mapping = self.read_register(Register::RegDioMapping1.addr()).await?; | ||
| 100 | self.write_register(Register::RegDioMapping1.addr(), mapping & 0x3F) | ||
| 101 | .await | ||
| 102 | } | ||
| 103 | |||
| 104 | pub async fn transmit_start(&mut self, buffer: &[u8]) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 105 | assert!(buffer.len() < 255); | ||
| 106 | if self.transmitting().await? { | ||
| 107 | //trace!("ALREADY TRANSMNITTING"); | ||
| 108 | Err(Transmitting) | ||
| 109 | } else { | ||
| 110 | self.set_mode(RadioMode::Stdby).await?; | ||
| 111 | if self.explicit_header { | ||
| 112 | self.set_explicit_header_mode().await?; | ||
| 113 | } else { | ||
| 114 | self.set_implicit_header_mode().await?; | ||
| 115 | } | ||
| 116 | |||
| 117 | self.write_register(Register::RegIrqFlags.addr(), 0).await?; | ||
| 118 | self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?; | ||
| 119 | self.write_register(Register::RegPayloadLength.addr(), 0).await?; | ||
| 120 | for byte in buffer.iter() { | ||
| 121 | self.write_register(Register::RegFifo.addr(), *byte).await?; | ||
| 122 | } | ||
| 123 | self.write_register(Register::RegPayloadLength.addr(), buffer.len() as u8) | ||
| 124 | .await?; | ||
| 125 | self.set_mode(RadioMode::Tx).await?; | ||
| 126 | Ok(()) | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | pub async fn packet_ready(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> { | ||
| 131 | Ok(self.read_register(Register::RegIrqFlags.addr()).await?.get_bit(6)) | ||
| 132 | } | ||
| 133 | |||
| 134 | pub async fn irq_flags_mask(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 135 | Ok(self.read_register(Register::RegIrqFlagsMask.addr()).await? as u8) | ||
| 136 | } | ||
| 137 | |||
| 138 | pub async fn irq_flags(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 139 | Ok(self.read_register(Register::RegIrqFlags.addr()).await? as u8) | ||
| 140 | } | ||
| 141 | |||
| 142 | pub async fn read_packet_size(&mut self) -> Result<usize, Error<E, CS::Error, RESET::Error>> { | ||
| 143 | let size = self.read_register(Register::RegRxNbBytes.addr()).await?; | ||
| 144 | Ok(size as usize) | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Returns the contents of the fifo as a fixed 255 u8 array. This should only be called is there is a | ||
| 148 | /// new packet ready to be read. | ||
| 149 | pub async fn read_packet(&mut self, buffer: &mut [u8]) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 150 | self.clear_irq().await?; | ||
| 151 | let size = self.read_register(Register::RegRxNbBytes.addr()).await?; | ||
| 152 | assert!(size as usize <= buffer.len()); | ||
| 153 | let fifo_addr = self.read_register(Register::RegFifoRxCurrentAddr.addr()).await?; | ||
| 154 | self.write_register(Register::RegFifoAddrPtr.addr(), fifo_addr).await?; | ||
| 155 | for i in 0..size { | ||
| 156 | let byte = self.read_register(Register::RegFifo.addr()).await?; | ||
| 157 | buffer[i as usize] = byte; | ||
| 158 | } | ||
| 159 | self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?; | ||
| 160 | Ok(()) | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Returns true if the radio is currently transmitting a packet. | ||
| 164 | pub async fn transmitting(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> { | ||
| 165 | if (self.read_register(Register::RegOpMode.addr()).await?) & RadioMode::Tx.addr() == RadioMode::Tx.addr() { | ||
| 166 | Ok(true) | ||
| 167 | } else { | ||
| 168 | if (self.read_register(Register::RegIrqFlags.addr()).await? & IRQ::IrqTxDoneMask.addr()) == 1 { | ||
| 169 | self.write_register(Register::RegIrqFlags.addr(), IRQ::IrqTxDoneMask.addr()) | ||
| 170 | .await?; | ||
| 171 | } | ||
| 172 | Ok(false) | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Clears the radio's IRQ registers. | ||
| 177 | pub async fn clear_irq(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 178 | let irq_flags = self.read_register(Register::RegIrqFlags.addr()).await?; | ||
| 179 | self.write_register(Register::RegIrqFlags.addr(), 0xFF).await?; | ||
| 180 | Ok(irq_flags) | ||
| 181 | } | ||
| 182 | |||
| 183 | /// Sets the transmit power and pin. Levels can range from 0-14 when the output | ||
| 184 | /// pin = 0(RFO), and form 0-20 when output pin = 1(PaBoost). Power is in dB. | ||
| 185 | /// Default value is `17`. | ||
| 186 | pub async fn set_tx_power( | ||
| 187 | &mut self, | ||
| 188 | mut level: i32, | ||
| 189 | output_pin: u8, | ||
| 190 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 191 | if PaConfig::PaOutputRfoPin.addr() == output_pin { | ||
| 192 | // RFO | ||
| 193 | if level < 0 { | ||
| 194 | level = 0; | ||
| 195 | } else if level > 14 { | ||
| 196 | level = 14; | ||
| 197 | } | ||
| 198 | self.write_register(Register::RegPaConfig.addr(), (0x70 | level) as u8) | ||
| 199 | .await | ||
| 200 | } else { | ||
| 201 | // PA BOOST | ||
| 202 | if level > 17 { | ||
| 203 | if level > 20 { | ||
| 204 | level = 20; | ||
| 205 | } | ||
| 206 | // subtract 3 from level, so 18 - 20 maps to 15 - 17 | ||
| 207 | level -= 3; | ||
| 208 | |||
| 209 | // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) | ||
| 210 | self.write_register(Register::RegPaDac.addr(), 0x87).await?; | ||
| 211 | self.set_ocp(140).await?; | ||
| 212 | } else { | ||
| 213 | if level < 2 { | ||
| 214 | level = 2; | ||
| 215 | } | ||
| 216 | //Default value PA_HF/LF or +17dBm | ||
| 217 | self.write_register(Register::RegPaDac.addr(), 0x84).await?; | ||
| 218 | self.set_ocp(100).await?; | ||
| 219 | } | ||
| 220 | level -= 2; | ||
| 221 | self.write_register(Register::RegPaConfig.addr(), PaConfig::PaBoost.addr() | level as u8) | ||
| 222 | .await | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | pub async fn get_modem_stat(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 227 | Ok(self.read_register(Register::RegModemStat.addr()).await? as u8) | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Sets the over current protection on the radio(mA). | ||
| 231 | pub async fn set_ocp(&mut self, ma: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 232 | let mut ocp_trim: u8 = 27; | ||
| 233 | |||
| 234 | if ma <= 120 { | ||
| 235 | ocp_trim = (ma - 45) / 5; | ||
| 236 | } else if ma <= 240 { | ||
| 237 | ocp_trim = (ma + 30) / 10; | ||
| 238 | } | ||
| 239 | self.write_register(Register::RegOcp.addr(), 0x20 | (0x1F & ocp_trim)) | ||
| 240 | .await | ||
| 241 | } | ||
| 242 | |||
| 243 | /// Sets the state of the radio. Default mode after initiation is `Standby`. | ||
| 244 | pub async fn set_mode(&mut self, mode: RadioMode) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 245 | if self.explicit_header { | ||
| 246 | self.set_explicit_header_mode().await?; | ||
| 247 | } else { | ||
| 248 | self.set_implicit_header_mode().await?; | ||
| 249 | } | ||
| 250 | self.write_register( | ||
| 251 | Register::RegOpMode.addr(), | ||
| 252 | RadioMode::LongRangeMode.addr() | mode.addr(), | ||
| 253 | ) | ||
| 254 | .await?; | ||
| 255 | |||
| 256 | self.mode = mode; | ||
| 257 | Ok(()) | ||
| 258 | } | ||
| 259 | |||
| 260 | pub async fn reset_payload_length(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 261 | self.write_register(Register::RegPayloadLength.addr(), 0xFF).await | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Sets the frequency of the radio. Values are in megahertz. | ||
| 265 | /// I.E. 915 MHz must be used for North America. Check regulation for your area. | ||
| 266 | pub async fn set_frequency(&mut self, freq: u32) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 267 | const FREQ_STEP: f64 = 61.03515625; | ||
| 268 | // calculate register values | ||
| 269 | let frf = (freq as f64 / FREQ_STEP) as u32; | ||
| 270 | // write registers | ||
| 271 | self.write_register(Register::RegFrfMsb.addr(), ((frf & 0x00FF_0000) >> 16) as u8) | ||
| 272 | .await?; | ||
| 273 | self.write_register(Register::RegFrfMid.addr(), ((frf & 0x0000_FF00) >> 8) as u8) | ||
| 274 | .await?; | ||
| 275 | self.write_register(Register::RegFrfLsb.addr(), (frf & 0x0000_00FF) as u8) | ||
| 276 | .await | ||
| 277 | } | ||
| 278 | |||
| 279 | /// Sets the radio to use an explicit header. Default state is `ON`. | ||
| 280 | async fn set_explicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 281 | let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; | ||
| 282 | self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0xfe) | ||
| 283 | .await?; | ||
| 284 | self.explicit_header = true; | ||
| 285 | Ok(()) | ||
| 286 | } | ||
| 287 | |||
| 288 | /// Sets the radio to use an implicit header. Default state is `OFF`. | ||
| 289 | async fn set_implicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 290 | let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; | ||
| 291 | self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0x01) | ||
| 292 | .await?; | ||
| 293 | self.explicit_header = false; | ||
| 294 | Ok(()) | ||
| 295 | } | ||
| 296 | |||
| 297 | /// Sets the spreading factor of the radio. Supported values are between 6 and 12. | ||
| 298 | /// If a spreading factor of 6 is set, implicit header mode must be used to transmit | ||
| 299 | /// and receive packets. Default value is `7`. | ||
| 300 | pub async fn set_spreading_factor(&mut self, mut sf: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 301 | if sf < 6 { | ||
| 302 | sf = 6; | ||
| 303 | } else if sf > 12 { | ||
| 304 | sf = 12; | ||
| 305 | } | ||
| 306 | |||
| 307 | if sf == 6 { | ||
| 308 | self.write_register(Register::RegDetectionOptimize.addr(), 0xc5).await?; | ||
| 309 | self.write_register(Register::RegDetectionThreshold.addr(), 0x0c) | ||
| 310 | .await?; | ||
| 311 | } else { | ||
| 312 | self.write_register(Register::RegDetectionOptimize.addr(), 0xc3).await?; | ||
| 313 | self.write_register(Register::RegDetectionThreshold.addr(), 0x0a) | ||
| 314 | .await?; | ||
| 315 | } | ||
| 316 | let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?; | ||
| 317 | self.write_register( | ||
| 318 | Register::RegModemConfig2.addr(), | ||
| 319 | (modem_config_2 & 0x0f) | ((sf << 4) & 0xf0), | ||
| 320 | ) | ||
| 321 | .await?; | ||
| 322 | self.set_ldo_flag().await?; | ||
| 323 | |||
| 324 | self.write_register(Register::RegSymbTimeoutLsb.addr(), 0x05).await?; | ||
| 325 | |||
| 326 | Ok(()) | ||
| 327 | } | ||
| 328 | |||
| 329 | pub async fn set_tcxo(&mut self, external: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 330 | if external { | ||
| 331 | self.write_register(Register::RegTcxo.addr(), 0x10).await | ||
| 332 | } else { | ||
| 333 | self.write_register(Register::RegTcxo.addr(), 0x00).await | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | /// Sets the signal bandwidth of the radio. Supported values are: `7800 Hz`, `10400 Hz`, | ||
| 338 | /// `15600 Hz`, `20800 Hz`, `31250 Hz`,`41700 Hz` ,`62500 Hz`,`125000 Hz` and `250000 Hz` | ||
| 339 | /// Default value is `125000 Hz` | ||
| 340 | pub async fn set_signal_bandwidth(&mut self, sbw: i64) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 341 | let bw: i64 = match sbw { | ||
| 342 | 7_800 => 0, | ||
| 343 | 10_400 => 1, | ||
| 344 | 15_600 => 2, | ||
| 345 | 20_800 => 3, | ||
| 346 | 31_250 => 4, | ||
| 347 | 41_700 => 5, | ||
| 348 | 62_500 => 6, | ||
| 349 | 125_000 => 7, | ||
| 350 | 250_000 => 8, | ||
| 351 | _ => 9, | ||
| 352 | }; | ||
| 353 | let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; | ||
| 354 | self.write_register( | ||
| 355 | Register::RegModemConfig1.addr(), | ||
| 356 | (modem_config_1 & 0x0f) | ((bw << 4) as u8), | ||
| 357 | ) | ||
| 358 | .await?; | ||
| 359 | self.set_ldo_flag().await?; | ||
| 360 | Ok(()) | ||
| 361 | } | ||
| 362 | |||
| 363 | /// Sets the coding rate of the radio with the numerator fixed at 4. Supported values | ||
| 364 | /// are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. | ||
| 365 | /// Default value is `5`. | ||
| 366 | pub async fn set_coding_rate_4(&mut self, mut denominator: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 367 | if denominator < 5 { | ||
| 368 | denominator = 5; | ||
| 369 | } else if denominator > 8 { | ||
| 370 | denominator = 8; | ||
| 371 | } | ||
| 372 | let cr = denominator - 4; | ||
| 373 | let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; | ||
| 374 | self.write_register(Register::RegModemConfig1.addr(), (modem_config_1 & 0xf1) | (cr << 1)) | ||
| 375 | .await | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Sets the preamble length of the radio. Values are between 6 and 65535. | ||
| 379 | /// Default value is `8`. | ||
| 380 | pub async fn set_preamble_length(&mut self, length: i64) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 381 | self.write_register(Register::RegPreambleMsb.addr(), (length >> 8) as u8) | ||
| 382 | .await?; | ||
| 383 | self.write_register(Register::RegPreambleLsb.addr(), length as u8).await | ||
| 384 | } | ||
| 385 | |||
| 386 | /// Enables are disables the radio's CRC check. Default value is `false`. | ||
| 387 | pub async fn set_crc(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 388 | let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?; | ||
| 389 | if value { | ||
| 390 | self.write_register(Register::RegModemConfig2.addr(), modem_config_2 | 0x04) | ||
| 391 | .await | ||
| 392 | } else { | ||
| 393 | self.write_register(Register::RegModemConfig2.addr(), modem_config_2 & 0xfb) | ||
| 394 | .await | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | /// Inverts the radio's IQ signals. Default value is `false`. | ||
| 399 | pub async fn set_invert_iq(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 400 | if value { | ||
| 401 | self.write_register(Register::RegInvertiq.addr(), 0x66).await?; | ||
| 402 | self.write_register(Register::RegInvertiq2.addr(), 0x19).await | ||
| 403 | } else { | ||
| 404 | self.write_register(Register::RegInvertiq.addr(), 0x27).await?; | ||
| 405 | self.write_register(Register::RegInvertiq2.addr(), 0x1d).await | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | /// Returns the spreading factor of the radio. | ||
| 410 | pub async fn get_spreading_factor(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 411 | Ok(self.read_register(Register::RegModemConfig2.addr()).await? >> 4) | ||
| 412 | } | ||
| 413 | |||
| 414 | /// Returns the signal bandwidth of the radio. | ||
| 415 | pub async fn get_signal_bandwidth(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> { | ||
| 416 | let bw = self.read_register(Register::RegModemConfig1.addr()).await? >> 4; | ||
| 417 | let bw = match bw { | ||
| 418 | 0 => 7_800, | ||
| 419 | 1 => 10_400, | ||
| 420 | 2 => 15_600, | ||
| 421 | 3 => 20_800, | ||
| 422 | 4 => 31_250, | ||
| 423 | 5 => 41_700, | ||
| 424 | 6 => 62_500, | ||
| 425 | 7 => 125_000, | ||
| 426 | 8 => 250_000, | ||
| 427 | 9 => 500_000, | ||
| 428 | _ => -1, | ||
| 429 | }; | ||
| 430 | Ok(bw) | ||
| 431 | } | ||
| 432 | |||
| 433 | /// Returns the RSSI of the last received packet. | ||
| 434 | pub async fn get_packet_rssi(&mut self) -> Result<i32, Error<E, CS::Error, RESET::Error>> { | ||
| 435 | Ok(i32::from(self.read_register(Register::RegPktRssiValue.addr()).await?) - 157) | ||
| 436 | } | ||
| 437 | |||
| 438 | /// Returns the signal to noise radio of the the last received packet. | ||
| 439 | pub async fn get_packet_snr(&mut self) -> Result<f64, Error<E, CS::Error, RESET::Error>> { | ||
| 440 | Ok(f64::from(self.read_register(Register::RegPktSnrValue.addr()).await?)) | ||
| 441 | } | ||
| 442 | |||
| 443 | /// Returns the frequency error of the last received packet in Hz. | ||
| 444 | pub async fn get_packet_frequency_error(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> { | ||
| 445 | let mut freq_error: i32; | ||
| 446 | freq_error = i32::from(self.read_register(Register::RegFreqErrorMsb.addr()).await? & 0x7); | ||
| 447 | freq_error <<= 8i64; | ||
| 448 | freq_error += i32::from(self.read_register(Register::RegFreqErrorMid.addr()).await?); | ||
| 449 | freq_error <<= 8i64; | ||
| 450 | freq_error += i32::from(self.read_register(Register::RegFreqErrorLsb.addr()).await?); | ||
| 451 | |||
| 452 | let f_xtal = 32_000_000; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) | ||
| 453 | let f_error = ((f64::from(freq_error) * (1i64 << 24) as f64) / f64::from(f_xtal)) | ||
| 454 | * (self.get_signal_bandwidth().await? as f64 / 500_000.0f64); // p. 37 | ||
| 455 | Ok(f_error as i64) | ||
| 456 | } | ||
| 457 | |||
| 458 | async fn set_ldo_flag(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 459 | let sw = self.get_signal_bandwidth().await?; | ||
| 460 | // Section 4.1.1.5 | ||
| 461 | let symbol_duration = 1000 / (sw / ((1_i64) << self.get_spreading_factor().await?)); | ||
| 462 | |||
| 463 | // Section 4.1.1.6 | ||
| 464 | let ldo_on = symbol_duration > 16; | ||
| 465 | |||
| 466 | let mut config_3 = self.read_register(Register::RegModemConfig3.addr()).await?; | ||
| 467 | config_3.set_bit(3, ldo_on); | ||
| 468 | //config_3.set_bit(2, true); | ||
| 469 | self.write_register(Register::RegModemConfig3.addr(), config_3).await | ||
| 470 | } | ||
| 471 | |||
| 472 | async fn read_register(&mut self, reg: u8) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 473 | let mut buffer = [reg & 0x7f, 0]; | ||
| 474 | self.cs.set_low().map_err(CS)?; | ||
| 475 | |||
| 476 | let _ = self.spi.transfer(&mut buffer, &[reg & 0x7f, 0]).await.map_err(SPI)?; | ||
| 477 | |||
| 478 | self.cs.set_high().map_err(CS)?; | ||
| 479 | Ok(buffer[1]) | ||
| 480 | } | ||
| 481 | |||
| 482 | async fn write_register(&mut self, reg: u8, byte: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 483 | self.cs.set_low().map_err(CS)?; | ||
| 484 | let buffer = [reg | 0x80, byte]; | ||
| 485 | self.spi.write(&buffer).await.map_err(SPI)?; | ||
| 486 | self.cs.set_high().map_err(CS)?; | ||
| 487 | Ok(()) | ||
| 488 | } | ||
| 489 | |||
| 490 | pub async fn put_in_fsk_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 491 | // Put in FSK mode | ||
| 492 | let mut op_mode = 0; | ||
| 493 | op_mode | ||
| 494 | .set_bit(7, false) // FSK mode | ||
| 495 | .set_bits(5..6, 0x00) // FSK modulation | ||
| 496 | .set_bit(3, false) //Low freq registers | ||
| 497 | .set_bits(0..2, 0b011); // Mode | ||
| 498 | |||
| 499 | self.write_register(Register::RegOpMode as u8, op_mode).await | ||
| 500 | } | ||
| 501 | |||
| 502 | pub async fn set_fsk_pa_ramp( | ||
| 503 | &mut self, | ||
| 504 | modulation_shaping: FskDataModulationShaping, | ||
| 505 | ramp: FskRampUpRamDown, | ||
| 506 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 507 | let mut pa_ramp = 0; | ||
| 508 | pa_ramp | ||
| 509 | .set_bits(5..6, modulation_shaping as u8) | ||
| 510 | .set_bits(0..3, ramp as u8); | ||
| 511 | |||
| 512 | self.write_register(Register::RegPaRamp as u8, pa_ramp).await | ||
| 513 | } | ||
| 514 | |||
| 515 | pub async fn set_lora_pa_ramp(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 516 | self.write_register(Register::RegPaRamp as u8, 0b1000).await | ||
| 517 | } | ||
| 518 | |||
| 519 | pub async fn set_lora_sync_word(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 520 | self.write_register(Register::RegSyncWord as u8, 0x34).await | ||
| 521 | } | ||
| 522 | } | ||
| 523 | /// Modes of the radio and their corresponding register values. | ||
| 524 | #[derive(Clone, Copy)] | ||
| 525 | pub enum RadioMode { | ||
| 526 | LongRangeMode = 0x80, | ||
| 527 | Sleep = 0x00, | ||
| 528 | Stdby = 0x01, | ||
| 529 | Tx = 0x03, | ||
| 530 | RxContinuous = 0x05, | ||
| 531 | RxSingle = 0x06, | ||
| 532 | } | ||
| 533 | |||
| 534 | impl RadioMode { | ||
| 535 | /// Returns the address of the mode. | ||
| 536 | pub fn addr(self) -> u8 { | ||
| 537 | self as u8 | ||
| 538 | } | ||
| 539 | } | ||
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 2445e21b1..000000000 --- a/embassy-lora/src/sx127x/sx127x_lora/register.rs +++ /dev/null | |||
| @@ -1,107 +0,0 @@ | |||
| 1 | // Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 | ||
| 2 | // license | ||
| 3 | // | ||
| 4 | // Modifications made to make the driver work with the rust-lorawan link layer. | ||
| 5 | #![allow(dead_code, clippy::enum_variant_names)] | ||
| 6 | |||
| 7 | #[derive(Clone, Copy)] | ||
| 8 | pub enum Register { | ||
| 9 | RegFifo = 0x00, | ||
| 10 | RegOpMode = 0x01, | ||
| 11 | RegFrfMsb = 0x06, | ||
| 12 | RegFrfMid = 0x07, | ||
| 13 | RegFrfLsb = 0x08, | ||
| 14 | RegPaConfig = 0x09, | ||
| 15 | RegPaRamp = 0x0a, | ||
| 16 | RegOcp = 0x0b, | ||
| 17 | RegLna = 0x0c, | ||
| 18 | RegFifoAddrPtr = 0x0d, | ||
| 19 | RegFifoTxBaseAddr = 0x0e, | ||
| 20 | RegFifoRxBaseAddr = 0x0f, | ||
| 21 | RegFifoRxCurrentAddr = 0x10, | ||
| 22 | RegIrqFlagsMask = 0x11, | ||
| 23 | RegIrqFlags = 0x12, | ||
| 24 | RegRxNbBytes = 0x13, | ||
| 25 | RegPktSnrValue = 0x19, | ||
| 26 | RegModemStat = 0x18, | ||
| 27 | RegPktRssiValue = 0x1a, | ||
| 28 | RegModemConfig1 = 0x1d, | ||
| 29 | RegModemConfig2 = 0x1e, | ||
| 30 | RegSymbTimeoutLsb = 0x1f, | ||
| 31 | RegPreambleMsb = 0x20, | ||
| 32 | RegPreambleLsb = 0x21, | ||
| 33 | RegPayloadLength = 0x22, | ||
| 34 | RegMaxPayloadLength = 0x23, | ||
| 35 | RegModemConfig3 = 0x26, | ||
| 36 | RegFreqErrorMsb = 0x28, | ||
| 37 | RegFreqErrorMid = 0x29, | ||
| 38 | RegFreqErrorLsb = 0x2a, | ||
| 39 | RegRssiWideband = 0x2c, | ||
| 40 | RegDetectionOptimize = 0x31, | ||
| 41 | RegInvertiq = 0x33, | ||
| 42 | RegDetectionThreshold = 0x37, | ||
| 43 | RegSyncWord = 0x39, | ||
| 44 | RegInvertiq2 = 0x3b, | ||
| 45 | RegDioMapping1 = 0x40, | ||
| 46 | RegVersion = 0x42, | ||
| 47 | RegTcxo = 0x4b, | ||
| 48 | RegPaDac = 0x4d, | ||
| 49 | } | ||
| 50 | #[derive(Clone, Copy)] | ||
| 51 | pub enum PaConfig { | ||
| 52 | PaBoost = 0x80, | ||
| 53 | PaOutputRfoPin = 0, | ||
| 54 | } | ||
| 55 | |||
| 56 | #[derive(Clone, Copy)] | ||
| 57 | pub enum IRQ { | ||
| 58 | IrqTxDoneMask = 0x08, | ||
| 59 | IrqPayloadCrcErrorMask = 0x20, | ||
| 60 | IrqRxDoneMask = 0x40, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl Register { | ||
| 64 | pub fn addr(self) -> u8 { | ||
| 65 | self as u8 | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | impl PaConfig { | ||
| 70 | pub fn addr(self) -> u8 { | ||
| 71 | self as u8 | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl IRQ { | ||
| 76 | pub fn addr(self) -> u8 { | ||
| 77 | self as u8 | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | #[derive(Clone, Copy)] | ||
| 82 | pub enum FskDataModulationShaping { | ||
| 83 | None = 1, | ||
| 84 | GaussianBt1d0 = 2, | ||
| 85 | GaussianBt0d5 = 10, | ||
| 86 | GaussianBt0d3 = 11, | ||
| 87 | } | ||
| 88 | |||
| 89 | #[derive(Clone, Copy)] | ||
| 90 | pub enum FskRampUpRamDown { | ||
| 91 | _3d4ms = 0b000, | ||
| 92 | _2ms = 0b0001, | ||
| 93 | _1ms = 0b0010, | ||
| 94 | _500us = 0b0011, | ||
| 95 | _250us = 0b0100, | ||
| 96 | _125us = 0b0101, | ||
| 97 | _100us = 0b0110, | ||
| 98 | _62us = 0b0111, | ||
| 99 | _50us = 0b1000, | ||
| 100 | _40us = 0b1001, | ||
| 101 | _31us = 0b1010, | ||
| 102 | _25us = 0b1011, | ||
| 103 | _20us = 0b1100, | ||
| 104 | _15us = 0b1101, | ||
| 105 | _12us = 0b1110, | ||
| 106 | _10us = 0b1111, | ||
| 107 | } | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index f0fc152ce..24a26eddd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -55,9 +55,6 @@ pub mod rtc; | |||
| 55 | pub mod sdmmc; | 55 | pub mod sdmmc; |
| 56 | #[cfg(spi)] | 56 | #[cfg(spi)] |
| 57 | pub mod spi; | 57 | pub mod spi; |
| 58 | #[cfg(stm32wl)] | ||
| 59 | #[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] | ||
| 60 | pub mod subghz; | ||
| 61 | #[cfg(usart)] | 58 | #[cfg(usart)] |
| 62 | pub mod usart; | 59 | pub mod usart; |
| 63 | #[cfg(all(usb, feature = "time"))] | 60 | #[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 f3cba05f9..000000000 --- a/embassy-stm32/src/subghz/bit_sync.rs +++ /dev/null | |||
| @@ -1,160 +0,0 @@ | |||
| 1 | /// Bit synchronization. | ||
| 2 | /// | ||
| 3 | /// This must be cleared to `0x00` (the reset value) when using packet types | ||
| 4 | /// other than LoRa. | ||
| 5 | /// | ||
| 6 | /// Argument of [`set_bit_sync`](crate::subghz::SubGhz::set_bit_sync). | ||
| 7 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 9 | pub struct BitSync { | ||
| 10 | val: u8, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl BitSync { | ||
| 14 | /// Bit synchronization register reset value. | ||
| 15 | pub const RESET: BitSync = BitSync { val: 0x00 }; | ||
| 16 | |||
| 17 | /// Create a new [`BitSync`] structure from a raw value. | ||
| 18 | /// | ||
| 19 | /// Reserved bits will be masked. | ||
| 20 | pub const fn from_raw(raw: u8) -> Self { | ||
| 21 | Self { val: raw & 0x70 } | ||
| 22 | } | ||
| 23 | |||
| 24 | /// Get the raw value of the [`BitSync`] register. | ||
| 25 | pub const fn as_bits(&self) -> u8 { | ||
| 26 | self.val | ||
| 27 | } | ||
| 28 | |||
| 29 | /// LoRa simple bit synchronization enable. | ||
| 30 | /// | ||
| 31 | /// # Example | ||
| 32 | /// | ||
| 33 | /// Enable simple bit synchronization. | ||
| 34 | /// | ||
| 35 | /// ``` | ||
| 36 | /// use stm32wlxx_hal::subghz::BitSync; | ||
| 37 | /// | ||
| 38 | /// const BIT_SYNC: BitSync = BitSync::RESET.set_simple_bit_sync_en(true); | ||
| 39 | /// # assert_eq!(u8::from(BIT_SYNC), 0x40u8); | ||
| 40 | /// ``` | ||
| 41 | #[must_use = "set_simple_bit_sync_en returns a modified BitSync"] | ||
| 42 | pub const fn set_simple_bit_sync_en(mut self, en: bool) -> BitSync { | ||
| 43 | if en { | ||
| 44 | self.val |= 1 << 6; | ||
| 45 | } else { | ||
| 46 | self.val &= !(1 << 6); | ||
| 47 | } | ||
| 48 | self | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Returns `true` if simple bit synchronization is enabled. | ||
| 52 | /// | ||
| 53 | /// # Example | ||
| 54 | /// | ||
| 55 | /// ``` | ||
| 56 | /// use stm32wlxx_hal::subghz::BitSync; | ||
| 57 | /// | ||
| 58 | /// let bs: BitSync = BitSync::RESET; | ||
| 59 | /// assert_eq!(bs.simple_bit_sync_en(), false); | ||
| 60 | /// let bs: BitSync = bs.set_simple_bit_sync_en(true); | ||
| 61 | /// assert_eq!(bs.simple_bit_sync_en(), true); | ||
| 62 | /// let bs: BitSync = bs.set_simple_bit_sync_en(false); | ||
| 63 | /// assert_eq!(bs.simple_bit_sync_en(), false); | ||
| 64 | /// ``` | ||
| 65 | pub const fn simple_bit_sync_en(&self) -> bool { | ||
| 66 | self.val & (1 << 6) != 0 | ||
| 67 | } | ||
| 68 | |||
| 69 | /// LoRa RX data inversion. | ||
| 70 | /// | ||
| 71 | /// # Example | ||
| 72 | /// | ||
| 73 | /// Invert receive data. | ||
| 74 | /// | ||
| 75 | /// ``` | ||
| 76 | /// use stm32wlxx_hal::subghz::BitSync; | ||
| 77 | /// | ||
| 78 | /// const BIT_SYNC: BitSync = BitSync::RESET.set_rx_data_inv(true); | ||
| 79 | /// # assert_eq!(u8::from(BIT_SYNC), 0x20u8); | ||
| 80 | /// ``` | ||
| 81 | #[must_use = "set_rx_data_inv returns a modified BitSync"] | ||
| 82 | pub const fn set_rx_data_inv(mut self, inv: bool) -> BitSync { | ||
| 83 | if inv { | ||
| 84 | self.val |= 1 << 5; | ||
| 85 | } else { | ||
| 86 | self.val &= !(1 << 5); | ||
| 87 | } | ||
| 88 | self | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Returns `true` if LoRa RX data is inverted. | ||
| 92 | /// | ||
| 93 | /// # Example | ||
| 94 | /// | ||
| 95 | /// ``` | ||
| 96 | /// use stm32wlxx_hal::subghz::BitSync; | ||
| 97 | /// | ||
| 98 | /// let bs: BitSync = BitSync::RESET; | ||
| 99 | /// assert_eq!(bs.rx_data_inv(), false); | ||
| 100 | /// let bs: BitSync = bs.set_rx_data_inv(true); | ||
| 101 | /// assert_eq!(bs.rx_data_inv(), true); | ||
| 102 | /// let bs: BitSync = bs.set_rx_data_inv(false); | ||
| 103 | /// assert_eq!(bs.rx_data_inv(), false); | ||
| 104 | /// ``` | ||
| 105 | pub const fn rx_data_inv(&self) -> bool { | ||
| 106 | self.val & (1 << 5) != 0 | ||
| 107 | } | ||
| 108 | |||
| 109 | /// LoRa normal bit synchronization enable. | ||
| 110 | /// | ||
| 111 | /// # Example | ||
| 112 | /// | ||
| 113 | /// Enable normal bit synchronization. | ||
| 114 | /// | ||
| 115 | /// ``` | ||
| 116 | /// use stm32wlxx_hal::subghz::BitSync; | ||
| 117 | /// | ||
| 118 | /// const BIT_SYNC: BitSync = BitSync::RESET.set_norm_bit_sync_en(true); | ||
| 119 | /// # assert_eq!(u8::from(BIT_SYNC), 0x10u8); | ||
| 120 | /// ``` | ||
| 121 | #[must_use = "set_norm_bit_sync_en returns a modified BitSync"] | ||
| 122 | pub const fn set_norm_bit_sync_en(mut self, en: bool) -> BitSync { | ||
| 123 | if en { | ||
| 124 | self.val |= 1 << 4; | ||
| 125 | } else { | ||
| 126 | self.val &= !(1 << 4); | ||
| 127 | } | ||
| 128 | self | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Returns `true` if normal bit synchronization is enabled. | ||
| 132 | /// | ||
| 133 | /// # Example | ||
| 134 | /// | ||
| 135 | /// ``` | ||
| 136 | /// use stm32wlxx_hal::subghz::BitSync; | ||
| 137 | /// | ||
| 138 | /// let bs: BitSync = BitSync::RESET; | ||
| 139 | /// assert_eq!(bs.norm_bit_sync_en(), false); | ||
| 140 | /// let bs: BitSync = bs.set_norm_bit_sync_en(true); | ||
| 141 | /// assert_eq!(bs.norm_bit_sync_en(), true); | ||
| 142 | /// let bs: BitSync = bs.set_norm_bit_sync_en(false); | ||
| 143 | /// assert_eq!(bs.norm_bit_sync_en(), false); | ||
| 144 | /// ``` | ||
| 145 | pub const fn norm_bit_sync_en(&self) -> bool { | ||
| 146 | self.val & (1 << 4) != 0 | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | impl From<BitSync> for u8 { | ||
| 151 | fn from(bs: BitSync) -> Self { | ||
| 152 | bs.val | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | impl Default for BitSync { | ||
| 157 | fn default() -> Self { | ||
| 158 | Self::RESET | ||
| 159 | } | ||
| 160 | } | ||
diff --git a/embassy-stm32/src/subghz/cad_params.rs b/embassy-stm32/src/subghz/cad_params.rs deleted file mode 100644 index 1d90ff706..000000000 --- a/embassy-stm32/src/subghz/cad_params.rs +++ /dev/null | |||
| @@ -1,230 +0,0 @@ | |||
| 1 | use super::Timeout; | ||
| 2 | |||
| 3 | /// Number of symbols used for channel activity detection scans. | ||
| 4 | /// | ||
| 5 | /// Argument of [`CadParams::set_num_symbol`]. | ||
| 6 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | #[repr(u8)] | ||
| 9 | pub enum NbCadSymbol { | ||
| 10 | /// 1 symbol. | ||
| 11 | S1 = 0x0, | ||
| 12 | /// 2 symbols. | ||
| 13 | S2 = 0x1, | ||
| 14 | /// 4 symbols. | ||
| 15 | S4 = 0x2, | ||
| 16 | /// 8 symbols. | ||
| 17 | S8 = 0x3, | ||
| 18 | /// 16 symbols. | ||
| 19 | S16 = 0x4, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Mode to enter after a channel activity detection scan is finished. | ||
| 23 | /// | ||
| 24 | /// Argument of [`CadParams::set_exit_mode`]. | ||
| 25 | #[derive(Debug, PartialEq, Eq)] | ||
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 27 | #[repr(u8)] | ||
| 28 | pub enum ExitMode { | ||
| 29 | /// Standby with RC 13 MHz mode entry after CAD. | ||
| 30 | Standby = 0, | ||
| 31 | /// Standby with RC 13 MHz mode after CAD if no LoRa symbol is detected | ||
| 32 | /// during the CAD scan. | ||
| 33 | /// If a LoRa symbol is detected, the sub-GHz radio stays in RX mode | ||
| 34 | /// until a packet is received or until the CAD timeout is reached. | ||
| 35 | StandbyLoRa = 1, | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Channel activity detection (CAD) parameters. | ||
| 39 | /// | ||
| 40 | /// Argument of [`set_cad_params`]. | ||
| 41 | /// | ||
| 42 | /// # Recommended CAD settings | ||
| 43 | /// | ||
| 44 | /// This is taken directly from the datasheet. | ||
| 45 | /// | ||
| 46 | /// "The correct values selected in the table below must be carefully tested to | ||
| 47 | /// ensure a good detection at sensitivity level and to limit the number of | ||
| 48 | /// false detections" | ||
| 49 | /// | ||
| 50 | /// | SF (Spreading Factor) | [`set_det_peak`] | [`set_det_min`] | | ||
| 51 | /// |-----------------------|------------------|-----------------| | ||
| 52 | /// | 5 | 0x18 | 0x10 | | ||
| 53 | /// | 6 | 0x19 | 0x10 | | ||
| 54 | /// | 7 | 0x20 | 0x10 | | ||
| 55 | /// | 8 | 0x21 | 0x10 | | ||
| 56 | /// | 9 | 0x22 | 0x10 | | ||
| 57 | /// | 10 | 0x23 | 0x10 | | ||
| 58 | /// | 11 | 0x24 | 0x10 | | ||
| 59 | /// | 12 | 0x25 | 0x10 | | ||
| 60 | /// | ||
| 61 | /// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params | ||
| 62 | /// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak | ||
| 63 | /// [`set_det_min`]: crate::subghz::CadParams::set_det_min | ||
| 64 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 65 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 66 | pub struct CadParams { | ||
| 67 | buf: [u8; 8], | ||
| 68 | } | ||
| 69 | |||
| 70 | impl CadParams { | ||
| 71 | /// Create a new `CadParams`. | ||
| 72 | /// | ||
| 73 | /// This is the same as `default`, but in a `const` function. | ||
| 74 | /// | ||
| 75 | /// # Example | ||
| 76 | /// | ||
| 77 | /// ``` | ||
| 78 | /// use stm32wlxx_hal::subghz::CadParams; | ||
| 79 | /// | ||
| 80 | /// const CAD_PARAMS: CadParams = CadParams::new(); | ||
| 81 | /// assert_eq!(CAD_PARAMS, CadParams::default()); | ||
| 82 | /// ``` | ||
| 83 | pub const fn new() -> CadParams { | ||
| 84 | CadParams { | ||
| 85 | buf: [super::OpCode::SetCadParams as u8, 0, 0, 0, 0, 0, 0, 0], | ||
| 86 | } | ||
| 87 | .set_num_symbol(NbCadSymbol::S1) | ||
| 88 | .set_det_peak(0x18) | ||
| 89 | .set_det_min(0x10) | ||
| 90 | .set_exit_mode(ExitMode::Standby) | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Number of symbols used for a CAD scan. | ||
| 94 | /// | ||
| 95 | /// # Example | ||
| 96 | /// | ||
| 97 | /// Set the number of symbols to 4. | ||
| 98 | /// | ||
| 99 | /// ``` | ||
| 100 | /// use stm32wlxx_hal::subghz::{CadParams, NbCadSymbol}; | ||
| 101 | /// | ||
| 102 | /// const CAD_PARAMS: CadParams = CadParams::new().set_num_symbol(NbCadSymbol::S4); | ||
| 103 | /// # assert_eq!(CAD_PARAMS.as_slice()[1], 0x2); | ||
| 104 | /// ``` | ||
| 105 | #[must_use = "set_num_symbol returns a modified CadParams"] | ||
| 106 | pub const fn set_num_symbol(mut self, nb: NbCadSymbol) -> CadParams { | ||
| 107 | self.buf[1] = nb as u8; | ||
| 108 | self | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Used with [`set_det_min`] to correlate the LoRa symbol. | ||
| 112 | /// | ||
| 113 | /// See the table in [`CadParams`] docs for recommended values. | ||
| 114 | /// | ||
| 115 | /// # Example | ||
| 116 | /// | ||
| 117 | /// Setting the recommended value for a spreading factor of 7. | ||
| 118 | /// | ||
| 119 | /// ``` | ||
| 120 | /// use stm32wlxx_hal::subghz::CadParams; | ||
| 121 | /// | ||
| 122 | /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x20).set_det_min(0x10); | ||
| 123 | /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x20); | ||
| 124 | /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10); | ||
| 125 | /// ``` | ||
| 126 | /// | ||
| 127 | /// [`set_det_min`]: crate::subghz::CadParams::set_det_min | ||
| 128 | #[must_use = "set_det_peak returns a modified CadParams"] | ||
| 129 | pub const fn set_det_peak(mut self, peak: u8) -> CadParams { | ||
| 130 | self.buf[2] = peak; | ||
| 131 | self | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Used with [`set_det_peak`] to correlate the LoRa symbol. | ||
| 135 | /// | ||
| 136 | /// See the table in [`CadParams`] docs for recommended values. | ||
| 137 | /// | ||
| 138 | /// # Example | ||
| 139 | /// | ||
| 140 | /// Setting the recommended value for a spreading factor of 6. | ||
| 141 | /// | ||
| 142 | /// ``` | ||
| 143 | /// use stm32wlxx_hal::subghz::CadParams; | ||
| 144 | /// | ||
| 145 | /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x18).set_det_min(0x10); | ||
| 146 | /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x18); | ||
| 147 | /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10); | ||
| 148 | /// ``` | ||
| 149 | /// | ||
| 150 | /// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak | ||
| 151 | #[must_use = "set_det_min returns a modified CadParams"] | ||
| 152 | pub const fn set_det_min(mut self, min: u8) -> CadParams { | ||
| 153 | self.buf[3] = min; | ||
| 154 | self | ||
| 155 | } | ||
| 156 | |||
| 157 | /// Mode to enter after a channel activity detection scan is finished. | ||
| 158 | /// | ||
| 159 | /// # Example | ||
| 160 | /// | ||
| 161 | /// ``` | ||
| 162 | /// use stm32wlxx_hal::subghz::{CadParams, ExitMode}; | ||
| 163 | /// | ||
| 164 | /// const CAD_PARAMS: CadParams = CadParams::new().set_exit_mode(ExitMode::Standby); | ||
| 165 | /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x00); | ||
| 166 | /// # assert_eq!(CAD_PARAMS.set_exit_mode(ExitMode::StandbyLoRa).as_slice()[4], 0x01); | ||
| 167 | /// ``` | ||
| 168 | #[must_use = "set_exit_mode returns a modified CadParams"] | ||
| 169 | pub const fn set_exit_mode(mut self, mode: ExitMode) -> CadParams { | ||
| 170 | self.buf[4] = mode as u8; | ||
| 171 | self | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Set the timeout. | ||
| 175 | /// | ||
| 176 | /// This is only used with [`ExitMode::StandbyLoRa`]. | ||
| 177 | /// | ||
| 178 | /// # Example | ||
| 179 | /// | ||
| 180 | /// ``` | ||
| 181 | /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, Timeout}; | ||
| 182 | /// | ||
| 183 | /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456); | ||
| 184 | /// const CAD_PARAMS: CadParams = CadParams::new() | ||
| 185 | /// .set_exit_mode(ExitMode::StandbyLoRa) | ||
| 186 | /// .set_timeout(TIMEOUT); | ||
| 187 | /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x01); | ||
| 188 | /// # assert_eq!(CAD_PARAMS.as_slice()[5], 0x12); | ||
| 189 | /// # assert_eq!(CAD_PARAMS.as_slice()[6], 0x34); | ||
| 190 | /// # assert_eq!(CAD_PARAMS.as_slice()[7], 0x56); | ||
| 191 | /// ``` | ||
| 192 | #[must_use = "set_timeout returns a modified CadParams"] | ||
| 193 | pub const fn set_timeout(mut self, to: Timeout) -> CadParams { | ||
| 194 | let to_bytes: [u8; 3] = to.as_bytes(); | ||
| 195 | self.buf[5] = to_bytes[0]; | ||
| 196 | self.buf[6] = to_bytes[1]; | ||
| 197 | self.buf[7] = to_bytes[2]; | ||
| 198 | self | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Extracts a slice containing the packet. | ||
| 202 | /// | ||
| 203 | /// # Example | ||
| 204 | /// | ||
| 205 | /// ``` | ||
| 206 | /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, NbCadSymbol, Timeout}; | ||
| 207 | /// | ||
| 208 | /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456); | ||
| 209 | /// const CAD_PARAMS: CadParams = CadParams::new() | ||
| 210 | /// .set_num_symbol(NbCadSymbol::S4) | ||
| 211 | /// .set_det_peak(0x18) | ||
| 212 | /// .set_det_min(0x10) | ||
| 213 | /// .set_exit_mode(ExitMode::StandbyLoRa) | ||
| 214 | /// .set_timeout(TIMEOUT); | ||
| 215 | /// | ||
| 216 | /// assert_eq!( | ||
| 217 | /// CAD_PARAMS.as_slice(), | ||
| 218 | /// &[0x88, 0x02, 0x18, 0x10, 0x01, 0x12, 0x34, 0x56] | ||
| 219 | /// ); | ||
| 220 | /// ``` | ||
| 221 | pub const fn as_slice(&self) -> &[u8] { | ||
| 222 | &self.buf | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | impl Default for CadParams { | ||
| 227 | fn default() -> Self { | ||
| 228 | Self::new() | ||
| 229 | } | ||
| 230 | } | ||
diff --git a/embassy-stm32/src/subghz/calibrate.rs b/embassy-stm32/src/subghz/calibrate.rs deleted file mode 100644 index f94538f86..000000000 --- a/embassy-stm32/src/subghz/calibrate.rs +++ /dev/null | |||
| @@ -1,122 +0,0 @@ | |||
| 1 | /// Image calibration. | ||
| 2 | /// | ||
| 3 | /// Argument of [`calibrate_image`]. | ||
| 4 | /// | ||
| 5 | /// [`calibrate_image`]: crate::subghz::SubGhz::calibrate_image | ||
| 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct CalibrateImage(pub(crate) u8, pub(crate) u8); | ||
| 9 | |||
| 10 | impl CalibrateImage { | ||
| 11 | /// Image calibration for the 430 - 440 MHz ISM band. | ||
| 12 | pub const ISM_430_440: CalibrateImage = CalibrateImage(0x6B, 0x6F); | ||
| 13 | |||
| 14 | /// Image calibration for the 470 - 510 MHz ISM band. | ||
| 15 | pub const ISM_470_510: CalibrateImage = CalibrateImage(0x75, 0x81); | ||
| 16 | |||
| 17 | /// Image calibration for the 779 - 787 MHz ISM band. | ||
| 18 | pub const ISM_779_787: CalibrateImage = CalibrateImage(0xC1, 0xC5); | ||
| 19 | |||
| 20 | /// Image calibration for the 863 - 870 MHz ISM band. | ||
| 21 | pub const ISM_863_870: CalibrateImage = CalibrateImage(0xD7, 0xDB); | ||
| 22 | |||
| 23 | /// Image calibration for the 902 - 928 MHz ISM band. | ||
| 24 | pub const ISM_902_928: CalibrateImage = CalibrateImage(0xE1, 0xE9); | ||
| 25 | |||
| 26 | /// Create a new `CalibrateImage` structure from raw values. | ||
| 27 | /// | ||
| 28 | /// # Example | ||
| 29 | /// | ||
| 30 | /// ``` | ||
| 31 | /// use stm32wlxx_hal::subghz::CalibrateImage; | ||
| 32 | /// | ||
| 33 | /// const CAL: CalibrateImage = CalibrateImage::new(0xE1, 0xE9); | ||
| 34 | /// assert_eq!(CAL, CalibrateImage::ISM_902_928); | ||
| 35 | /// ``` | ||
| 36 | pub const fn new(f1: u8, f2: u8) -> CalibrateImage { | ||
| 37 | CalibrateImage(f1, f2) | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Create a new `CalibrateImage` structure from two frequencies. | ||
| 41 | /// | ||
| 42 | /// # Arguments | ||
| 43 | /// | ||
| 44 | /// The units for `freq1` and `freq2` are in MHz. | ||
| 45 | /// | ||
| 46 | /// # Panics | ||
| 47 | /// | ||
| 48 | /// * Panics if `freq1` is less than `freq2`. | ||
| 49 | /// * Panics if `freq1` or `freq2` is not a multiple of 4MHz. | ||
| 50 | /// * Panics if `freq1` or `freq2` is greater than `1020`. | ||
| 51 | /// | ||
| 52 | /// # Example | ||
| 53 | /// | ||
| 54 | /// Create an image calibration for the 430 - 440 MHz ISM band. | ||
| 55 | /// | ||
| 56 | /// ``` | ||
| 57 | /// use stm32wlxx_hal::subghz::CalibrateImage; | ||
| 58 | /// | ||
| 59 | /// let cal: CalibrateImage = CalibrateImage::from_freq(428, 444); | ||
| 60 | /// assert_eq!(cal, CalibrateImage::ISM_430_440); | ||
| 61 | /// ``` | ||
| 62 | pub fn from_freq(freq1: u16, freq2: u16) -> CalibrateImage { | ||
| 63 | assert!(freq2 >= freq1); | ||
| 64 | assert_eq!(freq1 % 4, 0); | ||
| 65 | assert_eq!(freq2 % 4, 0); | ||
| 66 | assert!(freq1 <= 1020); | ||
| 67 | assert!(freq2 <= 1020); | ||
| 68 | CalibrateImage((freq1 / 4) as u8, (freq2 / 4) as u8) | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | impl Default for CalibrateImage { | ||
| 73 | fn default() -> Self { | ||
| 74 | CalibrateImage::new(0xE1, 0xE9) | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Block calibration. | ||
| 79 | /// | ||
| 80 | /// Argument of [`calibrate`]. | ||
| 81 | /// | ||
| 82 | /// [`calibrate`]: crate::subghz::SubGhz::calibrate | ||
| 83 | #[derive(PartialEq, Eq, Debug, Clone, Copy)] | ||
| 84 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 85 | #[repr(u8)] | ||
| 86 | pub enum Calibrate { | ||
| 87 | /// Image calibration | ||
| 88 | Image = 1 << 6, | ||
| 89 | /// RF-ADC bulk P calibration | ||
| 90 | AdcBulkP = 1 << 5, | ||
| 91 | /// RF-ADC bulk N calibration | ||
| 92 | AdcBulkN = 1 << 4, | ||
| 93 | /// RF-ADC pulse calibration | ||
| 94 | AdcPulse = 1 << 3, | ||
| 95 | /// RF-PLL calibration | ||
| 96 | Pll = 1 << 2, | ||
| 97 | /// Sub-GHz radio RC 13 MHz calibration | ||
| 98 | Rc13M = 1 << 1, | ||
| 99 | /// Sub-GHz radio RC 64 kHz calibration | ||
| 100 | Rc64K = 1, | ||
| 101 | } | ||
| 102 | |||
| 103 | impl Calibrate { | ||
| 104 | /// Get the bitmask for the block calibration. | ||
| 105 | /// | ||
| 106 | /// # Example | ||
| 107 | /// | ||
| 108 | /// ``` | ||
| 109 | /// use stm32wlxx_hal::subghz::Calibrate; | ||
| 110 | /// | ||
| 111 | /// assert_eq!(Calibrate::Image.mask(), 0b0100_0000); | ||
| 112 | /// assert_eq!(Calibrate::AdcBulkP.mask(), 0b0010_0000); | ||
| 113 | /// assert_eq!(Calibrate::AdcBulkN.mask(), 0b0001_0000); | ||
| 114 | /// assert_eq!(Calibrate::AdcPulse.mask(), 0b0000_1000); | ||
| 115 | /// assert_eq!(Calibrate::Pll.mask(), 0b0000_0100); | ||
| 116 | /// assert_eq!(Calibrate::Rc13M.mask(), 0b0000_0010); | ||
| 117 | /// assert_eq!(Calibrate::Rc64K.mask(), 0b0000_0001); | ||
| 118 | /// ``` | ||
| 119 | pub const fn mask(self) -> u8 { | ||
| 120 | self as u8 | ||
| 121 | } | ||
| 122 | } | ||
diff --git a/embassy-stm32/src/subghz/fallback_mode.rs b/embassy-stm32/src/subghz/fallback_mode.rs deleted file mode 100644 index 50ec592f5..000000000 --- a/embassy-stm32/src/subghz/fallback_mode.rs +++ /dev/null | |||
| @@ -1,37 +0,0 @@ | |||
| 1 | /// Fallback mode after successful packet transmission or packet reception. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_tx_rx_fallback_mode`]. | ||
| 4 | /// | ||
| 5 | /// [`set_tx_rx_fallback_mode`]: crate::subghz::SubGhz::set_tx_rx_fallback_mode. | ||
| 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | #[repr(u8)] | ||
| 9 | pub enum FallbackMode { | ||
| 10 | /// Standby mode entry. | ||
| 11 | Standby = 0x20, | ||
| 12 | /// Standby with HSE32 enabled. | ||
| 13 | StandbyHse = 0x30, | ||
| 14 | /// Frequency synthesizer entry. | ||
| 15 | Fs = 0x40, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl From<FallbackMode> for u8 { | ||
| 19 | fn from(fm: FallbackMode) -> Self { | ||
| 20 | fm as u8 | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | impl Default for FallbackMode { | ||
| 25 | /// Default fallback mode after power-on reset. | ||
| 26 | /// | ||
| 27 | /// # Example | ||
| 28 | /// | ||
| 29 | /// ``` | ||
| 30 | /// use stm32wlxx_hal::subghz::FallbackMode; | ||
| 31 | /// | ||
| 32 | /// assert_eq!(FallbackMode::default(), FallbackMode::Standby); | ||
| 33 | /// ``` | ||
| 34 | fn default() -> Self { | ||
| 35 | FallbackMode::Standby | ||
| 36 | } | ||
| 37 | } | ||
diff --git a/embassy-stm32/src/subghz/hse_trim.rs b/embassy-stm32/src/subghz/hse_trim.rs deleted file mode 100644 index edfd52aca..000000000 --- a/embassy-stm32/src/subghz/hse_trim.rs +++ /dev/null | |||
| @@ -1,107 +0,0 @@ | |||
| 1 | use super::ValueError; | ||
| 2 | |||
| 3 | /// HSE32 load capacitor trimming. | ||
| 4 | /// | ||
| 5 | /// Argument of [`set_hse_in_trim`] and [`set_hse_out_trim`]. | ||
| 6 | /// | ||
| 7 | /// [`set_hse_in_trim`]: crate::subghz::SubGhz::set_hse_in_trim | ||
| 8 | /// [`set_hse_out_trim`]: crate::subghz::SubGhz::set_hse_out_trim | ||
| 9 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
| 10 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 11 | pub struct HseTrim { | ||
| 12 | val: u8, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl HseTrim { | ||
| 16 | /// Maximum capacitor value, ~33.4 pF | ||
| 17 | pub const MAX: HseTrim = HseTrim::from_raw(0x2F); | ||
| 18 | |||
| 19 | /// Minimum capacitor value, ~11.3 pF | ||
| 20 | pub const MIN: HseTrim = HseTrim::from_raw(0x00); | ||
| 21 | |||
| 22 | /// Power-on-reset capacitor value, ~20.3 pF | ||
| 23 | /// | ||
| 24 | /// This is the same as `default`. | ||
| 25 | /// | ||
| 26 | /// # Example | ||
| 27 | /// | ||
| 28 | /// ``` | ||
| 29 | /// use stm32wlxx_hal::subghz::HseTrim; | ||
| 30 | /// | ||
| 31 | /// assert_eq!(HseTrim::POR, HseTrim::default()); | ||
| 32 | /// ``` | ||
| 33 | pub const POR: HseTrim = HseTrim::from_raw(0x12); | ||
| 34 | |||
| 35 | /// Create a new [`HseTrim`] structure from a raw value. | ||
| 36 | /// | ||
| 37 | /// Values greater than the maximum of `0x2F` will be set to the maximum. | ||
| 38 | /// | ||
| 39 | /// # Example | ||
| 40 | /// | ||
| 41 | /// ``` | ||
| 42 | /// use stm32wlxx_hal::subghz::HseTrim; | ||
| 43 | /// | ||
| 44 | /// assert_eq!(HseTrim::from_raw(0xFF), HseTrim::MAX); | ||
| 45 | /// assert_eq!(HseTrim::from_raw(0x2F), HseTrim::MAX); | ||
| 46 | /// assert_eq!(HseTrim::from_raw(0x00), HseTrim::MIN); | ||
| 47 | /// ``` | ||
| 48 | pub const fn from_raw(raw: u8) -> HseTrim { | ||
| 49 | if raw > 0x2F { | ||
| 50 | HseTrim { val: 0x2F } | ||
| 51 | } else { | ||
| 52 | HseTrim { val: raw } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Create a HSE trim value from farads. | ||
| 57 | /// | ||
| 58 | /// Values greater than the maximum of 33.4 pF will be set to the maximum. | ||
| 59 | /// Values less than the minimum of 11.3 pF will be set to the minimum. | ||
| 60 | /// | ||
| 61 | /// # Example | ||
| 62 | /// | ||
| 63 | /// ``` | ||
| 64 | /// use stm32wlxx_hal::subghz::HseTrim; | ||
| 65 | /// | ||
| 66 | /// assert!(HseTrim::from_farads(1.0).is_err()); | ||
| 67 | /// assert!(HseTrim::from_farads(1e-12).is_err()); | ||
| 68 | /// assert_eq!(HseTrim::from_farads(20.2e-12), Ok(HseTrim::default())); | ||
| 69 | /// ``` | ||
| 70 | pub fn from_farads(farads: f32) -> Result<HseTrim, ValueError<f32>> { | ||
| 71 | const MAX: f32 = 33.4E-12; | ||
| 72 | const MIN: f32 = 11.3E-12; | ||
| 73 | if farads > MAX { | ||
| 74 | Err(ValueError::too_high(farads, MAX)) | ||
| 75 | } else if farads < MIN { | ||
| 76 | Err(ValueError::too_low(farads, MIN)) | ||
| 77 | } else { | ||
| 78 | Ok(HseTrim::from_raw(((farads - 11.3e-12) / 0.47e-12) as u8)) | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Get the capacitance as farads. | ||
| 83 | /// | ||
| 84 | /// # Example | ||
| 85 | /// | ||
| 86 | /// ``` | ||
| 87 | /// use stm32wlxx_hal::subghz::HseTrim; | ||
| 88 | /// | ||
| 89 | /// assert_eq!((HseTrim::MAX.as_farads() * 10e11) as u8, 33); | ||
| 90 | /// assert_eq!((HseTrim::MIN.as_farads() * 10e11) as u8, 11); | ||
| 91 | /// ``` | ||
| 92 | pub fn as_farads(&self) -> f32 { | ||
| 93 | (self.val as f32) * 0.47E-12 + 11.3E-12 | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl From<HseTrim> for u8 { | ||
| 98 | fn from(ht: HseTrim) -> Self { | ||
| 99 | ht.val | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl Default for HseTrim { | ||
| 104 | fn default() -> Self { | ||
| 105 | Self::POR | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/embassy-stm32/src/subghz/irq.rs b/embassy-stm32/src/subghz/irq.rs deleted file mode 100644 index b56b8ad94..000000000 --- a/embassy-stm32/src/subghz/irq.rs +++ /dev/null | |||
| @@ -1,292 +0,0 @@ | |||
| 1 | /// Interrupt lines. | ||
| 2 | /// | ||
| 3 | /// Argument of [`CfgIrq::irq_enable`] and [`CfgIrq::irq_disable`]. | ||
| 4 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | pub enum IrqLine { | ||
| 7 | /// Global interrupt. | ||
| 8 | Global, | ||
| 9 | /// Interrupt line 1. | ||
| 10 | /// | ||
| 11 | /// This will output to the [`RfIrq0`](crate::gpio::RfIrq0) pin. | ||
| 12 | Line1, | ||
| 13 | /// Interrupt line 2. | ||
| 14 | /// | ||
| 15 | /// This will output to the [`RfIrq1`](crate::gpio::RfIrq1) pin. | ||
| 16 | Line2, | ||
| 17 | /// Interrupt line 3. | ||
| 18 | /// | ||
| 19 | /// This will output to the [`RfIrq2`](crate::gpio::RfIrq2) pin. | ||
| 20 | Line3, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl IrqLine { | ||
| 24 | pub(super) const fn offset(&self) -> usize { | ||
| 25 | match self { | ||
| 26 | IrqLine::Global => 1, | ||
| 27 | IrqLine::Line1 => 3, | ||
| 28 | IrqLine::Line2 => 5, | ||
| 29 | IrqLine::Line3 => 7, | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | /// IRQ bit mapping | ||
| 35 | /// | ||
| 36 | /// See table 37 "IRQ bit mapping and definition" in the reference manual for | ||
| 37 | /// more information. | ||
| 38 | #[repr(u16)] | ||
| 39 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 40 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 41 | pub enum Irq { | ||
| 42 | /// Packet transmission finished. | ||
| 43 | /// | ||
| 44 | /// * Packet type: LoRa and GFSK | ||
| 45 | /// * Operation: TX | ||
| 46 | TxDone = (1 << 0), | ||
| 47 | /// Packet reception finished. | ||
| 48 | /// | ||
| 49 | /// * Packet type: LoRa and GFSK | ||
| 50 | /// * Operation: RX | ||
| 51 | RxDone = (1 << 1), | ||
| 52 | /// Preamble detected. | ||
| 53 | /// | ||
| 54 | /// * Packet type: LoRa and GFSK | ||
| 55 | /// * Operation: RX | ||
| 56 | PreambleDetected = (1 << 2), | ||
| 57 | /// Synchronization word valid. | ||
| 58 | /// | ||
| 59 | /// * Packet type: GFSK | ||
| 60 | /// * Operation: RX | ||
| 61 | SyncDetected = (1 << 3), | ||
| 62 | /// Header valid. | ||
| 63 | /// | ||
| 64 | /// * Packet type: LoRa | ||
| 65 | /// * Operation: RX | ||
| 66 | HeaderValid = (1 << 4), | ||
| 67 | /// Header CRC error. | ||
| 68 | /// | ||
| 69 | /// * Packet type: LoRa | ||
| 70 | /// * Operation: RX | ||
| 71 | HeaderErr = (1 << 5), | ||
| 72 | /// Dual meaning error. | ||
| 73 | /// | ||
| 74 | /// For GFSK RX this indicates a preamble, syncword, address, CRC, or length | ||
| 75 | /// error. | ||
| 76 | /// | ||
| 77 | /// For LoRa RX this indicates a CRC error. | ||
| 78 | Err = (1 << 6), | ||
| 79 | /// Channel activity detection finished. | ||
| 80 | /// | ||
| 81 | /// * Packet type: LoRa | ||
| 82 | /// * Operation: CAD | ||
| 83 | CadDone = (1 << 7), | ||
| 84 | /// Channel activity detected. | ||
| 85 | /// | ||
| 86 | /// * Packet type: LoRa | ||
| 87 | /// * Operation: CAD | ||
| 88 | CadDetected = (1 << 8), | ||
| 89 | /// RX or TX timeout. | ||
| 90 | /// | ||
| 91 | /// * Packet type: LoRa and GFSK | ||
| 92 | /// * Operation: RX and TX | ||
| 93 | Timeout = (1 << 9), | ||
| 94 | } | ||
| 95 | |||
| 96 | impl Irq { | ||
| 97 | /// Get the bitmask for an IRQ. | ||
| 98 | /// | ||
| 99 | /// # Example | ||
| 100 | /// | ||
| 101 | /// ``` | ||
| 102 | /// use stm32wlxx_hal::subghz::Irq; | ||
| 103 | /// | ||
| 104 | /// assert_eq!(Irq::TxDone.mask(), 0x0001); | ||
| 105 | /// assert_eq!(Irq::Timeout.mask(), 0x0200); | ||
| 106 | /// ``` | ||
| 107 | pub const fn mask(self) -> u16 { | ||
| 108 | self as u16 | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Argument for [`set_irq_cfg`]. | ||
| 113 | /// | ||
| 114 | /// [`set_irq_cfg`]: crate::subghz::SubGhz::set_irq_cfg | ||
| 115 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 116 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 117 | pub struct CfgIrq { | ||
| 118 | buf: [u8; 9], | ||
| 119 | } | ||
| 120 | |||
| 121 | impl CfgIrq { | ||
| 122 | /// Create a new `CfgIrq`. | ||
| 123 | /// | ||
| 124 | /// This is the same as `default`, but in a `const` function. | ||
| 125 | /// | ||
| 126 | /// The default value has all interrupts disabled on all lines. | ||
| 127 | /// | ||
| 128 | /// # Example | ||
| 129 | /// | ||
| 130 | /// ``` | ||
| 131 | /// use stm32wlxx_hal::subghz::CfgIrq; | ||
| 132 | /// | ||
| 133 | /// const IRQ_CFG: CfgIrq = CfgIrq::new(); | ||
| 134 | /// ``` | ||
| 135 | pub const fn new() -> CfgIrq { | ||
| 136 | CfgIrq { | ||
| 137 | buf: [ | ||
| 138 | super::OpCode::CfgDioIrq as u8, | ||
| 139 | 0x00, | ||
| 140 | 0x00, | ||
| 141 | 0x00, | ||
| 142 | 0x00, | ||
| 143 | 0x00, | ||
| 144 | 0x00, | ||
| 145 | 0x00, | ||
| 146 | 0x00, | ||
| 147 | ], | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Enable an interrupt. | ||
| 152 | /// | ||
| 153 | /// # Example | ||
| 154 | /// | ||
| 155 | /// ``` | ||
| 156 | /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine}; | ||
| 157 | /// | ||
| 158 | /// const IRQ_CFG: CfgIrq = CfgIrq::new() | ||
| 159 | /// .irq_enable(IrqLine::Global, Irq::TxDone) | ||
| 160 | /// .irq_enable(IrqLine::Global, Irq::Timeout); | ||
| 161 | /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02); | ||
| 162 | /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01); | ||
| 163 | /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00); | ||
| 164 | /// ``` | ||
| 165 | #[must_use = "irq_enable returns a modified CfgIrq"] | ||
| 166 | pub const fn irq_enable(mut self, line: IrqLine, irq: Irq) -> CfgIrq { | ||
| 167 | let mask: u16 = irq as u16; | ||
| 168 | let offset: usize = line.offset(); | ||
| 169 | self.buf[offset] |= ((mask >> 8) & 0xFF) as u8; | ||
| 170 | self.buf[offset + 1] |= (mask & 0xFF) as u8; | ||
| 171 | self | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Enable an interrupt on all lines. | ||
| 175 | /// | ||
| 176 | /// As far as I can tell with empirical testing all IRQ lines need to be | ||
| 177 | /// enabled for the internal interrupt to be pending in the NVIC. | ||
| 178 | /// | ||
| 179 | /// # Example | ||
| 180 | /// | ||
| 181 | /// ``` | ||
| 182 | /// use stm32wlxx_hal::subghz::{CfgIrq, Irq}; | ||
| 183 | /// | ||
| 184 | /// const IRQ_CFG: CfgIrq = CfgIrq::new() | ||
| 185 | /// .irq_enable_all(Irq::TxDone) | ||
| 186 | /// .irq_enable_all(Irq::Timeout); | ||
| 187 | /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02); | ||
| 188 | /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01); | ||
| 189 | /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x02); | ||
| 190 | /// # assert_eq!(IRQ_CFG.as_slice()[4], 0x01); | ||
| 191 | /// # assert_eq!(IRQ_CFG.as_slice()[5], 0x02); | ||
| 192 | /// # assert_eq!(IRQ_CFG.as_slice()[6], 0x01); | ||
| 193 | /// # assert_eq!(IRQ_CFG.as_slice()[7], 0x02); | ||
| 194 | /// # assert_eq!(IRQ_CFG.as_slice()[8], 0x01); | ||
| 195 | /// ``` | ||
| 196 | #[must_use = "irq_enable_all returns a modified CfgIrq"] | ||
| 197 | pub const fn irq_enable_all(mut self, irq: Irq) -> CfgIrq { | ||
| 198 | let mask: [u8; 2] = irq.mask().to_be_bytes(); | ||
| 199 | |||
| 200 | self.buf[1] |= mask[0]; | ||
| 201 | self.buf[2] |= mask[1]; | ||
| 202 | self.buf[3] |= mask[0]; | ||
| 203 | self.buf[4] |= mask[1]; | ||
| 204 | self.buf[5] |= mask[0]; | ||
| 205 | self.buf[6] |= mask[1]; | ||
| 206 | self.buf[7] |= mask[0]; | ||
| 207 | self.buf[8] |= mask[1]; | ||
| 208 | |||
| 209 | self | ||
| 210 | } | ||
| 211 | |||
| 212 | /// Disable an interrupt. | ||
| 213 | /// | ||
| 214 | /// # Example | ||
| 215 | /// | ||
| 216 | /// ``` | ||
| 217 | /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine}; | ||
| 218 | /// | ||
| 219 | /// const IRQ_CFG: CfgIrq = CfgIrq::new() | ||
| 220 | /// .irq_enable(IrqLine::Global, Irq::TxDone) | ||
| 221 | /// .irq_enable(IrqLine::Global, Irq::Timeout) | ||
| 222 | /// .irq_disable(IrqLine::Global, Irq::TxDone) | ||
| 223 | /// .irq_disable(IrqLine::Global, Irq::Timeout); | ||
| 224 | /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x00); | ||
| 225 | /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x00); | ||
| 226 | /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00); | ||
| 227 | /// ``` | ||
| 228 | #[must_use = "irq_disable returns a modified CfgIrq"] | ||
| 229 | pub const fn irq_disable(mut self, line: IrqLine, irq: Irq) -> CfgIrq { | ||
| 230 | let mask: u16 = !(irq as u16); | ||
| 231 | let offset: usize = line.offset(); | ||
| 232 | self.buf[offset] &= ((mask >> 8) & 0xFF) as u8; | ||
| 233 | self.buf[offset + 1] &= (mask & 0xFF) as u8; | ||
| 234 | self | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Disable an interrupt on all lines. | ||
| 238 | /// | ||
| 239 | /// # Example | ||
| 240 | /// | ||
| 241 | /// ``` | ||
| 242 | /// use stm32wlxx_hal::subghz::{CfgIrq, Irq}; | ||
| 243 | /// | ||
| 244 | /// const IRQ_CFG: CfgIrq = CfgIrq::new() | ||
| 245 | /// .irq_enable_all(Irq::TxDone) | ||
| 246 | /// .irq_enable_all(Irq::Timeout) | ||
| 247 | /// .irq_disable_all(Irq::TxDone) | ||
| 248 | /// .irq_disable_all(Irq::Timeout); | ||
| 249 | /// # assert_eq!(IRQ_CFG, CfgIrq::new()); | ||
| 250 | /// ``` | ||
| 251 | #[must_use = "irq_disable_all returns a modified CfgIrq"] | ||
| 252 | pub const fn irq_disable_all(mut self, irq: Irq) -> CfgIrq { | ||
| 253 | let mask: [u8; 2] = (!irq.mask()).to_be_bytes(); | ||
| 254 | |||
| 255 | self.buf[1] &= mask[0]; | ||
| 256 | self.buf[2] &= mask[1]; | ||
| 257 | self.buf[3] &= mask[0]; | ||
| 258 | self.buf[4] &= mask[1]; | ||
| 259 | self.buf[5] &= mask[0]; | ||
| 260 | self.buf[6] &= mask[1]; | ||
| 261 | self.buf[7] &= mask[0]; | ||
| 262 | self.buf[8] &= mask[1]; | ||
| 263 | |||
| 264 | self | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Extracts a slice containing the packet. | ||
| 268 | /// | ||
| 269 | /// # Example | ||
| 270 | /// | ||
| 271 | /// ``` | ||
| 272 | /// use stm32wlxx_hal::subghz::{CfgIrq, Irq}; | ||
| 273 | /// | ||
| 274 | /// const IRQ_CFG: CfgIrq = CfgIrq::new() | ||
| 275 | /// .irq_enable_all(Irq::TxDone) | ||
| 276 | /// .irq_enable_all(Irq::Timeout); | ||
| 277 | /// | ||
| 278 | /// assert_eq!( | ||
| 279 | /// IRQ_CFG.as_slice(), | ||
| 280 | /// &[0x08, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01] | ||
| 281 | /// ); | ||
| 282 | /// ``` | ||
| 283 | pub const fn as_slice(&self) -> &[u8] { | ||
| 284 | &self.buf | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | impl Default for CfgIrq { | ||
| 289 | fn default() -> Self { | ||
| 290 | Self::new() | ||
| 291 | } | ||
| 292 | } | ||
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 2c163104e..000000000 --- a/embassy-stm32/src/subghz/lora_sync_word.rs +++ /dev/null | |||
| @@ -1,20 +0,0 @@ | |||
| 1 | /// LoRa synchronization word. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_lora_sync_word`][crate::subghz::SubGhz::set_lora_sync_word]. | ||
| 4 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | pub enum LoRaSyncWord { | ||
| 7 | /// LoRa private network. | ||
| 8 | Private, | ||
| 9 | /// LoRa public network. | ||
| 10 | Public, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl LoRaSyncWord { | ||
| 14 | pub(crate) const fn bytes(self) -> [u8; 2] { | ||
| 15 | match self { | ||
| 16 | LoRaSyncWord::Private => [0x14, 0x24], | ||
| 17 | LoRaSyncWord::Public => [0x34, 0x44], | ||
| 18 | } | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs deleted file mode 100644 index cd566ba24..000000000 --- a/embassy-stm32/src/subghz/mod.rs +++ /dev/null | |||
| @@ -1,1004 +0,0 @@ | |||
| 1 | //! Sub-GHz radio operating in the 150 - 960 MHz ISM band | ||
| 2 | //! | ||
| 3 | //! The main radio type is [`SubGhz`]. | ||
| 4 | //! | ||
| 5 | //! ## LoRa user notice | ||
| 6 | //! | ||
| 7 | //! The Sub-GHz radio may have an undocumented erratum, see this ST community | ||
| 8 | //! post for more information: [link] | ||
| 9 | //! | ||
| 10 | //! [link]: https://community.st.com/s/question/0D53W00000hR8kpSAC/stm32wl55-erratum-clairification | ||
| 11 | //! | ||
| 12 | //! NOTE: This HAL is based on https://github.com/newAM/stm32wl-hal, but adopted for use with the stm32-metapac | ||
| 13 | //! and SPI HALs. | ||
| 14 | |||
| 15 | mod bit_sync; | ||
| 16 | mod cad_params; | ||
| 17 | mod calibrate; | ||
| 18 | mod fallback_mode; | ||
| 19 | mod hse_trim; | ||
| 20 | mod irq; | ||
| 21 | mod lora_sync_word; | ||
| 22 | mod mod_params; | ||
| 23 | mod ocp; | ||
| 24 | mod op_error; | ||
| 25 | mod pa_config; | ||
| 26 | mod packet_params; | ||
| 27 | mod packet_status; | ||
| 28 | mod packet_type; | ||
| 29 | mod pkt_ctrl; | ||
| 30 | mod pmode; | ||
| 31 | mod pwr_ctrl; | ||
| 32 | mod reg_mode; | ||
| 33 | mod rf_frequency; | ||
| 34 | mod rx_timeout_stop; | ||
| 35 | mod sleep_cfg; | ||
| 36 | mod smps; | ||
| 37 | mod standby_clk; | ||
| 38 | mod stats; | ||
| 39 | mod status; | ||
| 40 | mod tcxo_mode; | ||
| 41 | mod timeout; | ||
| 42 | mod tx_params; | ||
| 43 | mod value_error; | ||
| 44 | |||
| 45 | pub use bit_sync::BitSync; | ||
| 46 | pub use cad_params::{CadParams, ExitMode, NbCadSymbol}; | ||
| 47 | pub use calibrate::{Calibrate, CalibrateImage}; | ||
| 48 | use embassy_hal_common::ratio::Ratio; | ||
| 49 | pub use fallback_mode::FallbackMode; | ||
| 50 | pub use hse_trim::HseTrim; | ||
| 51 | pub use irq::{CfgIrq, Irq, IrqLine}; | ||
| 52 | pub use lora_sync_word::LoRaSyncWord; | ||
| 53 | pub use mod_params::{ | ||
| 54 | BpskModParams, CodingRate, FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape, LoRaBandwidth, | ||
| 55 | LoRaModParams, SpreadingFactor, | ||
| 56 | }; | ||
| 57 | pub use ocp::Ocp; | ||
| 58 | pub use op_error::OpError; | ||
| 59 | pub use pa_config::{PaConfig, PaSel}; | ||
| 60 | pub use packet_params::{ | ||
| 61 | AddrComp, BpskPacketParams, CrcType, GenericPacketParams, HeaderType, LoRaPacketParams, PreambleDetection, | ||
| 62 | }; | ||
| 63 | pub use packet_status::{FskPacketStatus, LoRaPacketStatus}; | ||
| 64 | pub use packet_type::PacketType; | ||
| 65 | pub use pkt_ctrl::{InfSeqSel, PktCtrl}; | ||
| 66 | pub use pmode::PMode; | ||
| 67 | pub use pwr_ctrl::{CurrentLim, PwrCtrl}; | ||
| 68 | pub use reg_mode::RegMode; | ||
| 69 | pub use rf_frequency::RfFreq; | ||
| 70 | pub use rx_timeout_stop::RxTimeoutStop; | ||
| 71 | pub use sleep_cfg::{SleepCfg, Startup}; | ||
| 72 | pub use smps::SmpsDrv; | ||
| 73 | pub use standby_clk::StandbyClk; | ||
| 74 | pub use stats::{FskStats, LoRaStats, Stats}; | ||
| 75 | pub use status::{CmdStatus, Status, StatusMode}; | ||
| 76 | pub use tcxo_mode::{TcxoMode, TcxoTrim}; | ||
| 77 | pub use timeout::Timeout; | ||
| 78 | pub use tx_params::{RampTime, TxParams}; | ||
| 79 | pub use value_error::ValueError; | ||
| 80 | |||
| 81 | use crate::dma::NoDma; | ||
| 82 | use crate::peripherals::SUBGHZSPI; | ||
| 83 | use crate::rcc::sealed::RccPeripheral; | ||
| 84 | use crate::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; | ||
| 85 | use crate::time::Hertz; | ||
| 86 | use crate::{pac, Peripheral}; | ||
| 87 | |||
| 88 | /// Passthrough for SPI errors (for now) | ||
| 89 | pub type Error = crate::spi::Error; | ||
| 90 | |||
| 91 | struct Nss { | ||
| 92 | _priv: (), | ||
| 93 | } | ||
| 94 | |||
| 95 | impl Nss { | ||
| 96 | #[inline(always)] | ||
| 97 | pub fn new() -> Nss { | ||
| 98 | Self::clear(); | ||
| 99 | Nss { _priv: () } | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Clear NSS, enabling SPI transactions | ||
| 103 | #[inline(always)] | ||
| 104 | fn clear() { | ||
| 105 | let pwr = pac::PWR; | ||
| 106 | unsafe { | ||
| 107 | pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW)); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Set NSS, disabling SPI transactions | ||
| 112 | #[inline(always)] | ||
| 113 | fn set() { | ||
| 114 | let pwr = pac::PWR; | ||
| 115 | unsafe { | ||
| 116 | pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH)); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | impl Drop for Nss { | ||
| 122 | fn drop(&mut self) { | ||
| 123 | Self::set() | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Wakeup the radio from sleep mode. | ||
| 128 | /// | ||
| 129 | /// # Safety | ||
| 130 | /// | ||
| 131 | /// 1. This must not be called when the SubGHz radio is in use. | ||
| 132 | /// 2. This must not be called when the SubGHz SPI bus is in use. | ||
| 133 | /// | ||
| 134 | /// # Example | ||
| 135 | /// | ||
| 136 | /// See [`SubGhz::set_sleep`] | ||
| 137 | #[inline] | ||
| 138 | unsafe fn wakeup() { | ||
| 139 | Nss::clear(); | ||
| 140 | // RM0453 rev 2 page 171 section 5.7.2 "Sleep mode" | ||
| 141 | // on a firmware request via the sub-GHz radio SPI NSS signal | ||
| 142 | // (keeping sub-GHz radio SPI NSS low for at least 20 μs) | ||
| 143 | // | ||
| 144 | // I have found this to be a more reliable mechanism for ensuring NSS is | ||
| 145 | // pulled low for long enough to wake the radio. | ||
| 146 | while rfbusys() {} | ||
| 147 | Nss::set(); | ||
| 148 | } | ||
| 149 | |||
| 150 | /// Returns `true` if the radio is busy. | ||
| 151 | /// | ||
| 152 | /// This may not be set immediately after NSS going low. | ||
| 153 | /// | ||
| 154 | /// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more | ||
| 155 | /// details. | ||
| 156 | #[inline] | ||
| 157 | fn rfbusys() -> bool { | ||
| 158 | // safety: atmoic read with no side-effects | ||
| 159 | //unsafe { (*pac::PWR::ptr()).sr2.read().rfbusys().is_busy() } | ||
| 160 | let pwr = pac::PWR; | ||
| 161 | unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } | ||
| 162 | } | ||
| 163 | |||
| 164 | /* | ||
| 165 | /// Returns `true` if the radio is busy or NSS is low. | ||
| 166 | /// | ||
| 167 | /// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more | ||
| 168 | /// details. | ||
| 169 | #[inline] | ||
| 170 | fn rfbusyms() -> bool { | ||
| 171 | let pwr = pac::PWR; | ||
| 172 | unsafe { pwr.sr2().read().rfbusyms() == pac::pwr::vals::Rfbusyms::BUSY } | ||
| 173 | } | ||
| 174 | */ | ||
| 175 | |||
| 176 | /// Sub-GHz radio peripheral | ||
| 177 | pub struct SubGhz<'d, Tx, Rx> { | ||
| 178 | spi: Spi<'d, SUBGHZSPI, Tx, Rx>, | ||
| 179 | } | ||
| 180 | |||
| 181 | impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { | ||
| 182 | fn pulse_radio_reset() { | ||
| 183 | let rcc = pac::RCC; | ||
| 184 | unsafe { | ||
| 185 | rcc.csr().modify(|w| w.set_rfrst(true)); | ||
| 186 | rcc.csr().modify(|w| w.set_rfrst(false)); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | // TODO: This should be replaced with async handling based on IRQ | ||
| 191 | fn poll_not_busy(&self) { | ||
| 192 | let mut count: u32 = 1_000_000; | ||
| 193 | while rfbusys() { | ||
| 194 | count -= 1; | ||
| 195 | if count == 0 { | ||
| 196 | let pwr = pac::PWR; | ||
| 197 | unsafe { | ||
| 198 | panic!( | ||
| 199 | "rfbusys timeout pwr.sr2=0x{:X} pwr.subghzspicr=0x{:X} pwr.cr1=0x{:X}", | ||
| 200 | pwr.sr2().read().0, | ||
| 201 | pwr.subghzspicr().read().0, | ||
| 202 | pwr.cr1().read().0 | ||
| 203 | ); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Create a new sub-GHz radio driver from a peripheral. | ||
| 210 | /// | ||
| 211 | /// This will reset the radio and the SPI bus, and enable the peripheral | ||
| 212 | /// clock. | ||
| 213 | pub fn new( | ||
| 214 | peri: impl Peripheral<P = SUBGHZSPI> + 'd, | ||
| 215 | txdma: impl Peripheral<P = Tx> + 'd, | ||
| 216 | rxdma: impl Peripheral<P = Rx> + 'd, | ||
| 217 | ) -> Self { | ||
| 218 | Self::pulse_radio_reset(); | ||
| 219 | |||
| 220 | // see RM0453 rev 1 section 7.2.13 page 291 | ||
| 221 | // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. | ||
| 222 | // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. | ||
| 223 | let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); | ||
| 224 | let mut config = SpiConfig::default(); | ||
| 225 | config.mode = MODE_0; | ||
| 226 | config.bit_order = BitOrder::MsbFirst; | ||
| 227 | let spi = Spi::new_subghz(peri, txdma, rxdma, clk, config); | ||
| 228 | |||
| 229 | unsafe { wakeup() }; | ||
| 230 | |||
| 231 | SubGhz { spi } | ||
| 232 | } | ||
| 233 | |||
| 234 | pub fn is_busy(&mut self) -> bool { | ||
| 235 | rfbusys() | ||
| 236 | } | ||
| 237 | |||
| 238 | pub fn reset(&mut self) { | ||
| 239 | Self::pulse_radio_reset(); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 244 | fn read(&mut self, opcode: OpCode, data: &mut [u8]) -> Result<(), Error> { | ||
| 245 | self.poll_not_busy(); | ||
| 246 | { | ||
| 247 | let _nss: Nss = Nss::new(); | ||
| 248 | self.spi.blocking_write(&[opcode as u8])?; | ||
| 249 | self.spi.blocking_transfer_in_place(data)?; | ||
| 250 | } | ||
| 251 | self.poll_not_busy(); | ||
| 252 | Ok(()) | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Read one byte from the sub-Ghz radio. | ||
| 256 | fn read_1(&mut self, opcode: OpCode) -> Result<u8, Error> { | ||
| 257 | let mut buf: [u8; 1] = [0; 1]; | ||
| 258 | self.read(opcode, &mut buf)?; | ||
| 259 | Ok(buf[0]) | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Read a fixed number of bytes from the sub-Ghz radio. | ||
| 263 | fn read_n<const N: usize>(&mut self, opcode: OpCode) -> Result<[u8; N], Error> { | ||
| 264 | let mut buf: [u8; N] = [0; N]; | ||
| 265 | self.read(opcode, &mut buf)?; | ||
| 266 | Ok(buf) | ||
| 267 | } | ||
| 268 | |||
| 269 | fn write(&mut self, data: &[u8]) -> Result<(), Error> { | ||
| 270 | self.poll_not_busy(); | ||
| 271 | { | ||
| 272 | let _nss: Nss = Nss::new(); | ||
| 273 | self.spi.blocking_write(data)?; | ||
| 274 | } | ||
| 275 | self.poll_not_busy(); | ||
| 276 | Ok(()) | ||
| 277 | } | ||
| 278 | |||
| 279 | pub fn write_buffer(&mut self, offset: u8, data: &[u8]) -> Result<(), Error> { | ||
| 280 | self.poll_not_busy(); | ||
| 281 | { | ||
| 282 | let _nss: Nss = Nss::new(); | ||
| 283 | self.spi.blocking_write(&[OpCode::WriteBuffer as u8, offset])?; | ||
| 284 | self.spi.blocking_write(data)?; | ||
| 285 | } | ||
| 286 | self.poll_not_busy(); | ||
| 287 | |||
| 288 | Ok(()) | ||
| 289 | } | ||
| 290 | |||
| 291 | /// Read the radio buffer at the given offset. | ||
| 292 | /// | ||
| 293 | /// The offset and length of a received packet is provided by | ||
| 294 | /// [`rx_buffer_status`](Self::rx_buffer_status). | ||
| 295 | pub fn read_buffer(&mut self, offset: u8, buf: &mut [u8]) -> Result<Status, Error> { | ||
| 296 | let mut status_buf: [u8; 1] = [0]; | ||
| 297 | |||
| 298 | self.poll_not_busy(); | ||
| 299 | { | ||
| 300 | let _nss: Nss = Nss::new(); | ||
| 301 | self.spi.blocking_write(&[OpCode::ReadBuffer as u8, offset])?; | ||
| 302 | self.spi.blocking_transfer_in_place(&mut status_buf)?; | ||
| 303 | self.spi.blocking_transfer_in_place(buf)?; | ||
| 304 | } | ||
| 305 | self.poll_not_busy(); | ||
| 306 | |||
| 307 | Ok(status_buf[0].into()) | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | // helper to pack register writes into a single buffer to avoid multiple DMA | ||
| 312 | // transfers | ||
| 313 | macro_rules! wr_reg { | ||
| 314 | [$reg:ident, $($data:expr),+] => { | ||
| 315 | &[ | ||
| 316 | OpCode::WriteRegister as u8, | ||
| 317 | Register::$reg.address().to_be_bytes()[0], | ||
| 318 | Register::$reg.address().to_be_bytes()[1], | ||
| 319 | $($data),+ | ||
| 320 | ] | ||
| 321 | }; | ||
| 322 | } | ||
| 323 | |||
| 324 | // 5.8.2 | ||
| 325 | /// Register access | ||
| 326 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 327 | // register write with variable length data | ||
| 328 | fn write_register(&mut self, register: Register, data: &[u8]) -> Result<(), Error> { | ||
| 329 | let addr: [u8; 2] = register.address().to_be_bytes(); | ||
| 330 | |||
| 331 | self.poll_not_busy(); | ||
| 332 | { | ||
| 333 | let _nss: Nss = Nss::new(); | ||
| 334 | self.spi | ||
| 335 | .blocking_write(&[OpCode::WriteRegister as u8, addr[0], addr[1]])?; | ||
| 336 | self.spi.blocking_write(data)?; | ||
| 337 | } | ||
| 338 | self.poll_not_busy(); | ||
| 339 | |||
| 340 | Ok(()) | ||
| 341 | } | ||
| 342 | |||
| 343 | /// Set the LoRa bit synchronization. | ||
| 344 | pub fn set_bit_sync(&mut self, bs: BitSync) -> Result<(), Error> { | ||
| 345 | self.write(wr_reg![GBSYNC, bs.as_bits()]) | ||
| 346 | } | ||
| 347 | |||
| 348 | /// Set the generic packet control register. | ||
| 349 | pub fn set_pkt_ctrl(&mut self, pkt_ctrl: PktCtrl) -> Result<(), Error> { | ||
| 350 | self.write(wr_reg![GPKTCTL1A, pkt_ctrl.as_bits()]) | ||
| 351 | } | ||
| 352 | |||
| 353 | /// Set the initial value for generic packet whitening. | ||
| 354 | /// | ||
| 355 | /// This sets the first 8 bits, the 9th bit is set with | ||
| 356 | /// [`set_pkt_ctrl`](Self::set_pkt_ctrl). | ||
| 357 | pub fn set_init_whitening(&mut self, init: u8) -> Result<(), Error> { | ||
| 358 | self.write(wr_reg![GWHITEINIRL, init]) | ||
| 359 | } | ||
| 360 | |||
| 361 | /// Set the initial value for generic packet CRC polynomial. | ||
| 362 | pub fn set_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> { | ||
| 363 | let bytes: [u8; 2] = polynomial.to_be_bytes(); | ||
| 364 | self.write(wr_reg![GCRCINIRH, bytes[0], bytes[1]]) | ||
| 365 | } | ||
| 366 | |||
| 367 | /// Set the generic packet CRC polynomial. | ||
| 368 | pub fn set_initial_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> { | ||
| 369 | let bytes: [u8; 2] = polynomial.to_be_bytes(); | ||
| 370 | self.write(wr_reg![GCRCPOLRH, bytes[0], bytes[1]]) | ||
| 371 | } | ||
| 372 | |||
| 373 | /// Set the synchronization word registers. | ||
| 374 | pub fn set_sync_word(&mut self, sync_word: &[u8; 8]) -> Result<(), Error> { | ||
| 375 | self.write_register(Register::GSYNC7, sync_word) | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Set the LoRa synchronization word registers. | ||
| 379 | pub fn set_lora_sync_word(&mut self, sync_word: LoRaSyncWord) -> Result<(), Error> { | ||
| 380 | let bytes: [u8; 2] = sync_word.bytes(); | ||
| 381 | self.write(wr_reg![LSYNCH, bytes[0], bytes[1]]) | ||
| 382 | } | ||
| 383 | |||
| 384 | /// Set the RX gain control. | ||
| 385 | pub fn set_rx_gain(&mut self, pmode: PMode) -> Result<(), Error> { | ||
| 386 | self.write(wr_reg![RXGAINC, pmode as u8]) | ||
| 387 | } | ||
| 388 | |||
| 389 | /// Set the power amplifier over current protection. | ||
| 390 | pub fn set_pa_ocp(&mut self, ocp: Ocp) -> Result<(), Error> { | ||
| 391 | self.write(wr_reg![PAOCP, ocp as u8]) | ||
| 392 | } | ||
| 393 | |||
| 394 | /// Restart the radio RTC. | ||
| 395 | /// | ||
| 396 | /// This is used to workaround an erratum for [`set_rx_duty_cycle`]. | ||
| 397 | /// | ||
| 398 | /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle | ||
| 399 | pub fn restart_rtc(&mut self) -> Result<(), Error> { | ||
| 400 | self.write(wr_reg![RTCCTLR, 0b1]) | ||
| 401 | } | ||
| 402 | |||
| 403 | /// Set the radio real-time-clock period. | ||
| 404 | /// | ||
| 405 | /// This is used to workaround an erratum for [`set_rx_duty_cycle`]. | ||
| 406 | /// | ||
| 407 | /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle | ||
| 408 | pub fn set_rtc_period(&mut self, period: Timeout) -> Result<(), Error> { | ||
| 409 | let tobits: u32 = period.into_bits(); | ||
| 410 | self.write(wr_reg![ | ||
| 411 | RTCPRDR2, | ||
| 412 | (tobits >> 16) as u8, | ||
| 413 | (tobits >> 8) as u8, | ||
| 414 | tobits as u8 | ||
| 415 | ]) | ||
| 416 | } | ||
| 417 | |||
| 418 | /// Set the HSE32 crystal OSC_IN load capacitor trimming. | ||
| 419 | pub fn set_hse_in_trim(&mut self, trim: HseTrim) -> Result<(), Error> { | ||
| 420 | self.write(wr_reg![HSEINTRIM, trim.into()]) | ||
| 421 | } | ||
| 422 | |||
| 423 | /// Set the HSE32 crystal OSC_OUT load capacitor trimming. | ||
| 424 | pub fn set_hse_out_trim(&mut self, trim: HseTrim) -> Result<(), Error> { | ||
| 425 | self.write(wr_reg![HSEOUTTRIM, trim.into()]) | ||
| 426 | } | ||
| 427 | |||
| 428 | /// Set the SMPS clock detection enabled. | ||
| 429 | /// | ||
| 430 | /// SMPS clock detection must be enabled fore enabling the SMPS. | ||
| 431 | pub fn set_smps_clock_det_en(&mut self, en: bool) -> Result<(), Error> { | ||
| 432 | self.write(wr_reg![SMPSC0, (en as u8) << 6]) | ||
| 433 | } | ||
| 434 | |||
| 435 | /// Set the power current limiting. | ||
| 436 | pub fn set_pwr_ctrl(&mut self, pwr_ctrl: PwrCtrl) -> Result<(), Error> { | ||
| 437 | self.write(wr_reg![PC, pwr_ctrl.as_bits()]) | ||
| 438 | } | ||
| 439 | |||
| 440 | /// Set the maximum SMPS drive capability. | ||
| 441 | pub fn set_smps_drv(&mut self, drv: SmpsDrv) -> Result<(), Error> { | ||
| 442 | self.write(wr_reg![SMPSC2, (drv as u8) << 1]) | ||
| 443 | } | ||
| 444 | |||
| 445 | /// Set the node address. | ||
| 446 | /// | ||
| 447 | /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based | ||
| 448 | /// on node address. | ||
| 449 | pub fn set_node_addr(&mut self, addr: u8) -> Result<(), Error> { | ||
| 450 | self.write(wr_reg![NODE, addr]) | ||
| 451 | } | ||
| 452 | |||
| 453 | /// Set the broadcast address. | ||
| 454 | /// | ||
| 455 | /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based | ||
| 456 | /// on broadcast address. | ||
| 457 | pub fn set_broadcast_addr(&mut self, addr: u8) -> Result<(), Error> { | ||
| 458 | self.write(wr_reg![BROADCAST, addr]) | ||
| 459 | } | ||
| 460 | |||
| 461 | /// Set both the broadcast address and node address. | ||
| 462 | /// | ||
| 463 | /// This is a combination of [`set_node_addr`] and [`set_broadcast_addr`] | ||
| 464 | /// in a single SPI transfer. | ||
| 465 | /// | ||
| 466 | /// [`set_node_addr`]: Self::set_node_addr | ||
| 467 | /// [`set_broadcast_addr`]: Self::set_broadcast_addr | ||
| 468 | pub fn set_addrs(&mut self, node: u8, broadcast: u8) -> Result<(), Error> { | ||
| 469 | self.write(wr_reg![NODE, node, broadcast]) | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | // 5.8.3 | ||
| 474 | /// Operating mode commands | ||
| 475 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 476 | /// Put the radio into sleep mode. | ||
| 477 | /// | ||
| 478 | /// This command is only accepted in standby mode. | ||
| 479 | /// The cfg argument allows some optional functions to be maintained | ||
| 480 | /// in sleep mode. | ||
| 481 | /// | ||
| 482 | /// # Safety | ||
| 483 | /// | ||
| 484 | /// 1. After the `set_sleep` command, the sub-GHz radio NSS must not go low | ||
| 485 | /// for 500 μs. | ||
| 486 | /// No reason is provided, the reference manual (RM0453 rev 2) simply | ||
| 487 | /// says "you must". | ||
| 488 | /// 2. The radio cannot be used while in sleep mode. | ||
| 489 | /// 3. The radio must be woken up with [`wakeup`] before resuming use. | ||
| 490 | /// | ||
| 491 | /// # Example | ||
| 492 | /// | ||
| 493 | /// Put the radio into sleep mode. | ||
| 494 | /// | ||
| 495 | /// ```no_run | ||
| 496 | /// # let dp = unsafe { embassy_stm32::pac::Peripherals::steal() }; | ||
| 497 | /// # let mut sg = embassy_stm32::subghz::SubGhz::new(p.SUBGHZSPI, ...); | ||
| 498 | /// use embassy_stm32::{ | ||
| 499 | /// subghz::{wakeup, SleepCfg, StandbyClk}, | ||
| 500 | /// }; | ||
| 501 | /// | ||
| 502 | /// sg.set_standby(StandbyClk::Rc)?; | ||
| 503 | /// unsafe { sg.set_sleep(SleepCfg::default())? }; | ||
| 504 | /// embassy_time::Timer::after(embassy_time::Duration::from_micros(500)).await; | ||
| 505 | /// unsafe { wakeup() }; | ||
| 506 | /// # Ok::<(), embassy_stm32::subghz::Error>(()) | ||
| 507 | /// ``` | ||
| 508 | pub unsafe fn set_sleep(&mut self, cfg: SleepCfg) -> Result<(), Error> { | ||
| 509 | // poll for busy before, but not after | ||
| 510 | // radio idles with busy high while in sleep mode | ||
| 511 | self.poll_not_busy(); | ||
| 512 | { | ||
| 513 | let _nss: Nss = Nss::new(); | ||
| 514 | self.spi.blocking_write(&[OpCode::SetSleep as u8, u8::from(cfg)])?; | ||
| 515 | } | ||
| 516 | Ok(()) | ||
| 517 | } | ||
| 518 | |||
| 519 | /// Put the radio into standby mode. | ||
| 520 | pub fn set_standby(&mut self, standby_clk: StandbyClk) -> Result<(), Error> { | ||
| 521 | self.write(&[OpCode::SetStandby as u8, u8::from(standby_clk)]) | ||
| 522 | } | ||
| 523 | |||
| 524 | /// Put the subghz radio into frequency synthesis mode. | ||
| 525 | /// | ||
| 526 | /// The RF-PLL frequency must be set with [`set_rf_frequency`] before using | ||
| 527 | /// this command. | ||
| 528 | /// | ||
| 529 | /// Check the datasheet for more information, this is a test command but | ||
| 530 | /// I honestly do not see any use for it. Please update this description | ||
| 531 | /// if you know more than I do. | ||
| 532 | /// | ||
| 533 | /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency | ||
| 534 | pub fn set_fs(&mut self) -> Result<(), Error> { | ||
| 535 | self.write(&[OpCode::SetFs.into()]) | ||
| 536 | } | ||
| 537 | |||
| 538 | /// Setup the sub-GHz radio for TX. | ||
| 539 | pub fn set_tx(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 540 | let tobits: u32 = timeout.into_bits(); | ||
| 541 | self.write(&[ | ||
| 542 | OpCode::SetTx.into(), | ||
| 543 | (tobits >> 16) as u8, | ||
| 544 | (tobits >> 8) as u8, | ||
| 545 | tobits as u8, | ||
| 546 | ]) | ||
| 547 | } | ||
| 548 | |||
| 549 | /// Setup the sub-GHz radio for RX. | ||
| 550 | pub fn set_rx(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 551 | let tobits: u32 = timeout.into_bits(); | ||
| 552 | self.write(&[ | ||
| 553 | OpCode::SetRx.into(), | ||
| 554 | (tobits >> 16) as u8, | ||
| 555 | (tobits >> 8) as u8, | ||
| 556 | tobits as u8, | ||
| 557 | ]) | ||
| 558 | } | ||
| 559 | |||
| 560 | /// Allows selection of the receiver event which stops the RX timeout timer. | ||
| 561 | pub fn set_rx_timeout_stop(&mut self, rx_timeout_stop: RxTimeoutStop) -> Result<(), Error> { | ||
| 562 | self.write(&[OpCode::SetStopRxTimerOnPreamble.into(), rx_timeout_stop.into()]) | ||
| 563 | } | ||
| 564 | |||
| 565 | /// Put the radio in non-continuous RX mode. | ||
| 566 | /// | ||
| 567 | /// This command must be sent in Standby mode. | ||
| 568 | /// This command is only functional with FSK and LoRa packet type. | ||
| 569 | /// | ||
| 570 | /// The following steps are performed: | ||
| 571 | /// 1. Save sub-GHz radio configuration. | ||
| 572 | /// 2. Enter Receive mode and listen for a preamble for the specified `rx_period`. | ||
| 573 | /// 3. Upon the detection of a preamble, the `rx_period` timeout is stopped | ||
| 574 | /// and restarted with the value 2 x `rx_period` + `sleep_period`. | ||
| 575 | /// During this new period, the sub-GHz radio looks for the detection of | ||
| 576 | /// a synchronization word when in (G)FSK modulation mode, | ||
| 577 | /// or a header when in LoRa modulation mode. | ||
| 578 | /// 4. If no packet is received during the listen period defined by | ||
| 579 | /// 2 x `rx_period` + `sleep_period`, the sleep mode is entered for a | ||
| 580 | /// duration of `sleep_period`. At the end of the receive period, | ||
| 581 | /// the sub-GHz radio takes some time to save the context before starting | ||
| 582 | /// the sleep period. | ||
| 583 | /// 5. After the sleep period, a new listening period is automatically | ||
| 584 | /// started. The sub-GHz radio restores the sub-GHz radio configuration | ||
| 585 | /// and continuous with step 2. | ||
| 586 | /// | ||
| 587 | /// The listening mode is terminated in one of the following cases: | ||
| 588 | /// * if a packet is received during the listening period: the sub-GHz radio | ||
| 589 | /// issues a [`RxDone`] interrupt and enters standby mode. | ||
| 590 | /// * if [`set_standby`] is sent during the listening period or after the | ||
| 591 | /// sub-GHz has been requested to exit sleep mode by sub-GHz radio SPI NSS | ||
| 592 | /// | ||
| 593 | /// # Erratum | ||
| 594 | /// | ||
| 595 | /// When a preamble is detected the radio should restart the RX timeout | ||
| 596 | /// with a value of 2 × `rx_period` + `sleep_period`. | ||
| 597 | /// Instead the radio erroneously uses `sleep_period`. | ||
| 598 | /// | ||
| 599 | /// To workaround this use [`restart_rtc`] and [`set_rtc_period`] to | ||
| 600 | /// reprogram the radio timeout to 2 × `rx_period` + `sleep_period`. | ||
| 601 | /// | ||
| 602 | /// Use code similar to this in the [`PreambleDetected`] interrupt handler. | ||
| 603 | /// | ||
| 604 | /// ```no_run | ||
| 605 | /// # let rx_period: Timeout = Timeout::from_millis_sat(100); | ||
| 606 | /// # let sleep_period: Timeout = Timeout::from_millis_sat(100); | ||
| 607 | /// # let mut sg = unsafe { stm32wlxx_hal::subghz::SubGhz::steal() }; | ||
| 608 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 609 | /// | ||
| 610 | /// let period: Timeout = rx_period | ||
| 611 | /// .saturating_add(rx_period) | ||
| 612 | /// .saturating_add(sleep_period); | ||
| 613 | /// | ||
| 614 | /// sg.set_rtc_period(period)?; | ||
| 615 | /// sg.restart_rtc()?; | ||
| 616 | /// # Ok::<(), stm32wlxx_hal::subghz::Error>(()) | ||
| 617 | /// ``` | ||
| 618 | /// | ||
| 619 | /// Please read the erratum for more details. | ||
| 620 | /// | ||
| 621 | /// [`PreambleDetected`]: crate::subghz::Irq::PreambleDetected | ||
| 622 | /// [`restart_rtc`]: crate::subghz::SubGhz::restart_rtc | ||
| 623 | /// [`RxDone`]: crate::subghz::Irq::RxDone | ||
| 624 | /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency | ||
| 625 | /// [`set_rtc_period`]: crate::subghz::SubGhz::set_rtc_period | ||
| 626 | /// [`set_standby`]: crate::subghz::SubGhz::set_standby | ||
| 627 | pub fn set_rx_duty_cycle(&mut self, rx_period: Timeout, sleep_period: Timeout) -> Result<(), Error> { | ||
| 628 | let rx_period_bits: u32 = rx_period.into_bits(); | ||
| 629 | let sleep_period_bits: u32 = sleep_period.into_bits(); | ||
| 630 | self.write(&[ | ||
| 631 | OpCode::SetRxDutyCycle.into(), | ||
| 632 | (rx_period_bits >> 16) as u8, | ||
| 633 | (rx_period_bits >> 8) as u8, | ||
| 634 | rx_period_bits as u8, | ||
| 635 | (sleep_period_bits >> 16) as u8, | ||
| 636 | (sleep_period_bits >> 8) as u8, | ||
| 637 | sleep_period_bits as u8, | ||
| 638 | ]) | ||
| 639 | } | ||
| 640 | |||
| 641 | /// Channel Activity Detection (CAD) with LoRa packets. | ||
| 642 | /// | ||
| 643 | /// The channel activity detection (CAD) is a specific LoRa operation mode, | ||
| 644 | /// where the sub-GHz radio searches for a LoRa radio signal. | ||
| 645 | /// After the search is completed, the Standby mode is automatically | ||
| 646 | /// entered, CAD is done and IRQ is generated. | ||
| 647 | /// When a LoRa radio signal is detected, the CAD detected IRQ is also | ||
| 648 | /// generated. | ||
| 649 | /// | ||
| 650 | /// The length of the search must be configured with [`set_cad_params`] | ||
| 651 | /// prior to calling `set_cad`. | ||
| 652 | /// | ||
| 653 | /// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params | ||
| 654 | pub fn set_cad(&mut self) -> Result<(), Error> { | ||
| 655 | self.write(&[OpCode::SetCad.into()]) | ||
| 656 | } | ||
| 657 | |||
| 658 | /// Generate a continuous transmit tone at the RF-PLL frequency. | ||
| 659 | /// | ||
| 660 | /// The sub-GHz radio remains in continuous transmit tone mode until a mode | ||
| 661 | /// configuration command is received. | ||
| 662 | pub fn set_tx_continuous_wave(&mut self) -> Result<(), Error> { | ||
| 663 | self.write(&[OpCode::SetTxContinuousWave as u8]) | ||
| 664 | } | ||
| 665 | |||
| 666 | /// Generate an infinite preamble at the RF-PLL frequency. | ||
| 667 | /// | ||
| 668 | /// The preamble is an alternating 0s and 1s sequence in generic (G)FSK and | ||
| 669 | /// (G)MSK modulations. | ||
| 670 | /// The preamble is symbol 0 in LoRa modulation. | ||
| 671 | /// The sub-GHz radio remains in infinite preamble mode until a mode | ||
| 672 | /// configuration command is received. | ||
| 673 | pub fn set_tx_continuous_preamble(&mut self) -> Result<(), Error> { | ||
| 674 | self.write(&[OpCode::SetTxContinuousPreamble as u8]) | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | // 5.8.4 | ||
| 679 | /// Radio configuration commands | ||
| 680 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 681 | /// Set the packet type (modulation scheme). | ||
| 682 | pub fn set_packet_type(&mut self, packet_type: PacketType) -> Result<(), Error> { | ||
| 683 | self.write(&[OpCode::SetPacketType as u8, packet_type as u8]) | ||
| 684 | } | ||
| 685 | |||
| 686 | /// Get the packet type. | ||
| 687 | pub fn packet_type(&mut self) -> Result<Result<PacketType, u8>, Error> { | ||
| 688 | let pkt_type: [u8; 2] = self.read_n(OpCode::GetPacketType)?; | ||
| 689 | Ok(PacketType::from_raw(pkt_type[1])) | ||
| 690 | } | ||
| 691 | |||
| 692 | /// Set the radio carrier frequency. | ||
| 693 | pub fn set_rf_frequency(&mut self, freq: &RfFreq) -> Result<(), Error> { | ||
| 694 | self.write(freq.as_slice()) | ||
| 695 | } | ||
| 696 | |||
| 697 | /// Set the transmit output power and the PA ramp-up time. | ||
| 698 | pub fn set_tx_params(&mut self, params: &TxParams) -> Result<(), Error> { | ||
| 699 | self.write(params.as_slice()) | ||
| 700 | } | ||
| 701 | |||
| 702 | /// Power amplifier configuration. | ||
| 703 | /// | ||
| 704 | /// Used to customize the maximum output power and efficiency. | ||
| 705 | pub fn set_pa_config(&mut self, pa_config: &PaConfig) -> Result<(), Error> { | ||
| 706 | self.write(pa_config.as_slice()) | ||
| 707 | } | ||
| 708 | |||
| 709 | /// Operating mode to enter after a successful packet transmission or | ||
| 710 | /// packet reception. | ||
| 711 | pub fn set_tx_rx_fallback_mode(&mut self, fm: FallbackMode) -> Result<(), Error> { | ||
| 712 | self.write(&[OpCode::SetTxRxFallbackMode as u8, fm as u8]) | ||
| 713 | } | ||
| 714 | |||
| 715 | /// Set channel activity detection (CAD) parameters. | ||
| 716 | pub fn set_cad_params(&mut self, params: &CadParams) -> Result<(), Error> { | ||
| 717 | self.write(params.as_slice()) | ||
| 718 | } | ||
| 719 | |||
| 720 | /// Set the data buffer base address for the packet handling in TX and RX. | ||
| 721 | /// | ||
| 722 | /// There is a single buffer for both TX and RX. | ||
| 723 | /// The buffer is not memory mapped, it is accessed via the | ||
| 724 | /// [`read_buffer`](SubGhz::read_buffer) and | ||
| 725 | /// [`write_buffer`](SubGhz::write_buffer) methods. | ||
| 726 | pub fn set_buffer_base_address(&mut self, tx: u8, rx: u8) -> Result<(), Error> { | ||
| 727 | self.write(&[OpCode::SetBufferBaseAddress as u8, tx, rx]) | ||
| 728 | } | ||
| 729 | |||
| 730 | /// Set the (G)FSK modulation parameters. | ||
| 731 | pub fn set_fsk_mod_params(&mut self, params: &FskModParams) -> Result<(), Error> { | ||
| 732 | self.write(params.as_slice()) | ||
| 733 | } | ||
| 734 | |||
| 735 | /// Set the LoRa modulation parameters. | ||
| 736 | pub fn set_lora_mod_params(&mut self, params: &LoRaModParams) -> Result<(), Error> { | ||
| 737 | self.write(params.as_slice()) | ||
| 738 | } | ||
| 739 | |||
| 740 | /// Set the BPSK modulation parameters. | ||
| 741 | pub fn set_bpsk_mod_params(&mut self, params: &BpskModParams) -> Result<(), Error> { | ||
| 742 | self.write(params.as_slice()) | ||
| 743 | } | ||
| 744 | |||
| 745 | /// Set the generic (FSK) packet parameters. | ||
| 746 | pub fn set_packet_params(&mut self, params: &GenericPacketParams) -> Result<(), Error> { | ||
| 747 | self.write(params.as_slice()) | ||
| 748 | } | ||
| 749 | |||
| 750 | /// Set the BPSK packet parameters. | ||
| 751 | pub fn set_bpsk_packet_params(&mut self, params: &BpskPacketParams) -> Result<(), Error> { | ||
| 752 | self.write(params.as_slice()) | ||
| 753 | } | ||
| 754 | |||
| 755 | /// Set the LoRa packet parameters. | ||
| 756 | pub fn set_lora_packet_params(&mut self, params: &LoRaPacketParams) -> Result<(), Error> { | ||
| 757 | self.write(params.as_slice()) | ||
| 758 | } | ||
| 759 | |||
| 760 | /// Set the number of LoRa symbols to be received before starting the | ||
| 761 | /// reception of a LoRa packet. | ||
| 762 | /// | ||
| 763 | /// Packet reception is started after `n` + 1 symbols are detected. | ||
| 764 | pub fn set_lora_symb_timeout(&mut self, n: u8) -> Result<(), Error> { | ||
| 765 | self.write(&[OpCode::SetLoRaSymbTimeout.into(), n]) | ||
| 766 | } | ||
| 767 | } | ||
| 768 | |||
| 769 | // 5.8.5 | ||
| 770 | /// Communication status and information commands | ||
| 771 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 772 | /// Get the radio status. | ||
| 773 | /// | ||
| 774 | /// The hardware (or documentation) appears to have many bugs where this | ||
| 775 | /// will return reserved values. | ||
| 776 | /// See this thread in the ST community for details: [link] | ||
| 777 | /// | ||
| 778 | /// [link]: https://community.st.com/s/question/0D53W00000hR9GQSA0/stm32wl55-getstatus-command-returns-reserved-cmdstatus | ||
| 779 | pub fn status(&mut self) -> Result<Status, Error> { | ||
| 780 | Ok(self.read_1(OpCode::GetStatus)?.into()) | ||
| 781 | } | ||
| 782 | |||
| 783 | /// Get the RX buffer status. | ||
| 784 | /// | ||
| 785 | /// The return tuple is (status, payload_length, buffer_pointer). | ||
| 786 | pub fn rx_buffer_status(&mut self) -> Result<(Status, u8, u8), Error> { | ||
| 787 | let data: [u8; 3] = self.read_n(OpCode::GetRxBufferStatus)?; | ||
| 788 | Ok((data[0].into(), data[1], data[2])) | ||
| 789 | } | ||
| 790 | |||
| 791 | /// Returns information on the last received (G)FSK packet. | ||
| 792 | pub fn fsk_packet_status(&mut self) -> Result<FskPacketStatus, Error> { | ||
| 793 | Ok(FskPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?)) | ||
| 794 | } | ||
| 795 | |||
| 796 | /// Returns information on the last received LoRa packet. | ||
| 797 | pub fn lora_packet_status(&mut self) -> Result<LoRaPacketStatus, Error> { | ||
| 798 | Ok(LoRaPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?)) | ||
| 799 | } | ||
| 800 | |||
| 801 | /// Get the instantaneous signal strength during packet reception. | ||
| 802 | /// | ||
| 803 | /// The units are in dbm. | ||
| 804 | pub fn rssi_inst(&mut self) -> Result<(Status, Ratio<i16>), Error> { | ||
| 805 | let data: [u8; 2] = self.read_n(OpCode::GetRssiInst)?; | ||
| 806 | let status: Status = data[0].into(); | ||
| 807 | let rssi: Ratio<i16> = Ratio::new_raw(i16::from(data[1]), -2); | ||
| 808 | |||
| 809 | Ok((status, rssi)) | ||
| 810 | } | ||
| 811 | |||
| 812 | /// (G)FSK packet stats. | ||
| 813 | pub fn fsk_stats(&mut self) -> Result<Stats<FskStats>, Error> { | ||
| 814 | let data: [u8; 7] = self.read_n(OpCode::GetStats)?; | ||
| 815 | Ok(Stats::from_raw_fsk(data)) | ||
| 816 | } | ||
| 817 | |||
| 818 | /// LoRa packet stats. | ||
| 819 | pub fn lora_stats(&mut self) -> Result<Stats<LoRaStats>, Error> { | ||
| 820 | let data: [u8; 7] = self.read_n(OpCode::GetStats)?; | ||
| 821 | Ok(Stats::from_raw_lora(data)) | ||
| 822 | } | ||
| 823 | |||
| 824 | /// Reset the stats as reported in [`lora_stats`](SubGhz::lora_stats) and | ||
| 825 | /// [`fsk_stats`](SubGhz::fsk_stats). | ||
| 826 | pub fn reset_stats(&mut self) -> Result<(), Error> { | ||
| 827 | const RESET_STATS: [u8; 7] = [0x00; 7]; | ||
| 828 | self.write(&RESET_STATS) | ||
| 829 | } | ||
| 830 | } | ||
| 831 | |||
| 832 | // 5.8.6 | ||
| 833 | /// IRQ commands | ||
| 834 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 835 | /// Set the interrupt configuration. | ||
| 836 | pub fn set_irq_cfg(&mut self, cfg: &CfgIrq) -> Result<(), Error> { | ||
| 837 | self.write(cfg.as_slice()) | ||
| 838 | } | ||
| 839 | |||
| 840 | /// Get the IRQ status. | ||
| 841 | pub fn irq_status(&mut self) -> Result<(Status, u16), Error> { | ||
| 842 | let data: [u8; 3] = self.read_n(OpCode::GetIrqStatus)?; | ||
| 843 | let irq_status: u16 = u16::from_be_bytes([data[1], data[2]]); | ||
| 844 | Ok((data[0].into(), irq_status)) | ||
| 845 | } | ||
| 846 | |||
| 847 | /// Clear the IRQ status. | ||
| 848 | pub fn clear_irq_status(&mut self, mask: u16) -> Result<(), Error> { | ||
| 849 | self.write(&[OpCode::ClrIrqStatus as u8, (mask >> 8) as u8, mask as u8]) | ||
| 850 | } | ||
| 851 | } | ||
| 852 | |||
| 853 | // 5.8.7 | ||
| 854 | /// Miscellaneous commands | ||
| 855 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 856 | /// Calibrate one or several blocks at any time when in standby mode. | ||
| 857 | pub fn calibrate(&mut self, cal: u8) -> Result<(), Error> { | ||
| 858 | // bit 7 is reserved and must be kept at reset value. | ||
| 859 | self.write(&[OpCode::Calibrate as u8, cal & 0x7F]) | ||
| 860 | } | ||
| 861 | |||
| 862 | /// Calibrate the image at the given frequencies. | ||
| 863 | /// | ||
| 864 | /// Requires the radio to be in standby mode. | ||
| 865 | pub fn calibrate_image(&mut self, cal: CalibrateImage) -> Result<(), Error> { | ||
| 866 | self.write(&[OpCode::CalibrateImage as u8, cal.0, cal.1]) | ||
| 867 | } | ||
| 868 | |||
| 869 | /// Set the radio power supply. | ||
| 870 | pub fn set_regulator_mode(&mut self, reg_mode: RegMode) -> Result<(), Error> { | ||
| 871 | self.write(&[OpCode::SetRegulatorMode as u8, reg_mode as u8]) | ||
| 872 | } | ||
| 873 | |||
| 874 | /// Get the radio operational errors. | ||
| 875 | pub fn op_error(&mut self) -> Result<(Status, u16), Error> { | ||
| 876 | let data: [u8; 3] = self.read_n(OpCode::GetError)?; | ||
| 877 | Ok((data[0].into(), u16::from_be_bytes([data[1], data[2]]))) | ||
| 878 | } | ||
| 879 | |||
| 880 | /// Clear all errors as reported by [`op_error`](SubGhz::op_error). | ||
| 881 | pub fn clear_error(&mut self) -> Result<(), Error> { | ||
| 882 | self.write(&[OpCode::ClrError as u8, 0x00]) | ||
| 883 | } | ||
| 884 | } | ||
| 885 | |||
| 886 | // 5.8.8 | ||
| 887 | /// Set TCXO mode command | ||
| 888 | impl<'d> SubGhz<'d, NoDma, NoDma> { | ||
| 889 | /// Set the TCXO trim and HSE32 ready timeout. | ||
| 890 | pub fn set_tcxo_mode(&mut self, tcxo_mode: &TcxoMode) -> Result<(), Error> { | ||
| 891 | self.write(tcxo_mode.as_slice()) | ||
| 892 | } | ||
| 893 | } | ||
| 894 | |||
| 895 | /// sub-GHz radio opcodes. | ||
| 896 | /// | ||
| 897 | /// See Table 41 "Sub-GHz radio SPI commands overview" | ||
| 898 | #[repr(u8)] | ||
| 899 | #[derive(Debug, Clone, Copy)] | ||
| 900 | #[allow(dead_code)] | ||
| 901 | pub(crate) enum OpCode { | ||
| 902 | Calibrate = 0x89, | ||
| 903 | CalibrateImage = 0x98, | ||
| 904 | CfgDioIrq = 0x08, | ||
| 905 | ClrError = 0x07, | ||
| 906 | ClrIrqStatus = 0x02, | ||
| 907 | GetError = 0x17, | ||
| 908 | GetIrqStatus = 0x12, | ||
| 909 | GetPacketStatus = 0x14, | ||
| 910 | GetPacketType = 0x11, | ||
| 911 | GetRssiInst = 0x15, | ||
| 912 | GetRxBufferStatus = 0x13, | ||
| 913 | GetStats = 0x10, | ||
| 914 | GetStatus = 0xC0, | ||
| 915 | ReadBuffer = 0x1E, | ||
| 916 | RegRegister = 0x1D, | ||
| 917 | ResetStats = 0x00, | ||
| 918 | SetBufferBaseAddress = 0x8F, | ||
| 919 | SetCad = 0xC5, | ||
| 920 | SetCadParams = 0x88, | ||
| 921 | SetFs = 0xC1, | ||
| 922 | SetLoRaSymbTimeout = 0xA0, | ||
| 923 | SetModulationParams = 0x8B, | ||
| 924 | SetPacketParams = 0x8C, | ||
| 925 | SetPacketType = 0x8A, | ||
| 926 | SetPaConfig = 0x95, | ||
| 927 | SetRegulatorMode = 0x96, | ||
| 928 | SetRfFrequency = 0x86, | ||
| 929 | SetRx = 0x82, | ||
| 930 | SetRxDutyCycle = 0x94, | ||
| 931 | SetSleep = 0x84, | ||
| 932 | SetStandby = 0x80, | ||
| 933 | SetStopRxTimerOnPreamble = 0x9F, | ||
| 934 | SetTcxoMode = 0x97, | ||
| 935 | SetTx = 0x83, | ||
| 936 | SetTxContinuousPreamble = 0xD2, | ||
| 937 | SetTxContinuousWave = 0xD1, | ||
| 938 | SetTxParams = 0x8E, | ||
| 939 | SetTxRxFallbackMode = 0x93, | ||
| 940 | WriteBuffer = 0x0E, | ||
| 941 | WriteRegister = 0x0D, | ||
| 942 | } | ||
| 943 | |||
| 944 | impl From<OpCode> for u8 { | ||
| 945 | fn from(opcode: OpCode) -> Self { | ||
| 946 | opcode as u8 | ||
| 947 | } | ||
| 948 | } | ||
| 949 | |||
| 950 | #[repr(u16)] | ||
| 951 | #[allow(clippy::upper_case_acronyms)] | ||
| 952 | pub(crate) enum Register { | ||
| 953 | /// Generic bit synchronization. | ||
| 954 | GBSYNC = 0x06AC, | ||
| 955 | /// Generic packet control. | ||
| 956 | GPKTCTL1A = 0x06B8, | ||
| 957 | /// Generic whitening. | ||
| 958 | GWHITEINIRL = 0x06B9, | ||
| 959 | /// Generic CRC initial. | ||
| 960 | GCRCINIRH = 0x06BC, | ||
| 961 | /// Generic CRC polynomial. | ||
| 962 | GCRCPOLRH = 0x06BE, | ||
| 963 | /// Generic synchronization word 7. | ||
| 964 | GSYNC7 = 0x06C0, | ||
| 965 | /// Node address. | ||
| 966 | NODE = 0x06CD, | ||
| 967 | /// Broadcast address. | ||
| 968 | BROADCAST = 0x06CE, | ||
| 969 | /// LoRa synchronization word MSB. | ||
| 970 | LSYNCH = 0x0740, | ||
| 971 | /// LoRa synchronization word LSB. | ||
| 972 | #[allow(dead_code)] | ||
| 973 | LSYNCL = 0x0741, | ||
| 974 | /// Receiver gain control. | ||
| 975 | RXGAINC = 0x08AC, | ||
| 976 | /// PA over current protection. | ||
| 977 | PAOCP = 0x08E7, | ||
| 978 | /// RTC control. | ||
| 979 | RTCCTLR = 0x0902, | ||
| 980 | /// RTC period MSB. | ||
| 981 | RTCPRDR2 = 0x0906, | ||
| 982 | /// RTC period mid-byte. | ||
| 983 | #[allow(dead_code)] | ||
| 984 | RTCPRDR1 = 0x0907, | ||
| 985 | /// RTC period LSB. | ||
| 986 | #[allow(dead_code)] | ||
| 987 | RTCPRDR0 = 0x0908, | ||
| 988 | /// HSE32 OSC_IN capacitor trim. | ||
| 989 | HSEINTRIM = 0x0911, | ||
| 990 | /// HSE32 OSC_OUT capacitor trim. | ||
| 991 | HSEOUTTRIM = 0x0912, | ||
| 992 | /// SMPS control 0. | ||
| 993 | SMPSC0 = 0x0916, | ||
| 994 | /// Power control. | ||
| 995 | PC = 0x091A, | ||
| 996 | /// SMPS control 2. | ||
| 997 | SMPSC2 = 0x0923, | ||
| 998 | } | ||
| 999 | |||
| 1000 | impl Register { | ||
| 1001 | pub const fn address(self) -> u16 { | ||
| 1002 | self as u16 | ||
| 1003 | } | ||
| 1004 | } | ||
diff --git a/embassy-stm32/src/subghz/mod_params.rs b/embassy-stm32/src/subghz/mod_params.rs deleted file mode 100644 index d997ae112..000000000 --- a/embassy-stm32/src/subghz/mod_params.rs +++ /dev/null | |||
| @@ -1,1045 +0,0 @@ | |||
| 1 | /// Bandwidth options for [`FskModParams`]. | ||
| 2 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 3 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 4 | pub enum FskBandwidth { | ||
| 5 | /// 4.8 kHz double-sideband | ||
| 6 | Bw4 = 0x1F, | ||
| 7 | /// 5.8 kHz double-sideband | ||
| 8 | Bw5 = 0x17, | ||
| 9 | /// 7.3 kHz double-sideband | ||
| 10 | Bw7 = 0x0F, | ||
| 11 | /// 9.7 kHz double-sideband | ||
| 12 | Bw9 = 0x1E, | ||
| 13 | /// 11.7 kHz double-sideband | ||
| 14 | Bw11 = 0x16, | ||
| 15 | /// 14.6 kHz double-sideband | ||
| 16 | Bw14 = 0x0E, | ||
| 17 | /// 19.5 kHz double-sideband | ||
| 18 | Bw19 = 0x1D, | ||
| 19 | /// 23.4 kHz double-sideband | ||
| 20 | Bw23 = 0x15, | ||
| 21 | /// 29.3 kHz double-sideband | ||
| 22 | Bw29 = 0x0D, | ||
| 23 | /// 39.0 kHz double-sideband | ||
| 24 | Bw39 = 0x1C, | ||
| 25 | /// 46.9 kHz double-sideband | ||
| 26 | Bw46 = 0x14, | ||
| 27 | /// 58.6 kHz double-sideband | ||
| 28 | Bw58 = 0x0C, | ||
| 29 | /// 78.2 kHz double-sideband | ||
| 30 | Bw78 = 0x1B, | ||
| 31 | /// 93.8 kHz double-sideband | ||
| 32 | Bw93 = 0x13, | ||
| 33 | /// 117.3 kHz double-sideband | ||
| 34 | Bw117 = 0x0B, | ||
| 35 | /// 156.2 kHz double-sideband | ||
| 36 | Bw156 = 0x1A, | ||
| 37 | /// 187.2 kHz double-sideband | ||
| 38 | Bw187 = 0x12, | ||
| 39 | /// 234.3 kHz double-sideband | ||
| 40 | Bw234 = 0x0A, | ||
| 41 | /// 312.0 kHz double-sideband | ||
| 42 | Bw312 = 0x19, | ||
| 43 | /// 373.6 kHz double-sideband | ||
| 44 | Bw373 = 0x11, | ||
| 45 | /// 467.0 kHz double-sideband | ||
| 46 | Bw467 = 0x09, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl FskBandwidth { | ||
| 50 | /// Get the bandwidth in hertz. | ||
| 51 | /// | ||
| 52 | /// # Example | ||
| 53 | /// | ||
| 54 | /// ``` | ||
| 55 | /// use stm32wlxx_hal::subghz::FskBandwidth; | ||
| 56 | /// | ||
| 57 | /// assert_eq!(FskBandwidth::Bw4.hertz(), 4_800); | ||
| 58 | /// assert_eq!(FskBandwidth::Bw5.hertz(), 5_800); | ||
| 59 | /// assert_eq!(FskBandwidth::Bw7.hertz(), 7_300); | ||
| 60 | /// assert_eq!(FskBandwidth::Bw9.hertz(), 9_700); | ||
| 61 | /// assert_eq!(FskBandwidth::Bw11.hertz(), 11_700); | ||
| 62 | /// assert_eq!(FskBandwidth::Bw14.hertz(), 14_600); | ||
| 63 | /// assert_eq!(FskBandwidth::Bw19.hertz(), 19_500); | ||
| 64 | /// assert_eq!(FskBandwidth::Bw23.hertz(), 23_400); | ||
| 65 | /// assert_eq!(FskBandwidth::Bw29.hertz(), 29_300); | ||
| 66 | /// assert_eq!(FskBandwidth::Bw39.hertz(), 39_000); | ||
| 67 | /// assert_eq!(FskBandwidth::Bw46.hertz(), 46_900); | ||
| 68 | /// assert_eq!(FskBandwidth::Bw58.hertz(), 58_600); | ||
| 69 | /// assert_eq!(FskBandwidth::Bw78.hertz(), 78_200); | ||
| 70 | /// assert_eq!(FskBandwidth::Bw93.hertz(), 93_800); | ||
| 71 | /// assert_eq!(FskBandwidth::Bw117.hertz(), 117_300); | ||
| 72 | /// assert_eq!(FskBandwidth::Bw156.hertz(), 156_200); | ||
| 73 | /// assert_eq!(FskBandwidth::Bw187.hertz(), 187_200); | ||
| 74 | /// assert_eq!(FskBandwidth::Bw234.hertz(), 234_300); | ||
| 75 | /// assert_eq!(FskBandwidth::Bw312.hertz(), 312_000); | ||
| 76 | /// assert_eq!(FskBandwidth::Bw373.hertz(), 373_600); | ||
| 77 | /// assert_eq!(FskBandwidth::Bw467.hertz(), 467_000); | ||
| 78 | /// ``` | ||
| 79 | pub const fn hertz(&self) -> u32 { | ||
| 80 | match self { | ||
| 81 | FskBandwidth::Bw4 => 4_800, | ||
| 82 | FskBandwidth::Bw5 => 5_800, | ||
| 83 | FskBandwidth::Bw7 => 7_300, | ||
| 84 | FskBandwidth::Bw9 => 9_700, | ||
| 85 | FskBandwidth::Bw11 => 11_700, | ||
| 86 | FskBandwidth::Bw14 => 14_600, | ||
| 87 | FskBandwidth::Bw19 => 19_500, | ||
| 88 | FskBandwidth::Bw23 => 23_400, | ||
| 89 | FskBandwidth::Bw29 => 29_300, | ||
| 90 | FskBandwidth::Bw39 => 39_000, | ||
| 91 | FskBandwidth::Bw46 => 46_900, | ||
| 92 | FskBandwidth::Bw58 => 58_600, | ||
| 93 | FskBandwidth::Bw78 => 78_200, | ||
| 94 | FskBandwidth::Bw93 => 93_800, | ||
| 95 | FskBandwidth::Bw117 => 117_300, | ||
| 96 | FskBandwidth::Bw156 => 156_200, | ||
| 97 | FskBandwidth::Bw187 => 187_200, | ||
| 98 | FskBandwidth::Bw234 => 234_300, | ||
| 99 | FskBandwidth::Bw312 => 312_000, | ||
| 100 | FskBandwidth::Bw373 => 373_600, | ||
| 101 | FskBandwidth::Bw467 => 467_000, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Convert from a raw bit value. | ||
| 106 | /// | ||
| 107 | /// Invalid values will be returned in the `Err` variant of the result. | ||
| 108 | /// | ||
| 109 | /// # Example | ||
| 110 | /// | ||
| 111 | /// ``` | ||
| 112 | /// use stm32wlxx_hal::subghz::FskBandwidth; | ||
| 113 | /// | ||
| 114 | /// assert_eq!(FskBandwidth::from_bits(0x1F), Ok(FskBandwidth::Bw4)); | ||
| 115 | /// assert_eq!(FskBandwidth::from_bits(0x17), Ok(FskBandwidth::Bw5)); | ||
| 116 | /// assert_eq!(FskBandwidth::from_bits(0x0F), Ok(FskBandwidth::Bw7)); | ||
| 117 | /// assert_eq!(FskBandwidth::from_bits(0x1E), Ok(FskBandwidth::Bw9)); | ||
| 118 | /// assert_eq!(FskBandwidth::from_bits(0x16), Ok(FskBandwidth::Bw11)); | ||
| 119 | /// assert_eq!(FskBandwidth::from_bits(0x0E), Ok(FskBandwidth::Bw14)); | ||
| 120 | /// assert_eq!(FskBandwidth::from_bits(0x1D), Ok(FskBandwidth::Bw19)); | ||
| 121 | /// assert_eq!(FskBandwidth::from_bits(0x15), Ok(FskBandwidth::Bw23)); | ||
| 122 | /// assert_eq!(FskBandwidth::from_bits(0x0D), Ok(FskBandwidth::Bw29)); | ||
| 123 | /// assert_eq!(FskBandwidth::from_bits(0x1C), Ok(FskBandwidth::Bw39)); | ||
| 124 | /// assert_eq!(FskBandwidth::from_bits(0x14), Ok(FskBandwidth::Bw46)); | ||
| 125 | /// assert_eq!(FskBandwidth::from_bits(0x0C), Ok(FskBandwidth::Bw58)); | ||
| 126 | /// assert_eq!(FskBandwidth::from_bits(0x1B), Ok(FskBandwidth::Bw78)); | ||
| 127 | /// assert_eq!(FskBandwidth::from_bits(0x13), Ok(FskBandwidth::Bw93)); | ||
| 128 | /// assert_eq!(FskBandwidth::from_bits(0x0B), Ok(FskBandwidth::Bw117)); | ||
| 129 | /// assert_eq!(FskBandwidth::from_bits(0x1A), Ok(FskBandwidth::Bw156)); | ||
| 130 | /// assert_eq!(FskBandwidth::from_bits(0x12), Ok(FskBandwidth::Bw187)); | ||
| 131 | /// assert_eq!(FskBandwidth::from_bits(0x0A), Ok(FskBandwidth::Bw234)); | ||
| 132 | /// assert_eq!(FskBandwidth::from_bits(0x19), Ok(FskBandwidth::Bw312)); | ||
| 133 | /// assert_eq!(FskBandwidth::from_bits(0x11), Ok(FskBandwidth::Bw373)); | ||
| 134 | /// assert_eq!(FskBandwidth::from_bits(0x09), Ok(FskBandwidth::Bw467)); | ||
| 135 | /// assert_eq!(FskBandwidth::from_bits(0x00), Err(0x00)); | ||
| 136 | /// ``` | ||
| 137 | pub const fn from_bits(bits: u8) -> Result<Self, u8> { | ||
| 138 | match bits { | ||
| 139 | 0x1F => Ok(Self::Bw4), | ||
| 140 | 0x17 => Ok(Self::Bw5), | ||
| 141 | 0x0F => Ok(Self::Bw7), | ||
| 142 | 0x1E => Ok(Self::Bw9), | ||
| 143 | 0x16 => Ok(Self::Bw11), | ||
| 144 | 0x0E => Ok(Self::Bw14), | ||
| 145 | 0x1D => Ok(Self::Bw19), | ||
| 146 | 0x15 => Ok(Self::Bw23), | ||
| 147 | 0x0D => Ok(Self::Bw29), | ||
| 148 | 0x1C => Ok(Self::Bw39), | ||
| 149 | 0x14 => Ok(Self::Bw46), | ||
| 150 | 0x0C => Ok(Self::Bw58), | ||
| 151 | 0x1B => Ok(Self::Bw78), | ||
| 152 | 0x13 => Ok(Self::Bw93), | ||
| 153 | 0x0B => Ok(Self::Bw117), | ||
| 154 | 0x1A => Ok(Self::Bw156), | ||
| 155 | 0x12 => Ok(Self::Bw187), | ||
| 156 | 0x0A => Ok(Self::Bw234), | ||
| 157 | 0x19 => Ok(Self::Bw312), | ||
| 158 | 0x11 => Ok(Self::Bw373), | ||
| 159 | 0x09 => Ok(Self::Bw467), | ||
| 160 | x => Err(x), | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | impl Ord for FskBandwidth { | ||
| 166 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||
| 167 | self.hertz().cmp(&other.hertz()) | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | impl PartialOrd for FskBandwidth { | ||
| 172 | fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||
| 173 | Some(self.hertz().cmp(&other.hertz())) | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Pulse shaping options for [`FskModParams`]. | ||
| 178 | #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] | ||
| 179 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 180 | pub enum FskPulseShape { | ||
| 181 | /// No filtering applied. | ||
| 182 | None = 0b00, | ||
| 183 | /// Gaussian BT 0.3 | ||
| 184 | Bt03 = 0x08, | ||
| 185 | /// Gaussian BT 0.5 | ||
| 186 | Bt05 = 0x09, | ||
| 187 | /// Gaussian BT 0.7 | ||
| 188 | Bt07 = 0x0A, | ||
| 189 | /// Gaussian BT 1.0 | ||
| 190 | Bt10 = 0x0B, | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Bitrate argument for [`FskModParams::set_bitrate`] and | ||
| 194 | /// [`BpskModParams::set_bitrate`]. | ||
| 195 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 196 | pub struct FskBitrate { | ||
| 197 | bits: u32, | ||
| 198 | } | ||
| 199 | |||
| 200 | impl FskBitrate { | ||
| 201 | /// Create a new `FskBitrate` from a bitrate in bits per second. | ||
| 202 | /// | ||
| 203 | /// This the resulting value will be rounded down, and will saturate if | ||
| 204 | /// `bps` is outside of the theoretical limits. | ||
| 205 | /// | ||
| 206 | /// # Example | ||
| 207 | /// | ||
| 208 | /// ``` | ||
| 209 | /// use stm32wlxx_hal::subghz::FskBitrate; | ||
| 210 | /// | ||
| 211 | /// const BITRATE: FskBitrate = FskBitrate::from_bps(9600); | ||
| 212 | /// assert_eq!(BITRATE.as_bps(), 9600); | ||
| 213 | /// ``` | ||
| 214 | pub const fn from_bps(bps: u32) -> Self { | ||
| 215 | const MAX: u32 = 0x00FF_FFFF; | ||
| 216 | if bps == 0 { | ||
| 217 | Self { bits: MAX } | ||
| 218 | } else { | ||
| 219 | let bits: u32 = 32 * 32_000_000 / bps; | ||
| 220 | if bits > MAX { | ||
| 221 | Self { bits: MAX } | ||
| 222 | } else { | ||
| 223 | Self { bits } | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Create a new `FskBitrate` from a raw bit value. | ||
| 229 | /// | ||
| 230 | /// bits = 32 × 32 MHz / bitrate | ||
| 231 | /// | ||
| 232 | /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` | ||
| 233 | /// argument will be masked. | ||
| 234 | /// | ||
| 235 | /// # Example | ||
| 236 | /// | ||
| 237 | /// ``` | ||
| 238 | /// use stm32wlxx_hal::subghz::FskBitrate; | ||
| 239 | /// | ||
| 240 | /// const BITRATE: FskBitrate = FskBitrate::from_raw(0x7D00); | ||
| 241 | /// assert_eq!(BITRATE.as_bps(), 32_000); | ||
| 242 | /// ``` | ||
| 243 | pub const fn from_raw(bits: u32) -> Self { | ||
| 244 | Self { | ||
| 245 | bits: bits & 0x00FF_FFFF, | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | /// Return the bitrate in bits per second, rounded down. | ||
| 250 | /// | ||
| 251 | /// # Example | ||
| 252 | /// | ||
| 253 | /// ``` | ||
| 254 | /// use stm32wlxx_hal::subghz::FskBitrate; | ||
| 255 | /// | ||
| 256 | /// const BITS_PER_SEC: u32 = 9600; | ||
| 257 | /// const BITRATE: FskBitrate = FskBitrate::from_bps(BITS_PER_SEC); | ||
| 258 | /// assert_eq!(BITRATE.as_bps(), BITS_PER_SEC); | ||
| 259 | /// ``` | ||
| 260 | pub const fn as_bps(&self) -> u32 { | ||
| 261 | if self.bits == 0 { | ||
| 262 | 0 | ||
| 263 | } else { | ||
| 264 | 32 * 32_000_000 / self.bits | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | pub(crate) const fn into_bits(self) -> u32 { | ||
| 269 | self.bits | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | impl Ord for FskBitrate { | ||
| 274 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||
| 275 | self.as_bps().cmp(&other.as_bps()) | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | impl PartialOrd for FskBitrate { | ||
| 280 | fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||
| 281 | Some(self.as_bps().cmp(&other.as_bps())) | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Frequency deviation argument for [`FskModParams::set_fdev`] | ||
| 286 | #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] | ||
| 287 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 288 | pub struct FskFdev { | ||
| 289 | bits: u32, | ||
| 290 | } | ||
| 291 | |||
| 292 | impl FskFdev { | ||
| 293 | /// Create a new `FskFdev` from a frequency deviation in hertz, rounded | ||
| 294 | /// down. | ||
| 295 | /// | ||
| 296 | /// # Example | ||
| 297 | /// | ||
| 298 | /// ``` | ||
| 299 | /// use stm32wlxx_hal::subghz::FskFdev; | ||
| 300 | /// | ||
| 301 | /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); | ||
| 302 | /// assert_eq!(FDEV.as_hertz(), 31_250); | ||
| 303 | /// ``` | ||
| 304 | pub const fn from_hertz(hz: u32) -> Self { | ||
| 305 | Self { | ||
| 306 | bits: ((hz as u64) * (1 << 25) / 32_000_000) as u32 & 0x00FF_FFFF, | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | /// Create a new `FskFdev` from a raw bit value. | ||
| 311 | /// | ||
| 312 | /// bits = fdev × 2<sup>25</sup> / 32 MHz | ||
| 313 | /// | ||
| 314 | /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` | ||
| 315 | /// argument will be masked. | ||
| 316 | /// | ||
| 317 | /// # Example | ||
| 318 | /// | ||
| 319 | /// ``` | ||
| 320 | /// use stm32wlxx_hal::subghz::FskFdev; | ||
| 321 | /// | ||
| 322 | /// const FDEV: FskFdev = FskFdev::from_raw(0x8000); | ||
| 323 | /// assert_eq!(FDEV.as_hertz(), 31_250); | ||
| 324 | /// ``` | ||
| 325 | pub const fn from_raw(bits: u32) -> Self { | ||
| 326 | Self { | ||
| 327 | bits: bits & 0x00FF_FFFF, | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Return the frequency deviation in hertz, rounded down. | ||
| 332 | /// | ||
| 333 | /// # Example | ||
| 334 | /// | ||
| 335 | /// ``` | ||
| 336 | /// use stm32wlxx_hal::subghz::FskFdev; | ||
| 337 | /// | ||
| 338 | /// const HERTZ: u32 = 31_250; | ||
| 339 | /// const FDEV: FskFdev = FskFdev::from_hertz(HERTZ); | ||
| 340 | /// assert_eq!(FDEV.as_hertz(), HERTZ); | ||
| 341 | /// ``` | ||
| 342 | pub const fn as_hertz(&self) -> u32 { | ||
| 343 | ((self.bits as u64) * 32_000_000 / (1 << 25)) as u32 | ||
| 344 | } | ||
| 345 | |||
| 346 | pub(crate) const fn into_bits(self) -> u32 { | ||
| 347 | self.bits | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | /// (G)FSK modulation parameters. | ||
| 352 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 353 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 354 | pub struct FskModParams { | ||
| 355 | buf: [u8; 9], | ||
| 356 | } | ||
| 357 | |||
| 358 | impl FskModParams { | ||
| 359 | /// Create a new `FskModParams` struct. | ||
| 360 | /// | ||
| 361 | /// This is the same as `default`, but in a `const` function. | ||
| 362 | /// | ||
| 363 | /// # Example | ||
| 364 | /// | ||
| 365 | /// ``` | ||
| 366 | /// use stm32wlxx_hal::subghz::FskModParams; | ||
| 367 | /// | ||
| 368 | /// const MOD_PARAMS: FskModParams = FskModParams::new(); | ||
| 369 | /// ``` | ||
| 370 | pub const fn new() -> FskModParams { | ||
| 371 | FskModParams { | ||
| 372 | buf: [ | ||
| 373 | super::OpCode::SetModulationParams as u8, | ||
| 374 | 0x00, | ||
| 375 | 0x00, | ||
| 376 | 0x00, | ||
| 377 | 0x00, | ||
| 378 | 0x00, | ||
| 379 | 0x00, | ||
| 380 | 0x00, | ||
| 381 | 0x00, | ||
| 382 | ], | ||
| 383 | } | ||
| 384 | .set_bitrate(FskBitrate::from_bps(50_000)) | ||
| 385 | .set_pulse_shape(FskPulseShape::None) | ||
| 386 | .set_bandwidth(FskBandwidth::Bw58) | ||
| 387 | .set_fdev(FskFdev::from_hertz(25_000)) | ||
| 388 | } | ||
| 389 | |||
| 390 | /// Get the bitrate. | ||
| 391 | /// | ||
| 392 | /// # Example | ||
| 393 | /// | ||
| 394 | /// Setting the bitrate to 32,000 bits per second. | ||
| 395 | /// | ||
| 396 | /// ``` | ||
| 397 | /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams}; | ||
| 398 | /// | ||
| 399 | /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000); | ||
| 400 | /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE); | ||
| 401 | /// assert_eq!(MOD_PARAMS.bitrate(), BITRATE); | ||
| 402 | /// ``` | ||
| 403 | pub const fn bitrate(&self) -> FskBitrate { | ||
| 404 | let raw: u32 = u32::from_be_bytes([0, self.buf[1], self.buf[2], self.buf[3]]); | ||
| 405 | FskBitrate::from_raw(raw) | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Set the bitrate. | ||
| 409 | /// | ||
| 410 | /// # Example | ||
| 411 | /// | ||
| 412 | /// Setting the bitrate to 32,000 bits per second. | ||
| 413 | /// | ||
| 414 | /// ``` | ||
| 415 | /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams}; | ||
| 416 | /// | ||
| 417 | /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000); | ||
| 418 | /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE); | ||
| 419 | /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x00); | ||
| 420 | /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x7D); | ||
| 421 | /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0x00); | ||
| 422 | /// ``` | ||
| 423 | #[must_use = "set_bitrate returns a modified FskModParams"] | ||
| 424 | pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> FskModParams { | ||
| 425 | let bits: u32 = bitrate.into_bits(); | ||
| 426 | self.buf[1] = ((bits >> 16) & 0xFF) as u8; | ||
| 427 | self.buf[2] = ((bits >> 8) & 0xFF) as u8; | ||
| 428 | self.buf[3] = (bits & 0xFF) as u8; | ||
| 429 | self | ||
| 430 | } | ||
| 431 | |||
| 432 | /// Set the pulse shaping. | ||
| 433 | /// | ||
| 434 | /// # Example | ||
| 435 | /// | ||
| 436 | /// ``` | ||
| 437 | /// use stm32wlxx_hal::subghz::{FskModParams, FskPulseShape}; | ||
| 438 | /// | ||
| 439 | /// const MOD_PARAMS: FskModParams = FskModParams::new().set_pulse_shape(FskPulseShape::Bt03); | ||
| 440 | /// # assert_eq!(MOD_PARAMS.as_slice()[4], 0x08); | ||
| 441 | /// ``` | ||
| 442 | #[must_use = "set_pulse_shape returns a modified FskModParams"] | ||
| 443 | pub const fn set_pulse_shape(mut self, shape: FskPulseShape) -> FskModParams { | ||
| 444 | self.buf[4] = shape as u8; | ||
| 445 | self | ||
| 446 | } | ||
| 447 | |||
| 448 | /// Get the bandwidth. | ||
| 449 | /// | ||
| 450 | /// Values that do not correspond to a valid [`FskBandwidth`] will be | ||
| 451 | /// returned in the `Err` variant of the result. | ||
| 452 | /// | ||
| 453 | /// # Example | ||
| 454 | /// | ||
| 455 | /// ``` | ||
| 456 | /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams}; | ||
| 457 | /// | ||
| 458 | /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9); | ||
| 459 | /// assert_eq!(MOD_PARAMS.bandwidth(), Ok(FskBandwidth::Bw9)); | ||
| 460 | /// ``` | ||
| 461 | pub const fn bandwidth(&self) -> Result<FskBandwidth, u8> { | ||
| 462 | FskBandwidth::from_bits(self.buf[5]) | ||
| 463 | } | ||
| 464 | |||
| 465 | /// Set the bandwidth. | ||
| 466 | /// | ||
| 467 | /// # Example | ||
| 468 | /// | ||
| 469 | /// ``` | ||
| 470 | /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams}; | ||
| 471 | /// | ||
| 472 | /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9); | ||
| 473 | /// # assert_eq!(MOD_PARAMS.as_slice()[5], 0x1E); | ||
| 474 | /// ``` | ||
| 475 | #[must_use = "set_pulse_shape returns a modified FskModParams"] | ||
| 476 | pub const fn set_bandwidth(mut self, bw: FskBandwidth) -> FskModParams { | ||
| 477 | self.buf[5] = bw as u8; | ||
| 478 | self | ||
| 479 | } | ||
| 480 | |||
| 481 | /// Get the frequency deviation. | ||
| 482 | /// | ||
| 483 | /// # Example | ||
| 484 | /// | ||
| 485 | /// ``` | ||
| 486 | /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams}; | ||
| 487 | /// | ||
| 488 | /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); | ||
| 489 | /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV); | ||
| 490 | /// assert_eq!(MOD_PARAMS.fdev(), FDEV); | ||
| 491 | /// ``` | ||
| 492 | pub const fn fdev(&self) -> FskFdev { | ||
| 493 | let raw: u32 = u32::from_be_bytes([0, self.buf[6], self.buf[7], self.buf[8]]); | ||
| 494 | FskFdev::from_raw(raw) | ||
| 495 | } | ||
| 496 | |||
| 497 | /// Set the frequency deviation. | ||
| 498 | /// | ||
| 499 | /// # Example | ||
| 500 | /// | ||
| 501 | /// ``` | ||
| 502 | /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams}; | ||
| 503 | /// | ||
| 504 | /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); | ||
| 505 | /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV); | ||
| 506 | /// # assert_eq!(MOD_PARAMS.as_slice()[6], 0x00); | ||
| 507 | /// # assert_eq!(MOD_PARAMS.as_slice()[7], 0x80); | ||
| 508 | /// # assert_eq!(MOD_PARAMS.as_slice()[8], 0x00); | ||
| 509 | /// ``` | ||
| 510 | #[must_use = "set_fdev returns a modified FskModParams"] | ||
| 511 | pub const fn set_fdev(mut self, fdev: FskFdev) -> FskModParams { | ||
| 512 | let bits: u32 = fdev.into_bits(); | ||
| 513 | self.buf[6] = ((bits >> 16) & 0xFF) as u8; | ||
| 514 | self.buf[7] = ((bits >> 8) & 0xFF) as u8; | ||
| 515 | self.buf[8] = (bits & 0xFF) as u8; | ||
| 516 | self | ||
| 517 | } | ||
| 518 | /// Returns `true` if the modulation parameters are valid. | ||
| 519 | /// | ||
| 520 | /// The bandwidth must be chosen so that: | ||
| 521 | /// | ||
| 522 | /// [`FskBandwidth`] > [`FskBitrate`] + 2 × [`FskFdev`] + frequency error | ||
| 523 | /// | ||
| 524 | /// Where frequency error = 2 × HSE32<sub>FREQ</sub> error. | ||
| 525 | /// | ||
| 526 | /// The datasheet (DS13293 Rev 1) gives these requirements for the HSE32 | ||
| 527 | /// frequency tolerance: | ||
| 528 | /// | ||
| 529 | /// * Initial: ±10 ppm | ||
| 530 | /// * Over temperature (-20 to 70 °C): ±10 ppm | ||
| 531 | /// * Aging over 10 years: ±10 ppm | ||
| 532 | /// | ||
| 533 | /// # Example | ||
| 534 | /// | ||
| 535 | /// Checking valid parameters at compile-time | ||
| 536 | /// | ||
| 537 | /// ``` | ||
| 538 | /// extern crate static_assertions as sa; | ||
| 539 | /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape}; | ||
| 540 | /// | ||
| 541 | /// const MOD_PARAMS: FskModParams = FskModParams::new() | ||
| 542 | /// .set_bitrate(FskBitrate::from_bps(20_000)) | ||
| 543 | /// .set_pulse_shape(FskPulseShape::Bt03) | ||
| 544 | /// .set_bandwidth(FskBandwidth::Bw58) | ||
| 545 | /// .set_fdev(FskFdev::from_hertz(10_000)); | ||
| 546 | /// | ||
| 547 | /// // 30 PPM is wost case (if the HSE32 crystal meets requirements) | ||
| 548 | /// sa::const_assert!(MOD_PARAMS.is_valid(30)); | ||
| 549 | /// ``` | ||
| 550 | #[must_use = "the return value indicates if the modulation parameters are valid"] | ||
| 551 | pub const fn is_valid(&self, ppm: u8) -> bool { | ||
| 552 | let bw: u32 = match self.bandwidth() { | ||
| 553 | Ok(bw) => bw.hertz(), | ||
| 554 | Err(_) => return false, | ||
| 555 | }; | ||
| 556 | let br: u32 = self.bitrate().as_bps(); | ||
| 557 | let fdev: u32 = self.fdev().as_hertz(); | ||
| 558 | let hse_err: u32 = 32 * (ppm as u32); | ||
| 559 | let freq_err: u32 = 2 * hse_err; | ||
| 560 | |||
| 561 | bw > br + 2 * fdev + freq_err | ||
| 562 | } | ||
| 563 | |||
| 564 | /// Returns `true` if the modulation parameters are valid for a worst-case | ||
| 565 | /// crystal tolerance. | ||
| 566 | /// | ||
| 567 | /// This is equivalent to [`is_valid`](Self::is_valid) with a `ppm` argument | ||
| 568 | /// of 30. | ||
| 569 | #[must_use = "the return value indicates if the modulation parameters are valid"] | ||
| 570 | pub const fn is_valid_worst_case(&self) -> bool { | ||
| 571 | self.is_valid(30) | ||
| 572 | } | ||
| 573 | |||
| 574 | /// Extracts a slice containing the packet. | ||
| 575 | /// | ||
| 576 | /// # Example | ||
| 577 | /// | ||
| 578 | /// ``` | ||
| 579 | /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape}; | ||
| 580 | /// | ||
| 581 | /// const BITRATE: FskBitrate = FskBitrate::from_bps(20_000); | ||
| 582 | /// const PULSE_SHAPE: FskPulseShape = FskPulseShape::Bt03; | ||
| 583 | /// const BW: FskBandwidth = FskBandwidth::Bw58; | ||
| 584 | /// const FDEV: FskFdev = FskFdev::from_hertz(10_000); | ||
| 585 | /// | ||
| 586 | /// const MOD_PARAMS: FskModParams = FskModParams::new() | ||
| 587 | /// .set_bitrate(BITRATE) | ||
| 588 | /// .set_pulse_shape(PULSE_SHAPE) | ||
| 589 | /// .set_bandwidth(BW) | ||
| 590 | /// .set_fdev(FDEV); | ||
| 591 | /// | ||
| 592 | /// assert_eq!( | ||
| 593 | /// MOD_PARAMS.as_slice(), | ||
| 594 | /// &[0x8B, 0x00, 0xC8, 0x00, 0x08, 0x0C, 0x00, 0x28, 0xF5] | ||
| 595 | /// ); | ||
| 596 | /// ``` | ||
| 597 | pub const fn as_slice(&self) -> &[u8] { | ||
| 598 | &self.buf | ||
| 599 | } | ||
| 600 | } | ||
| 601 | |||
| 602 | impl Default for FskModParams { | ||
| 603 | fn default() -> Self { | ||
| 604 | Self::new() | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | /// LoRa spreading factor. | ||
| 609 | /// | ||
| 610 | /// Argument of [`LoRaModParams::set_sf`]. | ||
| 611 | /// | ||
| 612 | /// Higher spreading factors improve receiver sensitivity, but reduce bit rate | ||
| 613 | /// and increase power consumption. | ||
| 614 | #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] | ||
| 615 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 616 | #[repr(u8)] | ||
| 617 | pub enum SpreadingFactor { | ||
| 618 | /// Spreading factor 5. | ||
| 619 | Sf5 = 0x05, | ||
| 620 | /// Spreading factor 6. | ||
| 621 | Sf6 = 0x06, | ||
| 622 | /// Spreading factor 7. | ||
| 623 | Sf7 = 0x07, | ||
| 624 | /// Spreading factor 8. | ||
| 625 | Sf8 = 0x08, | ||
| 626 | /// Spreading factor 9. | ||
| 627 | Sf9 = 0x09, | ||
| 628 | /// Spreading factor 10. | ||
| 629 | Sf10 = 0x0A, | ||
| 630 | /// Spreading factor 11. | ||
| 631 | Sf11 = 0x0B, | ||
| 632 | /// Spreading factor 12. | ||
| 633 | Sf12 = 0x0C, | ||
| 634 | } | ||
| 635 | |||
| 636 | impl From<SpreadingFactor> for u8 { | ||
| 637 | fn from(sf: SpreadingFactor) -> Self { | ||
| 638 | sf as u8 | ||
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 642 | /// LoRa bandwidth. | ||
| 643 | /// | ||
| 644 | /// Argument of [`LoRaModParams::set_bw`]. | ||
| 645 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 646 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 647 | #[repr(u8)] | ||
| 648 | pub enum LoRaBandwidth { | ||
| 649 | /// 7.81 kHz | ||
| 650 | Bw7 = 0x00, | ||
| 651 | /// 10.42 kHz | ||
| 652 | Bw10 = 0x08, | ||
| 653 | /// 15.63 kHz | ||
| 654 | Bw15 = 0x01, | ||
| 655 | /// 20.83 kHz | ||
| 656 | Bw20 = 0x09, | ||
| 657 | /// 31.25 kHz | ||
| 658 | Bw31 = 0x02, | ||
| 659 | /// 41.67 kHz | ||
| 660 | Bw41 = 0x0A, | ||
| 661 | /// 62.50 kHz | ||
| 662 | Bw62 = 0x03, | ||
| 663 | /// 125 kHz | ||
| 664 | Bw125 = 0x04, | ||
| 665 | /// 250 kHz | ||
| 666 | Bw250 = 0x05, | ||
| 667 | /// 500 kHz | ||
| 668 | Bw500 = 0x06, | ||
| 669 | } | ||
| 670 | |||
| 671 | impl LoRaBandwidth { | ||
| 672 | /// Get the bandwidth in hertz. | ||
| 673 | /// | ||
| 674 | /// # Example | ||
| 675 | /// | ||
| 676 | /// ``` | ||
| 677 | /// use stm32wlxx_hal::subghz::LoRaBandwidth; | ||
| 678 | /// | ||
| 679 | /// assert_eq!(LoRaBandwidth::Bw7.hertz(), 7_810); | ||
| 680 | /// assert_eq!(LoRaBandwidth::Bw10.hertz(), 10_420); | ||
| 681 | /// assert_eq!(LoRaBandwidth::Bw15.hertz(), 15_630); | ||
| 682 | /// assert_eq!(LoRaBandwidth::Bw20.hertz(), 20_830); | ||
| 683 | /// assert_eq!(LoRaBandwidth::Bw31.hertz(), 31_250); | ||
| 684 | /// assert_eq!(LoRaBandwidth::Bw41.hertz(), 41_670); | ||
| 685 | /// assert_eq!(LoRaBandwidth::Bw62.hertz(), 62_500); | ||
| 686 | /// assert_eq!(LoRaBandwidth::Bw125.hertz(), 125_000); | ||
| 687 | /// assert_eq!(LoRaBandwidth::Bw250.hertz(), 250_000); | ||
| 688 | /// assert_eq!(LoRaBandwidth::Bw500.hertz(), 500_000); | ||
| 689 | /// ``` | ||
| 690 | pub const fn hertz(&self) -> u32 { | ||
| 691 | match self { | ||
| 692 | LoRaBandwidth::Bw7 => 7_810, | ||
| 693 | LoRaBandwidth::Bw10 => 10_420, | ||
| 694 | LoRaBandwidth::Bw15 => 15_630, | ||
| 695 | LoRaBandwidth::Bw20 => 20_830, | ||
| 696 | LoRaBandwidth::Bw31 => 31_250, | ||
| 697 | LoRaBandwidth::Bw41 => 41_670, | ||
| 698 | LoRaBandwidth::Bw62 => 62_500, | ||
| 699 | LoRaBandwidth::Bw125 => 125_000, | ||
| 700 | LoRaBandwidth::Bw250 => 250_000, | ||
| 701 | LoRaBandwidth::Bw500 => 500_000, | ||
| 702 | } | ||
| 703 | } | ||
| 704 | } | ||
| 705 | |||
| 706 | impl Ord for LoRaBandwidth { | ||
| 707 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||
| 708 | self.hertz().cmp(&other.hertz()) | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | impl PartialOrd for LoRaBandwidth { | ||
| 713 | fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||
| 714 | Some(self.hertz().cmp(&other.hertz())) | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | /// LoRa forward error correction coding rate. | ||
| 719 | /// | ||
| 720 | /// Argument of [`LoRaModParams::set_cr`]. | ||
| 721 | /// | ||
| 722 | /// A higher coding rate provides better immunity to interference at the expense | ||
| 723 | /// of longer transmission time. | ||
| 724 | /// In normal conditions [`CodingRate::Cr45`] provides the best trade off. | ||
| 725 | /// In case of strong interference, a higher coding rate may be used. | ||
| 726 | #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] | ||
| 727 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 728 | #[repr(u8)] | ||
| 729 | pub enum CodingRate { | ||
| 730 | /// No forward error correction coding rate 4/4 | ||
| 731 | /// | ||
| 732 | /// Overhead ratio of 1 | ||
| 733 | Cr44 = 0x00, | ||
| 734 | /// Forward error correction coding rate 4/5 | ||
| 735 | /// | ||
| 736 | /// Overhead ratio of 1.25 | ||
| 737 | Cr45 = 0x1, | ||
| 738 | /// Forward error correction coding rate 4/6 | ||
| 739 | /// | ||
| 740 | /// Overhead ratio of 1.5 | ||
| 741 | Cr46 = 0x2, | ||
| 742 | /// Forward error correction coding rate 4/7 | ||
| 743 | /// | ||
| 744 | /// Overhead ratio of 1.75 | ||
| 745 | Cr47 = 0x3, | ||
| 746 | /// Forward error correction coding rate 4/8 | ||
| 747 | /// | ||
| 748 | /// Overhead ratio of 2 | ||
| 749 | Cr48 = 0x4, | ||
| 750 | } | ||
| 751 | |||
| 752 | /// LoRa modulation parameters. | ||
| 753 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 754 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 755 | |||
| 756 | pub struct LoRaModParams { | ||
| 757 | buf: [u8; 5], | ||
| 758 | } | ||
| 759 | |||
| 760 | impl LoRaModParams { | ||
| 761 | /// Create a new `LoRaModParams` struct. | ||
| 762 | /// | ||
| 763 | /// This is the same as `default`, but in a `const` function. | ||
| 764 | /// | ||
| 765 | /// # Example | ||
| 766 | /// | ||
| 767 | /// ``` | ||
| 768 | /// use stm32wlxx_hal::subghz::LoRaModParams; | ||
| 769 | /// | ||
| 770 | /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new(); | ||
| 771 | /// assert_eq!(MOD_PARAMS, LoRaModParams::default()); | ||
| 772 | /// ``` | ||
| 773 | pub const fn new() -> LoRaModParams { | ||
| 774 | LoRaModParams { | ||
| 775 | buf: [ | ||
| 776 | super::OpCode::SetModulationParams as u8, | ||
| 777 | 0x05, // valid spreading factor | ||
| 778 | 0x00, | ||
| 779 | 0x00, | ||
| 780 | 0x00, | ||
| 781 | ], | ||
| 782 | } | ||
| 783 | } | ||
| 784 | |||
| 785 | /// Set the spreading factor. | ||
| 786 | /// | ||
| 787 | /// # Example | ||
| 788 | /// | ||
| 789 | /// ``` | ||
| 790 | /// use stm32wlxx_hal::subghz::{LoRaModParams, SpreadingFactor}; | ||
| 791 | /// | ||
| 792 | /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_sf(SpreadingFactor::Sf7); | ||
| 793 | /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x00, 0x00, 0x00]); | ||
| 794 | /// ``` | ||
| 795 | #[must_use = "set_sf returns a modified LoRaModParams"] | ||
| 796 | pub const fn set_sf(mut self, sf: SpreadingFactor) -> Self { | ||
| 797 | self.buf[1] = sf as u8; | ||
| 798 | self | ||
| 799 | } | ||
| 800 | |||
| 801 | /// Set the bandwidth. | ||
| 802 | /// | ||
| 803 | /// # Example | ||
| 804 | /// | ||
| 805 | /// ``` | ||
| 806 | /// use stm32wlxx_hal::subghz::{LoRaBandwidth, LoRaModParams}; | ||
| 807 | /// | ||
| 808 | /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_bw(LoRaBandwidth::Bw125); | ||
| 809 | /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x04, 0x00, 0x00]); | ||
| 810 | /// ``` | ||
| 811 | #[must_use = "set_bw returns a modified LoRaModParams"] | ||
| 812 | pub const fn set_bw(mut self, bw: LoRaBandwidth) -> Self { | ||
| 813 | self.buf[2] = bw as u8; | ||
| 814 | self | ||
| 815 | } | ||
| 816 | |||
| 817 | /// Set the forward error correction coding rate. | ||
| 818 | /// | ||
| 819 | /// See [`CodingRate`] for more information. | ||
| 820 | /// | ||
| 821 | /// # Example | ||
| 822 | /// | ||
| 823 | /// ``` | ||
| 824 | /// use stm32wlxx_hal::subghz::{CodingRate, LoRaModParams}; | ||
| 825 | /// | ||
| 826 | /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_cr(CodingRate::Cr45); | ||
| 827 | /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x01, 0x00]); | ||
| 828 | /// ``` | ||
| 829 | #[must_use = "set_cr returns a modified LoRaModParams"] | ||
| 830 | pub const fn set_cr(mut self, cr: CodingRate) -> Self { | ||
| 831 | self.buf[3] = cr as u8; | ||
| 832 | self | ||
| 833 | } | ||
| 834 | |||
| 835 | /// Set low data rate optimization enable. | ||
| 836 | /// | ||
| 837 | /// For low data rates (typically high SF or low BW) and very long payloads | ||
| 838 | /// (may last several seconds), the low data rate optimization (LDRO) can be | ||
| 839 | /// enabled. | ||
| 840 | /// This reduces the number of bits per symbol to the given SF minus 2, | ||
| 841 | /// to allow the receiver to have a better tracking of the LoRa receive | ||
| 842 | /// signal. | ||
| 843 | /// Depending on the payload length, the low data rate optimization is | ||
| 844 | /// usually recommended when the LoRa symbol time is equal or above | ||
| 845 | /// 16.38 ms. | ||
| 846 | /// When using LoRa modulation, the total frequency drift over the packet | ||
| 847 | /// time must be kept lower than Freq_drift_max: | ||
| 848 | /// | ||
| 849 | /// Freq_drift_max = BW / (3 × 2SF) | ||
| 850 | /// | ||
| 851 | /// When possible, enabling the low data rate optimization, relaxes the | ||
| 852 | /// total frequency drift over the packet time by 16: | ||
| 853 | /// | ||
| 854 | /// Freq_drift_optimise_max = 16 × Freq_drift_max | ||
| 855 | /// | ||
| 856 | /// # Example | ||
| 857 | /// | ||
| 858 | /// ``` | ||
| 859 | /// use stm32wlxx_hal::subghz::LoRaModParams; | ||
| 860 | /// | ||
| 861 | /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_ldro_en(true); | ||
| 862 | /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x00, 0x01]); | ||
| 863 | /// ``` | ||
| 864 | #[must_use = "set_ldro_en returns a modified LoRaModParams"] | ||
| 865 | pub const fn set_ldro_en(mut self, en: bool) -> Self { | ||
| 866 | self.buf[4] = en as u8; | ||
| 867 | self | ||
| 868 | } | ||
| 869 | |||
| 870 | /// Extracts a slice containing the packet. | ||
| 871 | /// | ||
| 872 | /// # Example | ||
| 873 | /// | ||
| 874 | /// ``` | ||
| 875 | /// use stm32wlxx_hal::subghz::{CodingRate, LoRaBandwidth, LoRaModParams, SpreadingFactor}; | ||
| 876 | /// | ||
| 877 | /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new() | ||
| 878 | /// .set_sf(SpreadingFactor::Sf7) | ||
| 879 | /// .set_bw(LoRaBandwidth::Bw125) | ||
| 880 | /// .set_cr(CodingRate::Cr45) | ||
| 881 | /// .set_ldro_en(false); | ||
| 882 | /// | ||
| 883 | /// assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x04, 0x01, 0x00]); | ||
| 884 | /// ``` | ||
| 885 | pub const fn as_slice(&self) -> &[u8] { | ||
| 886 | &self.buf | ||
| 887 | } | ||
| 888 | } | ||
| 889 | |||
| 890 | impl Default for LoRaModParams { | ||
| 891 | fn default() -> Self { | ||
| 892 | Self::new() | ||
| 893 | } | ||
| 894 | } | ||
| 895 | |||
| 896 | /// BPSK modulation parameters. | ||
| 897 | /// | ||
| 898 | /// **Note:** There is no method to set the pulse shape because there is only | ||
| 899 | /// one valid pulse shape (Gaussian BT 0.5). | ||
| 900 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 901 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 902 | pub struct BpskModParams { | ||
| 903 | buf: [u8; 5], | ||
| 904 | } | ||
| 905 | |||
| 906 | impl BpskModParams { | ||
| 907 | /// Create a new `BpskModParams` struct. | ||
| 908 | /// | ||
| 909 | /// This is the same as `default`, but in a `const` function. | ||
| 910 | /// | ||
| 911 | /// # Example | ||
| 912 | /// | ||
| 913 | /// ``` | ||
| 914 | /// use stm32wlxx_hal::subghz::BpskModParams; | ||
| 915 | /// | ||
| 916 | /// const MOD_PARAMS: BpskModParams = BpskModParams::new(); | ||
| 917 | /// assert_eq!(MOD_PARAMS, BpskModParams::default()); | ||
| 918 | /// ``` | ||
| 919 | pub const fn new() -> BpskModParams { | ||
| 920 | const OPCODE: u8 = super::OpCode::SetModulationParams as u8; | ||
| 921 | BpskModParams { | ||
| 922 | buf: [OPCODE, 0x1A, 0x0A, 0xAA, 0x16], | ||
| 923 | } | ||
| 924 | } | ||
| 925 | |||
| 926 | /// Set the bitrate. | ||
| 927 | /// | ||
| 928 | /// # Example | ||
| 929 | /// | ||
| 930 | /// Setting the bitrate to 600 bits per second. | ||
| 931 | /// | ||
| 932 | /// ``` | ||
| 933 | /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate}; | ||
| 934 | /// | ||
| 935 | /// const BITRATE: FskBitrate = FskBitrate::from_bps(600); | ||
| 936 | /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE); | ||
| 937 | /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x1A); | ||
| 938 | /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x0A); | ||
| 939 | /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0xAA); | ||
| 940 | /// ``` | ||
| 941 | #[must_use = "set_bitrate returns a modified BpskModParams"] | ||
| 942 | pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> BpskModParams { | ||
| 943 | let bits: u32 = bitrate.into_bits(); | ||
| 944 | self.buf[1] = ((bits >> 16) & 0xFF) as u8; | ||
| 945 | self.buf[2] = ((bits >> 8) & 0xFF) as u8; | ||
| 946 | self.buf[3] = (bits & 0xFF) as u8; | ||
| 947 | self | ||
| 948 | } | ||
| 949 | |||
| 950 | /// Extracts a slice containing the packet. | ||
| 951 | /// | ||
| 952 | /// # Example | ||
| 953 | /// | ||
| 954 | /// ``` | ||
| 955 | /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate}; | ||
| 956 | /// | ||
| 957 | /// const BITRATE: FskBitrate = FskBitrate::from_bps(100); | ||
| 958 | /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE); | ||
| 959 | /// assert_eq!(MOD_PARAMS.as_slice(), [0x8B, 0x9C, 0x40, 0x00, 0x16]); | ||
| 960 | /// ``` | ||
| 961 | pub const fn as_slice(&self) -> &[u8] { | ||
| 962 | &self.buf | ||
| 963 | } | ||
| 964 | } | ||
| 965 | |||
| 966 | impl Default for BpskModParams { | ||
| 967 | fn default() -> Self { | ||
| 968 | Self::new() | ||
| 969 | } | ||
| 970 | } | ||
| 971 | |||
| 972 | #[cfg(test)] | ||
| 973 | mod test { | ||
| 974 | use super::{FskBandwidth, FskBitrate, FskFdev, LoRaBandwidth}; | ||
| 975 | |||
| 976 | #[test] | ||
| 977 | fn fsk_bw_ord() { | ||
| 978 | assert!((FskBandwidth::Bw4 as u8) > (FskBandwidth::Bw5 as u8)); | ||
| 979 | assert!(FskBandwidth::Bw4 < FskBandwidth::Bw5); | ||
| 980 | assert!(FskBandwidth::Bw5 > FskBandwidth::Bw4); | ||
| 981 | } | ||
| 982 | |||
| 983 | #[test] | ||
| 984 | fn lora_bw_ord() { | ||
| 985 | assert!((LoRaBandwidth::Bw10 as u8) > (LoRaBandwidth::Bw15 as u8)); | ||
| 986 | assert!(LoRaBandwidth::Bw10 < LoRaBandwidth::Bw15); | ||
| 987 | assert!(LoRaBandwidth::Bw15 > LoRaBandwidth::Bw10); | ||
| 988 | } | ||
| 989 | |||
| 990 | #[test] | ||
| 991 | fn fsk_bitrate_ord() { | ||
| 992 | assert!(FskBitrate::from_bps(9600) > FskBitrate::from_bps(4800)); | ||
| 993 | assert!(FskBitrate::from_bps(4800) < FskBitrate::from_bps(9600)); | ||
| 994 | } | ||
| 995 | |||
| 996 | #[test] | ||
| 997 | fn fsk_bitrate_as_bps_limits() { | ||
| 998 | const ZERO: FskBitrate = FskBitrate::from_raw(0); | ||
| 999 | const ONE: FskBitrate = FskBitrate::from_raw(1); | ||
| 1000 | const MAX: FskBitrate = FskBitrate::from_raw(u32::MAX); | ||
| 1001 | |||
| 1002 | assert_eq!(ZERO.as_bps(), 0); | ||
| 1003 | assert_eq!(ONE.as_bps(), 1_024_000_000); | ||
| 1004 | assert_eq!(MAX.as_bps(), 61); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | #[test] | ||
| 1008 | fn fsk_bitrate_from_bps_limits() { | ||
| 1009 | const ZERO: FskBitrate = FskBitrate::from_bps(0); | ||
| 1010 | const ONE: FskBitrate = FskBitrate::from_bps(1); | ||
| 1011 | const MAX: FskBitrate = FskBitrate::from_bps(u32::MAX); | ||
| 1012 | |||
| 1013 | assert_eq!(ZERO.as_bps(), 61); | ||
| 1014 | assert_eq!(ONE.as_bps(), 61); | ||
| 1015 | assert_eq!(MAX.as_bps(), 0); | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | #[test] | ||
| 1019 | fn fsk_fdev_ord() { | ||
| 1020 | assert!(FskFdev::from_hertz(30_000) > FskFdev::from_hertz(20_000)); | ||
| 1021 | assert!(FskFdev::from_hertz(20_000) < FskFdev::from_hertz(30_000)); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | #[test] | ||
| 1025 | fn fsk_fdev_as_hertz_limits() { | ||
| 1026 | const ZERO: FskFdev = FskFdev::from_raw(0); | ||
| 1027 | const ONE: FskFdev = FskFdev::from_raw(1); | ||
| 1028 | const MAX: FskFdev = FskFdev::from_raw(u32::MAX); | ||
| 1029 | |||
| 1030 | assert_eq!(ZERO.as_hertz(), 0); | ||
| 1031 | assert_eq!(ONE.as_hertz(), 0); | ||
| 1032 | assert_eq!(MAX.as_hertz(), 15_999_999); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | #[test] | ||
| 1036 | fn fsk_fdev_from_hertz_limits() { | ||
| 1037 | const ZERO: FskFdev = FskFdev::from_hertz(0); | ||
| 1038 | const ONE: FskFdev = FskFdev::from_hertz(1); | ||
| 1039 | const MAX: FskFdev = FskFdev::from_hertz(u32::MAX); | ||
| 1040 | |||
| 1041 | assert_eq!(ZERO.as_hertz(), 0); | ||
| 1042 | assert_eq!(ONE.as_hertz(), 0); | ||
| 1043 | assert_eq!(MAX.as_hertz(), 6_967_294); | ||
| 1044 | } | ||
| 1045 | } | ||
diff --git a/embassy-stm32/src/subghz/ocp.rs b/embassy-stm32/src/subghz/ocp.rs deleted file mode 100644 index 81e89c217..000000000 --- a/embassy-stm32/src/subghz/ocp.rs +++ /dev/null | |||
| @@ -1,14 +0,0 @@ | |||
| 1 | /// Power amplifier over current protection. | ||
| 2 | /// | ||
| 3 | /// Used by [`set_pa_ocp`]. | ||
| 4 | /// | ||
| 5 | /// [`set_pa_ocp`]: super::SubGhz::set_pa_ocp | ||
| 6 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | #[repr(u8)] | ||
| 9 | pub enum Ocp { | ||
| 10 | /// Maximum 60mA current for LP PA mode. | ||
| 11 | Max60m = 0x18, | ||
| 12 | /// Maximum 140mA for HP PA mode. | ||
| 13 | Max140m = 0x38, | ||
| 14 | } | ||
diff --git a/embassy-stm32/src/subghz/op_error.rs b/embassy-stm32/src/subghz/op_error.rs deleted file mode 100644 index b17b99205..000000000 --- a/embassy-stm32/src/subghz/op_error.rs +++ /dev/null | |||
| @@ -1,48 +0,0 @@ | |||
| 1 | /// Operation Errors. | ||
| 2 | /// | ||
| 3 | /// Returned by [`op_error`]. | ||
| 4 | /// | ||
| 5 | /// [`op_error`]: super::SubGhz::op_error | ||
| 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | #[repr(u8)] | ||
| 9 | pub enum OpError { | ||
| 10 | /// PA ramping failed | ||
| 11 | PaRampError = 8, | ||
| 12 | /// RF-PLL locking failed | ||
| 13 | PllLockError = 6, | ||
| 14 | /// HSE32 clock startup failed | ||
| 15 | XoscStartError = 5, | ||
| 16 | /// Image calibration failed | ||
| 17 | ImageCalibrationError = 4, | ||
| 18 | /// RF-ADC calibration failed | ||
| 19 | AdcCalibrationError = 3, | ||
| 20 | /// RF-PLL calibration failed | ||
| 21 | PllCalibrationError = 2, | ||
| 22 | /// Sub-GHz radio RC 13 MHz oscillator | ||
| 23 | RC13MCalibrationError = 1, | ||
| 24 | /// Sub-GHz radio RC 64 kHz oscillator | ||
| 25 | RC64KCalibrationError = 0, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl OpError { | ||
| 29 | /// Get the bitmask for the error. | ||
| 30 | /// | ||
| 31 | /// # Example | ||
| 32 | /// | ||
| 33 | /// ``` | ||
| 34 | /// use stm32wlxx_hal::subghz::OpError; | ||
| 35 | /// | ||
| 36 | /// assert_eq!(OpError::PaRampError.mask(), 0b1_0000_0000); | ||
| 37 | /// assert_eq!(OpError::PllLockError.mask(), 0b0_0100_0000); | ||
| 38 | /// assert_eq!(OpError::XoscStartError.mask(), 0b0_0010_0000); | ||
| 39 | /// assert_eq!(OpError::ImageCalibrationError.mask(), 0b0_0001_0000); | ||
| 40 | /// assert_eq!(OpError::AdcCalibrationError.mask(), 0b0_0000_1000); | ||
| 41 | /// assert_eq!(OpError::PllCalibrationError.mask(), 0b0_0000_0100); | ||
| 42 | /// assert_eq!(OpError::RC13MCalibrationError.mask(), 0b0_0000_0010); | ||
| 43 | /// assert_eq!(OpError::RC64KCalibrationError.mask(), 0b0_0000_0001); | ||
| 44 | /// ``` | ||
| 45 | pub const fn mask(self) -> u16 { | ||
| 46 | 1 << (self as u8) | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/embassy-stm32/src/subghz/pa_config.rs b/embassy-stm32/src/subghz/pa_config.rs deleted file mode 100644 index 875827bd4..000000000 --- a/embassy-stm32/src/subghz/pa_config.rs +++ /dev/null | |||
| @@ -1,196 +0,0 @@ | |||
| 1 | /// Power amplifier configuration parameters. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_pa_config`]. | ||
| 4 | /// | ||
| 5 | /// [`set_pa_config`]: super::SubGhz::set_pa_config | ||
| 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct PaConfig { | ||
| 9 | buf: [u8; 5], | ||
| 10 | } | ||
| 11 | |||
| 12 | impl PaConfig { | ||
| 13 | /// Optimal settings for +15dBm output power with the low-power PA. | ||
| 14 | /// | ||
| 15 | /// This must be used with [`TxParams::LP_15`](super::TxParams::LP_15). | ||
| 16 | pub const LP_15: PaConfig = PaConfig::new().set_pa_duty_cycle(0x6).set_hp_max(0x0).set_pa(PaSel::Lp); | ||
| 17 | |||
| 18 | /// Optimal settings for +14dBm output power with the low-power PA. | ||
| 19 | /// | ||
| 20 | /// This must be used with [`TxParams::LP_14`](super::TxParams::LP_14). | ||
| 21 | pub const LP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x0).set_pa(PaSel::Lp); | ||
| 22 | |||
| 23 | /// Optimal settings for +10dBm output power with the low-power PA. | ||
| 24 | /// | ||
| 25 | /// This must be used with [`TxParams::LP_10`](super::TxParams::LP_10). | ||
| 26 | pub const LP_10: PaConfig = PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp); | ||
| 27 | |||
| 28 | /// Optimal settings for +22dBm output power with the high-power PA. | ||
| 29 | /// | ||
| 30 | /// This must be used with [`TxParams::HP`](super::TxParams::HP). | ||
| 31 | pub const HP_22: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x7).set_pa(PaSel::Hp); | ||
| 32 | |||
| 33 | /// Optimal settings for +20dBm output power with the high-power PA. | ||
| 34 | /// | ||
| 35 | /// This must be used with [`TxParams::HP`](super::TxParams::HP). | ||
| 36 | pub const HP_20: PaConfig = PaConfig::new().set_pa_duty_cycle(0x3).set_hp_max(0x5).set_pa(PaSel::Hp); | ||
| 37 | |||
| 38 | /// Optimal settings for +17dBm output power with the high-power PA. | ||
| 39 | /// | ||
| 40 | /// This must be used with [`TxParams::HP`](super::TxParams::HP). | ||
| 41 | pub const HP_17: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x3).set_pa(PaSel::Hp); | ||
| 42 | |||
| 43 | /// Optimal settings for +14dBm output power with the high-power PA. | ||
| 44 | /// | ||
| 45 | /// This must be used with [`TxParams::HP`](super::TxParams::HP). | ||
| 46 | pub const HP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x2).set_pa(PaSel::Hp); | ||
| 47 | |||
| 48 | /// Create a new `PaConfig` struct. | ||
| 49 | /// | ||
| 50 | /// This is the same as `default`, but in a `const` function. | ||
| 51 | /// | ||
| 52 | /// # Example | ||
| 53 | /// | ||
| 54 | /// ``` | ||
| 55 | /// use stm32wlxx_hal::subghz::PaConfig; | ||
| 56 | /// | ||
| 57 | /// const PA_CONFIG: PaConfig = PaConfig::new(); | ||
| 58 | /// ``` | ||
| 59 | pub const fn new() -> PaConfig { | ||
| 60 | PaConfig { | ||
| 61 | buf: [super::OpCode::SetPaConfig as u8, 0x01, 0x00, 0x01, 0x01], | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Set the power amplifier duty cycle (conduit angle) control. | ||
| 66 | /// | ||
| 67 | /// **Note:** Only the first 3 bits of the `pa_duty_cycle` argument are used. | ||
| 68 | /// | ||
| 69 | /// Duty cycle = 0.2 + 0.04 × bits | ||
| 70 | /// | ||
| 71 | /// # Caution | ||
| 72 | /// | ||
| 73 | /// The following restrictions must be observed to avoid over-stress on the PA: | ||
| 74 | /// * LP PA mode with synthesis frequency > 400 MHz, `pa_duty_cycle` must be < 0x7. | ||
| 75 | /// * LP PA mode with synthesis frequency < 400 MHz, `pa_duty_cycle` must be < 0x4. | ||
| 76 | /// * HP PA mode, `pa_duty_cycle` must be < 0x4 | ||
| 77 | /// | ||
| 78 | /// # Example | ||
| 79 | /// | ||
| 80 | /// ``` | ||
| 81 | /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; | ||
| 82 | /// | ||
| 83 | /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Lp).set_pa_duty_cycle(0x4); | ||
| 84 | /// # assert_eq!(PA_CONFIG.as_slice()[1], 0x04); | ||
| 85 | /// ``` | ||
| 86 | #[must_use = "set_pa_duty_cycle returns a modified PaConfig"] | ||
| 87 | pub const fn set_pa_duty_cycle(mut self, pa_duty_cycle: u8) -> PaConfig { | ||
| 88 | self.buf[1] = pa_duty_cycle & 0b111; | ||
| 89 | self | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Set the high power amplifier output power. | ||
| 93 | /// | ||
| 94 | /// **Note:** Only the first 3 bits of the `hp_max` argument are used. | ||
| 95 | /// | ||
| 96 | /// # Example | ||
| 97 | /// | ||
| 98 | /// ``` | ||
| 99 | /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; | ||
| 100 | /// | ||
| 101 | /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Hp).set_hp_max(0x2); | ||
| 102 | /// # assert_eq!(PA_CONFIG.as_slice()[2], 0x02); | ||
| 103 | /// ``` | ||
| 104 | #[must_use = "set_hp_max returns a modified PaConfig"] | ||
| 105 | pub const fn set_hp_max(mut self, hp_max: u8) -> PaConfig { | ||
| 106 | self.buf[2] = hp_max & 0b111; | ||
| 107 | self | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Set the power amplifier to use, low or high power. | ||
| 111 | /// | ||
| 112 | /// # Example | ||
| 113 | /// | ||
| 114 | /// ``` | ||
| 115 | /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; | ||
| 116 | /// | ||
| 117 | /// const PA_CONFIG_HP: PaConfig = PaConfig::new().set_pa(PaSel::Hp); | ||
| 118 | /// const PA_CONFIG_LP: PaConfig = PaConfig::new().set_pa(PaSel::Lp); | ||
| 119 | /// # assert_eq!(PA_CONFIG_HP.as_slice()[3], 0x00); | ||
| 120 | /// # assert_eq!(PA_CONFIG_LP.as_slice()[3], 0x01); | ||
| 121 | /// ``` | ||
| 122 | #[must_use = "set_pa returns a modified PaConfig"] | ||
| 123 | pub const fn set_pa(mut self, pa: PaSel) -> PaConfig { | ||
| 124 | self.buf[3] = pa as u8; | ||
| 125 | self | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Extracts a slice containing the packet. | ||
| 129 | /// | ||
| 130 | /// # Example | ||
| 131 | /// | ||
| 132 | /// ``` | ||
| 133 | /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; | ||
| 134 | /// | ||
| 135 | /// const PA_CONFIG: PaConfig = PaConfig::new() | ||
| 136 | /// .set_pa(PaSel::Hp) | ||
| 137 | /// .set_pa_duty_cycle(0x2) | ||
| 138 | /// .set_hp_max(0x3); | ||
| 139 | /// | ||
| 140 | /// assert_eq!(PA_CONFIG.as_slice(), &[0x95, 0x2, 0x03, 0x00, 0x01]); | ||
| 141 | /// ``` | ||
| 142 | pub const fn as_slice(&self) -> &[u8] { | ||
| 143 | &self.buf | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | impl Default for PaConfig { | ||
| 148 | fn default() -> Self { | ||
| 149 | Self::new() | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Power amplifier selection. | ||
| 154 | /// | ||
| 155 | /// Argument of [`PaConfig::set_pa`]. | ||
| 156 | #[repr(u8)] | ||
| 157 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 158 | pub enum PaSel { | ||
| 159 | /// High power amplifier. | ||
| 160 | Hp = 0b0, | ||
| 161 | /// Low power amplifier. | ||
| 162 | Lp = 0b1, | ||
| 163 | } | ||
| 164 | |||
| 165 | impl PartialOrd for PaSel { | ||
| 166 | fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||
| 167 | Some(self.cmp(other)) | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | impl Ord for PaSel { | ||
| 172 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||
| 173 | match (self, other) { | ||
| 174 | (PaSel::Hp, PaSel::Hp) | (PaSel::Lp, PaSel::Lp) => core::cmp::Ordering::Equal, | ||
| 175 | (PaSel::Hp, PaSel::Lp) => core::cmp::Ordering::Greater, | ||
| 176 | (PaSel::Lp, PaSel::Hp) => core::cmp::Ordering::Less, | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | impl Default for PaSel { | ||
| 182 | fn default() -> Self { | ||
| 183 | PaSel::Lp | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | #[cfg(test)] | ||
| 188 | mod test { | ||
| 189 | use super::PaSel; | ||
| 190 | |||
| 191 | #[test] | ||
| 192 | fn pa_sel_ord() { | ||
| 193 | assert!(PaSel::Lp < PaSel::Hp); | ||
| 194 | assert!(PaSel::Hp > PaSel::Lp); | ||
| 195 | } | ||
| 196 | } | ||
diff --git a/embassy-stm32/src/subghz/packet_params.rs b/embassy-stm32/src/subghz/packet_params.rs deleted file mode 100644 index db1fb88d9..000000000 --- a/embassy-stm32/src/subghz/packet_params.rs +++ /dev/null | |||
| @@ -1,534 +0,0 @@ | |||
| 1 | /// Preamble detection length for [`GenericPacketParams`]. | ||
| 2 | #[repr(u8)] | ||
| 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||
| 4 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 5 | pub enum PreambleDetection { | ||
| 6 | /// Preamble detection disabled. | ||
| 7 | Disabled = 0x0, | ||
| 8 | /// 8-bit preamble detection. | ||
| 9 | Bit8 = 0x4, | ||
| 10 | /// 16-bit preamble detection. | ||
| 11 | Bit16 = 0x5, | ||
| 12 | /// 24-bit preamble detection. | ||
| 13 | Bit24 = 0x6, | ||
| 14 | /// 32-bit preamble detection. | ||
| 15 | Bit32 = 0x7, | ||
| 16 | } | ||
| 17 | |||
| 18 | /// Address comparison/filtering for [`GenericPacketParams`]. | ||
| 19 | #[repr(u8)] | ||
| 20 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 22 | pub enum AddrComp { | ||
| 23 | /// Address comparison/filtering disabled. | ||
| 24 | Disabled = 0x0, | ||
| 25 | /// Address comparison/filtering on node address. | ||
| 26 | Node = 0x1, | ||
| 27 | /// Address comparison/filtering on node and broadcast addresses. | ||
| 28 | Broadcast = 0x2, | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Packet header type. | ||
| 32 | /// | ||
| 33 | /// Argument of [`GenericPacketParams::set_header_type`] and | ||
| 34 | /// [`LoRaPacketParams::set_header_type`]. | ||
| 35 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 36 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 37 | pub enum HeaderType { | ||
| 38 | /// Fixed; payload length and header field not added to packet. | ||
| 39 | Fixed, | ||
| 40 | /// Variable; payload length and header field added to packet. | ||
| 41 | Variable, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl HeaderType { | ||
| 45 | pub(crate) const fn to_bits_generic(self) -> u8 { | ||
| 46 | match self { | ||
| 47 | HeaderType::Fixed => 0, | ||
| 48 | HeaderType::Variable => 1, | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | pub(crate) const fn to_bits_lora(self) -> u8 { | ||
| 53 | match self { | ||
| 54 | HeaderType::Fixed => 1, | ||
| 55 | HeaderType::Variable => 0, | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /// CRC type definition for [`GenericPacketParams`]. | ||
| 61 | #[repr(u8)] | ||
| 62 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 63 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 64 | pub enum CrcType { | ||
| 65 | /// 1-byte CRC. | ||
| 66 | Byte1 = 0x0, | ||
| 67 | /// CRC disabled. | ||
| 68 | Disabled = 0x1, | ||
| 69 | /// 2-byte CRC. | ||
| 70 | Byte2 = 0x2, | ||
| 71 | /// 1-byte inverted CRC. | ||
| 72 | Byte1Inverted = 0x4, | ||
| 73 | /// 2-byte inverted CRC. | ||
| 74 | Byte2Inverted = 0x6, | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Packet parameters for [`set_packet_params`]. | ||
| 78 | /// | ||
| 79 | /// [`set_packet_params`]: super::SubGhz::set_packet_params | ||
| 80 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 81 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 82 | pub struct GenericPacketParams { | ||
| 83 | buf: [u8; 10], | ||
| 84 | } | ||
| 85 | |||
| 86 | impl GenericPacketParams { | ||
| 87 | /// Create a new `GenericPacketParams`. | ||
| 88 | /// | ||
| 89 | /// This is the same as `default`, but in a `const` function. | ||
| 90 | /// | ||
| 91 | /// # Example | ||
| 92 | /// | ||
| 93 | /// ``` | ||
| 94 | /// use stm32wlxx_hal::subghz::GenericPacketParams; | ||
| 95 | /// | ||
| 96 | /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new(); | ||
| 97 | /// assert_eq!(PKT_PARAMS, GenericPacketParams::default()); | ||
| 98 | /// ``` | ||
| 99 | pub const fn new() -> GenericPacketParams { | ||
| 100 | const OPCODE: u8 = super::OpCode::SetPacketParams as u8; | ||
| 101 | // const variable ensure the compile always optimizes the methods | ||
| 102 | const NEW: GenericPacketParams = GenericPacketParams { | ||
| 103 | buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], | ||
| 104 | } | ||
| 105 | .set_preamble_len(1) | ||
| 106 | .set_preamble_detection(PreambleDetection::Disabled) | ||
| 107 | .set_sync_word_len(0) | ||
| 108 | .set_addr_comp(AddrComp::Disabled) | ||
| 109 | .set_header_type(HeaderType::Fixed) | ||
| 110 | .set_payload_len(1); | ||
| 111 | |||
| 112 | NEW | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Preamble length in number of symbols. | ||
| 116 | /// | ||
| 117 | /// Values of zero are invalid, and will automatically be set to 1. | ||
| 118 | /// | ||
| 119 | /// # Example | ||
| 120 | /// | ||
| 121 | /// ``` | ||
| 122 | /// use stm32wlxx_hal::subghz::GenericPacketParams; | ||
| 123 | /// | ||
| 124 | /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_preamble_len(0x1234); | ||
| 125 | /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12); | ||
| 126 | /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34); | ||
| 127 | /// ``` | ||
| 128 | #[must_use = "preamble_length returns a modified GenericPacketParams"] | ||
| 129 | pub const fn set_preamble_len(mut self, mut len: u16) -> GenericPacketParams { | ||
| 130 | if len == 0 { | ||
| 131 | len = 1 | ||
| 132 | } | ||
| 133 | self.buf[1] = ((len >> 8) & 0xFF) as u8; | ||
| 134 | self.buf[2] = (len & 0xFF) as u8; | ||
| 135 | self | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Preamble detection length in number of bit symbols. | ||
| 139 | /// | ||
| 140 | /// # Example | ||
| 141 | /// | ||
| 142 | /// ``` | ||
| 143 | /// use stm32wlxx_hal::subghz::{GenericPacketParams, PreambleDetection}; | ||
| 144 | /// | ||
| 145 | /// const PKT_PARAMS: GenericPacketParams = | ||
| 146 | /// GenericPacketParams::new().set_preamble_detection(PreambleDetection::Bit8); | ||
| 147 | /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x4); | ||
| 148 | /// ``` | ||
| 149 | #[must_use = "set_preamble_detection returns a modified GenericPacketParams"] | ||
| 150 | pub const fn set_preamble_detection(mut self, pb_det: PreambleDetection) -> GenericPacketParams { | ||
| 151 | self.buf[3] = pb_det as u8; | ||
| 152 | self | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Sync word length in number of bit symbols. | ||
| 156 | /// | ||
| 157 | /// Valid values are `0x00` - `0x40` for 0 to 64-bits respectively. | ||
| 158 | /// Values that exceed the maximum will saturate at `0x40`. | ||
| 159 | /// | ||
| 160 | /// # Example | ||
| 161 | /// | ||
| 162 | /// Set the sync word length to 4 bytes (16 bits). | ||
| 163 | /// | ||
| 164 | /// ``` | ||
| 165 | /// use stm32wlxx_hal::subghz::GenericPacketParams; | ||
| 166 | /// | ||
| 167 | /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_sync_word_len(16); | ||
| 168 | /// # assert_eq!(PKT_PARAMS.as_slice()[4], 0x10); | ||
| 169 | /// ``` | ||
| 170 | #[must_use = "set_sync_word_len returns a modified GenericPacketParams"] | ||
| 171 | pub const fn set_sync_word_len(mut self, len: u8) -> GenericPacketParams { | ||
| 172 | const MAX: u8 = 0x40; | ||
| 173 | if len > MAX { | ||
| 174 | self.buf[4] = MAX; | ||
| 175 | } else { | ||
| 176 | self.buf[4] = len; | ||
| 177 | } | ||
| 178 | self | ||
| 179 | } | ||
| 180 | |||
| 181 | /// Address comparison/filtering. | ||
| 182 | /// | ||
| 183 | /// # Example | ||
| 184 | /// | ||
| 185 | /// Enable address on the node address. | ||
| 186 | /// | ||
| 187 | /// ``` | ||
| 188 | /// use stm32wlxx_hal::subghz::{AddrComp, GenericPacketParams}; | ||
| 189 | /// | ||
| 190 | /// const PKT_PARAMS: GenericPacketParams = | ||
| 191 | /// GenericPacketParams::new().set_addr_comp(AddrComp::Node); | ||
| 192 | /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x01); | ||
| 193 | /// ``` | ||
| 194 | #[must_use = "set_addr_comp returns a modified GenericPacketParams"] | ||
| 195 | pub const fn set_addr_comp(mut self, addr_comp: AddrComp) -> GenericPacketParams { | ||
| 196 | self.buf[5] = addr_comp as u8; | ||
| 197 | self | ||
| 198 | } | ||
| 199 | |||
| 200 | /// Header type definition. | ||
| 201 | /// | ||
| 202 | /// **Note:** The reference manual calls this packet type, but that results | ||
| 203 | /// in a conflicting variable name for the modulation scheme, which the | ||
| 204 | /// reference manual also calls packet type. | ||
| 205 | /// | ||
| 206 | /// # Example | ||
| 207 | /// | ||
| 208 | /// Set the header type to a variable length. | ||
| 209 | /// | ||
| 210 | /// ``` | ||
| 211 | /// use stm32wlxx_hal::subghz::{GenericPacketParams, HeaderType}; | ||
| 212 | /// | ||
| 213 | /// const PKT_PARAMS: GenericPacketParams = | ||
| 214 | /// GenericPacketParams::new().set_header_type(HeaderType::Variable); | ||
| 215 | /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x01); | ||
| 216 | /// ``` | ||
| 217 | #[must_use = "set_header_type returns a modified GenericPacketParams"] | ||
| 218 | pub const fn set_header_type(mut self, header_type: HeaderType) -> GenericPacketParams { | ||
| 219 | self.buf[6] = header_type.to_bits_generic(); | ||
| 220 | self | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Set the payload length in bytes. | ||
| 224 | /// | ||
| 225 | /// # Example | ||
| 226 | /// | ||
| 227 | /// ``` | ||
| 228 | /// use stm32wlxx_hal::subghz::GenericPacketParams; | ||
| 229 | /// | ||
| 230 | /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_payload_len(12); | ||
| 231 | /// # assert_eq!(PKT_PARAMS.as_slice()[7], 12); | ||
| 232 | /// ``` | ||
| 233 | #[must_use = "set_payload_len returns a modified GenericPacketParams"] | ||
| 234 | pub const fn set_payload_len(mut self, len: u8) -> GenericPacketParams { | ||
| 235 | self.buf[7] = len; | ||
| 236 | self | ||
| 237 | } | ||
| 238 | |||
| 239 | /// CRC type definition. | ||
| 240 | /// | ||
| 241 | /// # Example | ||
| 242 | /// | ||
| 243 | /// ``` | ||
| 244 | /// use stm32wlxx_hal::subghz::{CrcType, GenericPacketParams}; | ||
| 245 | /// | ||
| 246 | /// const PKT_PARAMS: GenericPacketParams = | ||
| 247 | /// GenericPacketParams::new().set_crc_type(CrcType::Byte2Inverted); | ||
| 248 | /// # assert_eq!(PKT_PARAMS.as_slice()[8], 0x6); | ||
| 249 | /// ``` | ||
| 250 | #[must_use = "set_payload_len returns a modified GenericPacketParams"] | ||
| 251 | pub const fn set_crc_type(mut self, crc_type: CrcType) -> GenericPacketParams { | ||
| 252 | self.buf[8] = crc_type as u8; | ||
| 253 | self | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Whitening enable. | ||
| 257 | /// | ||
| 258 | /// # Example | ||
| 259 | /// | ||
| 260 | /// Enable whitening. | ||
| 261 | /// | ||
| 262 | /// ``` | ||
| 263 | /// use stm32wlxx_hal::subghz::GenericPacketParams; | ||
| 264 | /// | ||
| 265 | /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_whitening_enable(true); | ||
| 266 | /// # assert_eq!(PKT_PARAMS.as_slice()[9], 1); | ||
| 267 | /// ``` | ||
| 268 | #[must_use = "set_whitening_enable returns a modified GenericPacketParams"] | ||
| 269 | pub const fn set_whitening_enable(mut self, en: bool) -> GenericPacketParams { | ||
| 270 | self.buf[9] = en as u8; | ||
| 271 | self | ||
| 272 | } | ||
| 273 | |||
| 274 | /// Extracts a slice containing the packet. | ||
| 275 | /// | ||
| 276 | /// # Example | ||
| 277 | /// | ||
| 278 | /// ``` | ||
| 279 | /// use stm32wlxx_hal::subghz::{ | ||
| 280 | /// AddrComp, CrcType, GenericPacketParams, HeaderType, PreambleDetection, | ||
| 281 | /// }; | ||
| 282 | /// | ||
| 283 | /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new() | ||
| 284 | /// .set_preamble_len(8) | ||
| 285 | /// .set_preamble_detection(PreambleDetection::Disabled) | ||
| 286 | /// .set_sync_word_len(2) | ||
| 287 | /// .set_addr_comp(AddrComp::Disabled) | ||
| 288 | /// .set_header_type(HeaderType::Fixed) | ||
| 289 | /// .set_payload_len(128) | ||
| 290 | /// .set_crc_type(CrcType::Byte2) | ||
| 291 | /// .set_whitening_enable(true); | ||
| 292 | /// | ||
| 293 | /// assert_eq!( | ||
| 294 | /// PKT_PARAMS.as_slice(), | ||
| 295 | /// &[0x8C, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x02, 0x01] | ||
| 296 | /// ); | ||
| 297 | /// ``` | ||
| 298 | pub const fn as_slice(&self) -> &[u8] { | ||
| 299 | &self.buf | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | impl Default for GenericPacketParams { | ||
| 304 | fn default() -> Self { | ||
| 305 | Self::new() | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Packet parameters for [`set_lora_packet_params`]. | ||
| 310 | /// | ||
| 311 | /// [`set_lora_packet_params`]: super::SubGhz::set_lora_packet_params | ||
| 312 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 313 | pub struct LoRaPacketParams { | ||
| 314 | buf: [u8; 7], | ||
| 315 | } | ||
| 316 | |||
| 317 | impl LoRaPacketParams { | ||
| 318 | /// Create a new `LoRaPacketParams`. | ||
| 319 | /// | ||
| 320 | /// This is the same as `default`, but in a `const` function. | ||
| 321 | /// | ||
| 322 | /// # Example | ||
| 323 | /// | ||
| 324 | /// ``` | ||
| 325 | /// use stm32wlxx_hal::subghz::LoRaPacketParams; | ||
| 326 | /// | ||
| 327 | /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new(); | ||
| 328 | /// assert_eq!(PKT_PARAMS, LoRaPacketParams::default()); | ||
| 329 | /// ``` | ||
| 330 | pub const fn new() -> LoRaPacketParams { | ||
| 331 | const OPCODE: u8 = super::OpCode::SetPacketParams as u8; | ||
| 332 | // const variable ensure the compile always optimizes the methods | ||
| 333 | const NEW: LoRaPacketParams = LoRaPacketParams { | ||
| 334 | buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], | ||
| 335 | } | ||
| 336 | .set_preamble_len(1) | ||
| 337 | .set_header_type(HeaderType::Fixed) | ||
| 338 | .set_payload_len(1) | ||
| 339 | .set_crc_en(true) | ||
| 340 | .set_invert_iq(false); | ||
| 341 | |||
| 342 | NEW | ||
| 343 | } | ||
| 344 | |||
| 345 | /// Preamble length in number of symbols. | ||
| 346 | /// | ||
| 347 | /// Values of zero are invalid, and will automatically be set to 1. | ||
| 348 | /// | ||
| 349 | /// # Example | ||
| 350 | /// | ||
| 351 | /// ``` | ||
| 352 | /// use stm32wlxx_hal::subghz::LoRaPacketParams; | ||
| 353 | /// | ||
| 354 | /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_preamble_len(0x1234); | ||
| 355 | /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12); | ||
| 356 | /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34); | ||
| 357 | /// ``` | ||
| 358 | #[must_use = "preamble_length returns a modified LoRaPacketParams"] | ||
| 359 | pub const fn set_preamble_len(mut self, mut len: u16) -> LoRaPacketParams { | ||
| 360 | if len == 0 { | ||
| 361 | len = 1 | ||
| 362 | } | ||
| 363 | self.buf[1] = ((len >> 8) & 0xFF) as u8; | ||
| 364 | self.buf[2] = (len & 0xFF) as u8; | ||
| 365 | self | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Header type (fixed or variable). | ||
| 369 | /// | ||
| 370 | /// # Example | ||
| 371 | /// | ||
| 372 | /// Set the payload type to a fixed length. | ||
| 373 | /// | ||
| 374 | /// ``` | ||
| 375 | /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams}; | ||
| 376 | /// | ||
| 377 | /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_header_type(HeaderType::Fixed); | ||
| 378 | /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x01); | ||
| 379 | /// ``` | ||
| 380 | #[must_use = "set_header_type returns a modified LoRaPacketParams"] | ||
| 381 | pub const fn set_header_type(mut self, header_type: HeaderType) -> LoRaPacketParams { | ||
| 382 | self.buf[3] = header_type.to_bits_lora(); | ||
| 383 | self | ||
| 384 | } | ||
| 385 | |||
| 386 | /// Set the payload length in bytes. | ||
| 387 | /// | ||
| 388 | /// # Example | ||
| 389 | /// | ||
| 390 | /// ``` | ||
| 391 | /// use stm32wlxx_hal::subghz::LoRaPacketParams; | ||
| 392 | /// | ||
| 393 | /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_payload_len(12); | ||
| 394 | /// # assert_eq!(PKT_PARAMS.as_slice()[4], 12); | ||
| 395 | /// ``` | ||
| 396 | #[must_use = "set_payload_len returns a modified LoRaPacketParams"] | ||
| 397 | pub const fn set_payload_len(mut self, len: u8) -> LoRaPacketParams { | ||
| 398 | self.buf[4] = len; | ||
| 399 | self | ||
| 400 | } | ||
| 401 | |||
| 402 | /// CRC enable. | ||
| 403 | /// | ||
| 404 | /// # Example | ||
| 405 | /// | ||
| 406 | /// Enable CRC. | ||
| 407 | /// | ||
| 408 | /// ``` | ||
| 409 | /// use stm32wlxx_hal::subghz::LoRaPacketParams; | ||
| 410 | /// | ||
| 411 | /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_crc_en(true); | ||
| 412 | /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x1); | ||
| 413 | /// ``` | ||
| 414 | #[must_use = "set_crc_en returns a modified LoRaPacketParams"] | ||
| 415 | pub const fn set_crc_en(mut self, en: bool) -> LoRaPacketParams { | ||
| 416 | self.buf[5] = en as u8; | ||
| 417 | self | ||
| 418 | } | ||
| 419 | |||
| 420 | /// IQ setup. | ||
| 421 | /// | ||
| 422 | /// # Example | ||
| 423 | /// | ||
| 424 | /// Use an inverted IQ setup. | ||
| 425 | /// | ||
| 426 | /// ``` | ||
| 427 | /// use stm32wlxx_hal::subghz::LoRaPacketParams; | ||
| 428 | /// | ||
| 429 | /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_invert_iq(true); | ||
| 430 | /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x1); | ||
| 431 | /// ``` | ||
| 432 | #[must_use = "set_invert_iq returns a modified LoRaPacketParams"] | ||
| 433 | pub const fn set_invert_iq(mut self, invert: bool) -> LoRaPacketParams { | ||
| 434 | self.buf[6] = invert as u8; | ||
| 435 | self | ||
| 436 | } | ||
| 437 | |||
| 438 | /// Extracts a slice containing the packet. | ||
| 439 | /// | ||
| 440 | /// # Example | ||
| 441 | /// | ||
| 442 | /// ``` | ||
| 443 | /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams}; | ||
| 444 | /// | ||
| 445 | /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new() | ||
| 446 | /// .set_preamble_len(5 * 8) | ||
| 447 | /// .set_header_type(HeaderType::Fixed) | ||
| 448 | /// .set_payload_len(64) | ||
| 449 | /// .set_crc_en(true) | ||
| 450 | /// .set_invert_iq(true); | ||
| 451 | /// | ||
| 452 | /// assert_eq!( | ||
| 453 | /// PKT_PARAMS.as_slice(), | ||
| 454 | /// &[0x8C, 0x00, 0x28, 0x01, 0x40, 0x01, 0x01] | ||
| 455 | /// ); | ||
| 456 | /// ``` | ||
| 457 | pub const fn as_slice(&self) -> &[u8] { | ||
| 458 | &self.buf | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | impl Default for LoRaPacketParams { | ||
| 463 | fn default() -> Self { | ||
| 464 | Self::new() | ||
| 465 | } | ||
| 466 | } | ||
| 467 | |||
| 468 | /// Packet parameters for [`set_bpsk_packet_params`]. | ||
| 469 | /// | ||
| 470 | /// [`set_bpsk_packet_params`]: super::SubGhz::set_bpsk_packet_params | ||
| 471 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 472 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 473 | pub struct BpskPacketParams { | ||
| 474 | buf: [u8; 2], | ||
| 475 | } | ||
| 476 | |||
| 477 | impl BpskPacketParams { | ||
| 478 | /// Create a new `BpskPacketParams`. | ||
| 479 | /// | ||
| 480 | /// This is the same as `default`, but in a `const` function. | ||
| 481 | /// | ||
| 482 | /// # Example | ||
| 483 | /// | ||
| 484 | /// ``` | ||
| 485 | /// use stm32wlxx_hal::subghz::BpskPacketParams; | ||
| 486 | /// | ||
| 487 | /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new(); | ||
| 488 | /// assert_eq!(PKT_PARAMS, BpskPacketParams::default()); | ||
| 489 | /// ``` | ||
| 490 | pub const fn new() -> BpskPacketParams { | ||
| 491 | BpskPacketParams { | ||
| 492 | buf: [super::OpCode::SetPacketParams as u8, 0x00], | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | /// Set the payload length in bytes. | ||
| 497 | /// | ||
| 498 | /// The length includes preamble, sync word, device ID, and CRC. | ||
| 499 | /// | ||
| 500 | /// # Example | ||
| 501 | /// | ||
| 502 | /// ``` | ||
| 503 | /// use stm32wlxx_hal::subghz::BpskPacketParams; | ||
| 504 | /// | ||
| 505 | /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(12); | ||
| 506 | /// # assert_eq!(PKT_PARAMS.as_slice()[1], 12); | ||
| 507 | /// ``` | ||
| 508 | #[must_use = "set_payload_len returns a modified BpskPacketParams"] | ||
| 509 | pub const fn set_payload_len(mut self, len: u8) -> BpskPacketParams { | ||
| 510 | self.buf[1] = len; | ||
| 511 | self | ||
| 512 | } | ||
| 513 | |||
| 514 | /// Extracts a slice containing the packet. | ||
| 515 | /// | ||
| 516 | /// # Example | ||
| 517 | /// | ||
| 518 | /// ``` | ||
| 519 | /// use stm32wlxx_hal::subghz::{BpskPacketParams, HeaderType}; | ||
| 520 | /// | ||
| 521 | /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(24); | ||
| 522 | /// | ||
| 523 | /// assert_eq!(PKT_PARAMS.as_slice(), &[0x8C, 24]); | ||
| 524 | /// ``` | ||
| 525 | pub const fn as_slice(&self) -> &[u8] { | ||
| 526 | &self.buf | ||
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | impl Default for BpskPacketParams { | ||
| 531 | fn default() -> Self { | ||
| 532 | Self::new() | ||
| 533 | } | ||
| 534 | } | ||
diff --git a/embassy-stm32/src/subghz/packet_status.rs b/embassy-stm32/src/subghz/packet_status.rs deleted file mode 100644 index b3acd73ce..000000000 --- a/embassy-stm32/src/subghz/packet_status.rs +++ /dev/null | |||
| @@ -1,282 +0,0 @@ | |||
| 1 | use super::{Ratio, Status}; | ||
| 2 | |||
| 3 | /// (G)FSK packet status. | ||
| 4 | /// | ||
| 5 | /// Returned by [`fsk_packet_status`]. | ||
| 6 | /// | ||
| 7 | /// [`fsk_packet_status`]: super::SubGhz::fsk_packet_status | ||
| 8 | #[derive(Clone, Copy, PartialEq, Eq)] | ||
| 9 | pub struct FskPacketStatus { | ||
| 10 | buf: [u8; 4], | ||
| 11 | } | ||
| 12 | |||
| 13 | impl From<[u8; 4]> for FskPacketStatus { | ||
| 14 | fn from(buf: [u8; 4]) -> Self { | ||
| 15 | FskPacketStatus { buf } | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | impl FskPacketStatus { | ||
| 20 | /// Get the status. | ||
| 21 | /// | ||
| 22 | /// # Example | ||
| 23 | /// | ||
| 24 | /// ``` | ||
| 25 | /// use stm32wlxx_hal::subghz::{CmdStatus, FskPacketStatus, Status, StatusMode}; | ||
| 26 | /// | ||
| 27 | /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0]; | ||
| 28 | /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio); | ||
| 29 | /// let status: Status = pkt_status.status(); | ||
| 30 | /// assert_eq!(status.mode(), Ok(StatusMode::Rx)); | ||
| 31 | /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable)); | ||
| 32 | /// ``` | ||
| 33 | pub const fn status(&self) -> Status { | ||
| 34 | Status::from_raw(self.buf[0]) | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Returns `true` if a preamble error occurred. | ||
| 38 | pub const fn preamble_err(&self) -> bool { | ||
| 39 | (self.buf[1] & (1 << 7)) != 0 | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Returns `true` if a synchronization error occurred. | ||
| 43 | pub const fn sync_err(&self) -> bool { | ||
| 44 | (self.buf[1] & (1 << 6)) != 0 | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Returns `true` if an address error occurred. | ||
| 48 | pub const fn addr_err(&self) -> bool { | ||
| 49 | (self.buf[1] & (1 << 5)) != 0 | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Returns `true` if an CRC error occurred. | ||
| 53 | pub const fn crc_err(&self) -> bool { | ||
| 54 | (self.buf[1] & (1 << 4)) != 0 | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Returns `true` if a length error occurred. | ||
| 58 | pub const fn length_err(&self) -> bool { | ||
| 59 | (self.buf[1] & (1 << 3)) != 0 | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Returns `true` if an abort error occurred. | ||
| 63 | pub const fn abort_err(&self) -> bool { | ||
| 64 | (self.buf[1] & (1 << 2)) != 0 | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Returns `true` if a packet is received. | ||
| 68 | pub const fn pkt_received(&self) -> bool { | ||
| 69 | (self.buf[1] & (1 << 1)) != 0 | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Returns `true` when a packet has been sent. | ||
| 73 | pub const fn pkt_sent(&self) -> bool { | ||
| 74 | (self.buf[1] & 1) != 0 | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Returns `true` if any error occurred. | ||
| 78 | pub const fn any_err(&self) -> bool { | ||
| 79 | (self.buf[1] & 0xFC) != 0 | ||
| 80 | } | ||
| 81 | |||
| 82 | /// RSSI level when the synchronization address is detected. | ||
| 83 | /// | ||
| 84 | /// Units are in dBm. | ||
| 85 | /// | ||
| 86 | /// # Example | ||
| 87 | /// | ||
| 88 | /// ``` | ||
| 89 | /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio}; | ||
| 90 | /// | ||
| 91 | /// let example_data_from_radio: [u8; 4] = [0, 0, 80, 0]; | ||
| 92 | /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio); | ||
| 93 | /// assert_eq!(pkt_status.rssi_sync().to_integer(), -40); | ||
| 94 | /// ``` | ||
| 95 | pub fn rssi_sync(&self) -> Ratio<i16> { | ||
| 96 | Ratio::new_raw(i16::from(self.buf[2]), -2) | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Return the RSSI level over the received packet. | ||
| 100 | /// | ||
| 101 | /// Units are in dBm. | ||
| 102 | /// | ||
| 103 | /// # Example | ||
| 104 | /// | ||
| 105 | /// ``` | ||
| 106 | /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio}; | ||
| 107 | /// | ||
| 108 | /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 100]; | ||
| 109 | /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio); | ||
| 110 | /// assert_eq!(pkt_status.rssi_avg().to_integer(), -50); | ||
| 111 | /// ``` | ||
| 112 | pub fn rssi_avg(&self) -> Ratio<i16> { | ||
| 113 | Ratio::new_raw(i16::from(self.buf[3]), -2) | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | #[cfg(feature = "defmt")] | ||
| 118 | impl defmt::Format for FskPacketStatus { | ||
| 119 | fn format(&self, fmt: defmt::Formatter) { | ||
| 120 | defmt::write!( | ||
| 121 | fmt, | ||
| 122 | r#"FskPacketStatus {{ | ||
| 123 | status: {}, | ||
| 124 | preamble_err: {}, | ||
| 125 | sync_err: {}, | ||
| 126 | addr_err: {}, | ||
| 127 | crc_err: {}, | ||
| 128 | length_err: {}, | ||
| 129 | abort_err: {}, | ||
| 130 | pkt_received: {}, | ||
| 131 | pkt_sent: {}, | ||
| 132 | rssi_sync: {}, | ||
| 133 | rssi_avg: {}, | ||
| 134 | }}"#, | ||
| 135 | self.status(), | ||
| 136 | self.preamble_err(), | ||
| 137 | self.sync_err(), | ||
| 138 | self.addr_err(), | ||
| 139 | self.crc_err(), | ||
| 140 | self.length_err(), | ||
| 141 | self.abort_err(), | ||
| 142 | self.pkt_received(), | ||
| 143 | self.pkt_sent(), | ||
| 144 | self.rssi_sync().to_integer(), | ||
| 145 | self.rssi_avg().to_integer() | ||
| 146 | ) | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | impl core::fmt::Debug for FskPacketStatus { | ||
| 151 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 152 | f.debug_struct("FskPacketStatus") | ||
| 153 | .field("status", &self.status()) | ||
| 154 | .field("preamble_err", &self.preamble_err()) | ||
| 155 | .field("sync_err", &self.sync_err()) | ||
| 156 | .field("addr_err", &self.addr_err()) | ||
| 157 | .field("crc_err", &self.crc_err()) | ||
| 158 | .field("length_err", &self.length_err()) | ||
| 159 | .field("abort_err", &self.abort_err()) | ||
| 160 | .field("pkt_received", &self.pkt_received()) | ||
| 161 | .field("pkt_sent", &self.pkt_sent()) | ||
| 162 | .field("rssi_sync", &self.rssi_sync().to_integer()) | ||
| 163 | .field("rssi_avg", &self.rssi_avg().to_integer()) | ||
| 164 | .finish() | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | /// (G)FSK packet status. | ||
| 169 | /// | ||
| 170 | /// Returned by [`lora_packet_status`]. | ||
| 171 | /// | ||
| 172 | /// [`lora_packet_status`]: super::SubGhz::lora_packet_status | ||
| 173 | #[derive(Clone, Copy, PartialEq, Eq)] | ||
| 174 | pub struct LoRaPacketStatus { | ||
| 175 | buf: [u8; 4], | ||
| 176 | } | ||
| 177 | |||
| 178 | impl From<[u8; 4]> for LoRaPacketStatus { | ||
| 179 | fn from(buf: [u8; 4]) -> Self { | ||
| 180 | LoRaPacketStatus { buf } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | impl LoRaPacketStatus { | ||
| 185 | /// Get the status. | ||
| 186 | /// | ||
| 187 | /// # Example | ||
| 188 | /// | ||
| 189 | /// ``` | ||
| 190 | /// use stm32wlxx_hal::subghz::{CmdStatus, LoRaPacketStatus, Status, StatusMode}; | ||
| 191 | /// | ||
| 192 | /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0]; | ||
| 193 | /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); | ||
| 194 | /// let status: Status = pkt_status.status(); | ||
| 195 | /// assert_eq!(status.mode(), Ok(StatusMode::Rx)); | ||
| 196 | /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable)); | ||
| 197 | /// ``` | ||
| 198 | pub const fn status(&self) -> Status { | ||
| 199 | Status::from_raw(self.buf[0]) | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Average RSSI level over the received packet. | ||
| 203 | /// | ||
| 204 | /// Units are in dBm. | ||
| 205 | /// | ||
| 206 | /// # Example | ||
| 207 | /// | ||
| 208 | /// ``` | ||
| 209 | /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio}; | ||
| 210 | /// | ||
| 211 | /// let example_data_from_radio: [u8; 4] = [0, 80, 0, 0]; | ||
| 212 | /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); | ||
| 213 | /// assert_eq!(pkt_status.rssi_pkt().to_integer(), -40); | ||
| 214 | /// ``` | ||
| 215 | pub fn rssi_pkt(&self) -> Ratio<i16> { | ||
| 216 | Ratio::new_raw(i16::from(self.buf[1]), -2) | ||
| 217 | } | ||
| 218 | |||
| 219 | /// Estimation of SNR over the received packet. | ||
| 220 | /// | ||
| 221 | /// Units are in dB. | ||
| 222 | /// | ||
| 223 | /// # Example | ||
| 224 | /// | ||
| 225 | /// ``` | ||
| 226 | /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio}; | ||
| 227 | /// | ||
| 228 | /// let example_data_from_radio: [u8; 4] = [0, 0, 40, 0]; | ||
| 229 | /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); | ||
| 230 | /// assert_eq!(pkt_status.snr_pkt().to_integer(), 10); | ||
| 231 | /// ``` | ||
| 232 | pub fn snr_pkt(&self) -> Ratio<i16> { | ||
| 233 | Ratio::new_raw(i16::from(self.buf[2]), 4) | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Estimation of RSSI level of the LoRa signal after despreading. | ||
| 237 | /// | ||
| 238 | /// Units are in dBm. | ||
| 239 | /// | ||
| 240 | /// # Example | ||
| 241 | /// | ||
| 242 | /// ``` | ||
| 243 | /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio}; | ||
| 244 | /// | ||
| 245 | /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 80]; | ||
| 246 | /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); | ||
| 247 | /// assert_eq!(pkt_status.signal_rssi_pkt().to_integer(), -40); | ||
| 248 | /// ``` | ||
| 249 | pub fn signal_rssi_pkt(&self) -> Ratio<i16> { | ||
| 250 | Ratio::new_raw(i16::from(self.buf[3]), -2) | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | #[cfg(feature = "defmt")] | ||
| 255 | impl defmt::Format for LoRaPacketStatus { | ||
| 256 | fn format(&self, fmt: defmt::Formatter) { | ||
| 257 | defmt::write!( | ||
| 258 | fmt, | ||
| 259 | r#"LoRaPacketStatus {{ | ||
| 260 | status: {}, | ||
| 261 | rssi_pkt: {}, | ||
| 262 | snr_pkt: {}, | ||
| 263 | signal_rssi_pkt: {}, | ||
| 264 | }}"#, | ||
| 265 | self.status(), | ||
| 266 | self.rssi_pkt().to_integer(), | ||
| 267 | self.snr_pkt().to_integer(), | ||
| 268 | self.signal_rssi_pkt().to_integer(), | ||
| 269 | ) | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | impl core::fmt::Debug for LoRaPacketStatus { | ||
| 274 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 275 | f.debug_struct("LoRaPacketStatus") | ||
| 276 | .field("status", &self.status()) | ||
| 277 | .field("rssi_pkt", &self.rssi_pkt().to_integer()) | ||
| 278 | .field("snr_pkt", &self.snr_pkt().to_integer()) | ||
| 279 | .field("signal_rssi_pkt", &self.signal_rssi_pkt().to_integer()) | ||
| 280 | .finish() | ||
| 281 | } | ||
| 282 | } | ||
diff --git a/embassy-stm32/src/subghz/packet_type.rs b/embassy-stm32/src/subghz/packet_type.rs deleted file mode 100644 index 88c62bb6a..000000000 --- a/embassy-stm32/src/subghz/packet_type.rs +++ /dev/null | |||
| @@ -1,44 +0,0 @@ | |||
| 1 | /// Packet type definition. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_packet_type`] | ||
| 4 | /// | ||
| 5 | /// [`set_packet_type`]: super::SubGhz::set_packet_type | ||
| 6 | #[repr(u8)] | ||
| 7 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 9 | pub enum PacketType { | ||
| 10 | /// FSK (frequency shift keying) generic packet type. | ||
| 11 | Fsk = 0, | ||
| 12 | /// LoRa (long range) packet type. | ||
| 13 | LoRa = 1, | ||
| 14 | /// BPSK (binary phase shift keying) packet type. | ||
| 15 | Bpsk = 2, | ||
| 16 | /// MSK (minimum shift keying) generic packet type. | ||
| 17 | Msk = 3, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl PacketType { | ||
| 21 | /// Create a new `PacketType` from bits. | ||
| 22 | /// | ||
| 23 | /// # Example | ||
| 24 | /// | ||
| 25 | /// ``` | ||
| 26 | /// use stm32wlxx_hal::subghz::PacketType; | ||
| 27 | /// | ||
| 28 | /// assert_eq!(PacketType::from_raw(0), Ok(PacketType::Fsk)); | ||
| 29 | /// assert_eq!(PacketType::from_raw(1), Ok(PacketType::LoRa)); | ||
| 30 | /// assert_eq!(PacketType::from_raw(2), Ok(PacketType::Bpsk)); | ||
| 31 | /// assert_eq!(PacketType::from_raw(3), Ok(PacketType::Msk)); | ||
| 32 | /// // Other values are reserved | ||
| 33 | /// assert_eq!(PacketType::from_raw(4), Err(4)); | ||
| 34 | /// ``` | ||
| 35 | pub const fn from_raw(bits: u8) -> Result<PacketType, u8> { | ||
| 36 | match bits { | ||
| 37 | 0 => Ok(PacketType::Fsk), | ||
| 38 | 1 => Ok(PacketType::LoRa), | ||
| 39 | 2 => Ok(PacketType::Bpsk), | ||
| 40 | 3 => Ok(PacketType::Msk), | ||
| 41 | _ => Err(bits), | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/embassy-stm32/src/subghz/pkt_ctrl.rs b/embassy-stm32/src/subghz/pkt_ctrl.rs deleted file mode 100644 index 265833e35..000000000 --- a/embassy-stm32/src/subghz/pkt_ctrl.rs +++ /dev/null | |||
| @@ -1,247 +0,0 @@ | |||
| 1 | /// Generic packet infinite sequence selection. | ||
| 2 | /// | ||
| 3 | /// Argument of [`PktCtrl::set_inf_seq_sel`]. | ||
| 4 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | pub enum InfSeqSel { | ||
| 7 | /// Preamble `0x5555`. | ||
| 8 | Five = 0b00, | ||
| 9 | /// Preamble `0x0000`. | ||
| 10 | Zero = 0b01, | ||
| 11 | /// Preamble `0xFFFF`. | ||
| 12 | One = 0b10, | ||
| 13 | /// PRBS9. | ||
| 14 | Prbs9 = 0b11, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl Default for InfSeqSel { | ||
| 18 | fn default() -> Self { | ||
| 19 | InfSeqSel::Five | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Generic packet control. | ||
| 24 | /// | ||
| 25 | /// Argument of [`set_pkt_ctrl`](super::SubGhz::set_pkt_ctrl). | ||
| 26 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | pub struct PktCtrl { | ||
| 29 | val: u8, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl PktCtrl { | ||
| 33 | /// Reset value of the packet control register. | ||
| 34 | pub const RESET: PktCtrl = PktCtrl { val: 0x21 }; | ||
| 35 | |||
| 36 | /// Create a new [`PktCtrl`] structure from a raw value. | ||
| 37 | /// | ||
| 38 | /// Reserved bits will be masked. | ||
| 39 | pub const fn from_raw(raw: u8) -> Self { | ||
| 40 | Self { val: raw & 0x3F } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Get the raw value of the [`PktCtrl`] register. | ||
| 44 | pub const fn as_bits(&self) -> u8 { | ||
| 45 | self.val | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Generic packet synchronization word detection enable. | ||
| 49 | /// | ||
| 50 | /// # Example | ||
| 51 | /// | ||
| 52 | /// ``` | ||
| 53 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 54 | /// | ||
| 55 | /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_sync_det_en(true); | ||
| 56 | /// ``` | ||
| 57 | #[must_use = "set_sync_det_en returns a modified PktCtrl"] | ||
| 58 | pub const fn set_sync_det_en(mut self, en: bool) -> PktCtrl { | ||
| 59 | if en { | ||
| 60 | self.val |= 1 << 5; | ||
| 61 | } else { | ||
| 62 | self.val &= !(1 << 5); | ||
| 63 | } | ||
| 64 | self | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Returns `true` if generic packet synchronization word detection is | ||
| 68 | /// enabled. | ||
| 69 | /// | ||
| 70 | /// # Example | ||
| 71 | /// | ||
| 72 | /// ``` | ||
| 73 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 74 | /// | ||
| 75 | /// let pc: PktCtrl = PktCtrl::RESET; | ||
| 76 | /// assert_eq!(pc.sync_det_en(), true); | ||
| 77 | /// let pc: PktCtrl = pc.set_sync_det_en(false); | ||
| 78 | /// assert_eq!(pc.sync_det_en(), false); | ||
| 79 | /// let pc: PktCtrl = pc.set_sync_det_en(true); | ||
| 80 | /// assert_eq!(pc.sync_det_en(), true); | ||
| 81 | /// ``` | ||
| 82 | pub const fn sync_det_en(&self) -> bool { | ||
| 83 | self.val & (1 << 5) != 0 | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Generic packet continuous transmit enable. | ||
| 87 | /// | ||
| 88 | /// # Example | ||
| 89 | /// | ||
| 90 | /// ``` | ||
| 91 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 92 | /// | ||
| 93 | /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_cont_tx_en(true); | ||
| 94 | /// ``` | ||
| 95 | #[must_use = "set_cont_tx_en returns a modified PktCtrl"] | ||
| 96 | pub const fn set_cont_tx_en(mut self, en: bool) -> PktCtrl { | ||
| 97 | if en { | ||
| 98 | self.val |= 1 << 4; | ||
| 99 | } else { | ||
| 100 | self.val &= !(1 << 4); | ||
| 101 | } | ||
| 102 | self | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Returns `true` if generic packet continuous transmit is enabled. | ||
| 106 | /// | ||
| 107 | /// # Example | ||
| 108 | /// | ||
| 109 | /// ``` | ||
| 110 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 111 | /// | ||
| 112 | /// let pc: PktCtrl = PktCtrl::RESET; | ||
| 113 | /// assert_eq!(pc.cont_tx_en(), false); | ||
| 114 | /// let pc: PktCtrl = pc.set_cont_tx_en(true); | ||
| 115 | /// assert_eq!(pc.cont_tx_en(), true); | ||
| 116 | /// let pc: PktCtrl = pc.set_cont_tx_en(false); | ||
| 117 | /// assert_eq!(pc.cont_tx_en(), false); | ||
| 118 | /// ``` | ||
| 119 | pub const fn cont_tx_en(&self) -> bool { | ||
| 120 | self.val & (1 << 4) != 0 | ||
| 121 | } | ||
| 122 | |||
| 123 | /// Set the continuous sequence type. | ||
| 124 | #[must_use = "set_inf_seq_sel returns a modified PktCtrl"] | ||
| 125 | pub const fn set_inf_seq_sel(mut self, sel: InfSeqSel) -> PktCtrl { | ||
| 126 | self.val &= !(0b11 << 2); | ||
| 127 | self.val |= (sel as u8) << 2; | ||
| 128 | self | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Get the continuous sequence type. | ||
| 132 | /// | ||
| 133 | /// # Example | ||
| 134 | /// | ||
| 135 | /// ``` | ||
| 136 | /// use stm32wlxx_hal::subghz::{InfSeqSel, PktCtrl}; | ||
| 137 | /// | ||
| 138 | /// let pc: PktCtrl = PktCtrl::RESET; | ||
| 139 | /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five); | ||
| 140 | /// | ||
| 141 | /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Zero); | ||
| 142 | /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Zero); | ||
| 143 | /// | ||
| 144 | /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::One); | ||
| 145 | /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::One); | ||
| 146 | /// | ||
| 147 | /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Prbs9); | ||
| 148 | /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Prbs9); | ||
| 149 | /// | ||
| 150 | /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Five); | ||
| 151 | /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five); | ||
| 152 | /// ``` | ||
| 153 | pub const fn inf_seq_sel(&self) -> InfSeqSel { | ||
| 154 | match (self.val >> 2) & 0b11 { | ||
| 155 | 0b00 => InfSeqSel::Five, | ||
| 156 | 0b01 => InfSeqSel::Zero, | ||
| 157 | 0b10 => InfSeqSel::One, | ||
| 158 | _ => InfSeqSel::Prbs9, | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Enable infinite sequence generation. | ||
| 163 | /// | ||
| 164 | /// # Example | ||
| 165 | /// | ||
| 166 | /// ``` | ||
| 167 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 168 | /// | ||
| 169 | /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_inf_seq_en(true); | ||
| 170 | /// ``` | ||
| 171 | #[must_use = "set_inf_seq_en returns a modified PktCtrl"] | ||
| 172 | pub const fn set_inf_seq_en(mut self, en: bool) -> PktCtrl { | ||
| 173 | if en { | ||
| 174 | self.val |= 1 << 1; | ||
| 175 | } else { | ||
| 176 | self.val &= !(1 << 1); | ||
| 177 | } | ||
| 178 | self | ||
| 179 | } | ||
| 180 | |||
| 181 | /// Returns `true` if infinite sequence generation is enabled. | ||
| 182 | /// | ||
| 183 | /// # Example | ||
| 184 | /// | ||
| 185 | /// ``` | ||
| 186 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 187 | /// | ||
| 188 | /// let pc: PktCtrl = PktCtrl::RESET; | ||
| 189 | /// assert_eq!(pc.inf_seq_en(), false); | ||
| 190 | /// let pc: PktCtrl = pc.set_inf_seq_en(true); | ||
| 191 | /// assert_eq!(pc.inf_seq_en(), true); | ||
| 192 | /// let pc: PktCtrl = pc.set_inf_seq_en(false); | ||
| 193 | /// assert_eq!(pc.inf_seq_en(), false); | ||
| 194 | /// ``` | ||
| 195 | pub const fn inf_seq_en(&self) -> bool { | ||
| 196 | self.val & (1 << 1) != 0 | ||
| 197 | } | ||
| 198 | |||
| 199 | /// Set the value of bit-8 (9th bit) for generic packet whitening. | ||
| 200 | /// | ||
| 201 | /// # Example | ||
| 202 | /// | ||
| 203 | /// ``` | ||
| 204 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 205 | /// | ||
| 206 | /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_whitening_init(true); | ||
| 207 | /// ``` | ||
| 208 | #[must_use = "set_whitening_init returns a modified PktCtrl"] | ||
| 209 | pub const fn set_whitening_init(mut self, val: bool) -> PktCtrl { | ||
| 210 | if val { | ||
| 211 | self.val |= 1; | ||
| 212 | } else { | ||
| 213 | self.val &= !1; | ||
| 214 | } | ||
| 215 | self | ||
| 216 | } | ||
| 217 | |||
| 218 | /// Returns `true` if bit-8 of the generic packet whitening is set. | ||
| 219 | /// | ||
| 220 | /// # Example | ||
| 221 | /// | ||
| 222 | /// ``` | ||
| 223 | /// use stm32wlxx_hal::subghz::PktCtrl; | ||
| 224 | /// | ||
| 225 | /// let pc: PktCtrl = PktCtrl::RESET; | ||
| 226 | /// assert_eq!(pc.whitening_init(), true); | ||
| 227 | /// let pc: PktCtrl = pc.set_whitening_init(false); | ||
| 228 | /// assert_eq!(pc.whitening_init(), false); | ||
| 229 | /// let pc: PktCtrl = pc.set_whitening_init(true); | ||
| 230 | /// assert_eq!(pc.whitening_init(), true); | ||
| 231 | /// ``` | ||
| 232 | pub const fn whitening_init(&self) -> bool { | ||
| 233 | self.val & 0b1 != 0 | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | impl From<PktCtrl> for u8 { | ||
| 238 | fn from(pc: PktCtrl) -> Self { | ||
| 239 | pc.val | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | impl Default for PktCtrl { | ||
| 244 | fn default() -> Self { | ||
| 245 | Self::RESET | ||
| 246 | } | ||
| 247 | } | ||
diff --git a/embassy-stm32/src/subghz/pmode.rs b/embassy-stm32/src/subghz/pmode.rs deleted file mode 100644 index 0c07f3195..000000000 --- a/embassy-stm32/src/subghz/pmode.rs +++ /dev/null | |||
| @@ -1,27 +0,0 @@ | |||
| 1 | /// RX gain power modes. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_rx_gain`]. | ||
| 4 | /// | ||
| 5 | /// [`set_rx_gain`]: super::SubGhz::set_rx_gain | ||
| 6 | #[repr(u8)] | ||
| 7 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 9 | pub enum PMode { | ||
| 10 | /// Power saving mode. | ||
| 11 | /// | ||
| 12 | /// Reduces sensitivity. | ||
| 13 | #[allow(clippy::identity_op)] | ||
| 14 | PowerSaving = (0x25 << 2) | 0b00, | ||
| 15 | /// Boost mode level 1. | ||
| 16 | /// | ||
| 17 | /// Improves sensitivity at detriment of power consumption. | ||
| 18 | Boost1 = (0x25 << 2) | 0b01, | ||
| 19 | /// Boost mode level 2. | ||
| 20 | /// | ||
| 21 | /// Improves a set further sensitivity at detriment of power consumption. | ||
| 22 | Boost2 = (0x25 << 2) | 0b10, | ||
| 23 | /// Boost mode. | ||
| 24 | /// | ||
| 25 | /// Best receiver sensitivity. | ||
| 26 | Boost = (0x25 << 2) | 0b11, | ||
| 27 | } | ||
diff --git a/embassy-stm32/src/subghz/pwr_ctrl.rs b/embassy-stm32/src/subghz/pwr_ctrl.rs deleted file mode 100644 index 974bddebb..000000000 --- a/embassy-stm32/src/subghz/pwr_ctrl.rs +++ /dev/null | |||
| @@ -1,160 +0,0 @@ | |||
| 1 | /// Power-supply current limit. | ||
| 2 | /// | ||
| 3 | /// Argument of [`PwrCtrl::set_current_lim`]. | ||
| 4 | #[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | #[repr(u8)] | ||
| 7 | pub enum CurrentLim { | ||
| 8 | /// 25 mA | ||
| 9 | Milli25 = 0x0, | ||
| 10 | /// 50 mA (default) | ||
| 11 | Milli50 = 0x1, | ||
| 12 | /// 100 mA | ||
| 13 | Milli100 = 0x2, | ||
| 14 | /// 200 mA | ||
| 15 | Milli200 = 0x3, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl CurrentLim { | ||
| 19 | /// Get the SMPS drive value as milliamps. | ||
| 20 | /// | ||
| 21 | /// # Example | ||
| 22 | /// | ||
| 23 | /// ``` | ||
| 24 | /// use stm32wlxx_hal::subghz::CurrentLim; | ||
| 25 | /// | ||
| 26 | /// assert_eq!(CurrentLim::Milli25.as_milliamps(), 25); | ||
| 27 | /// assert_eq!(CurrentLim::Milli50.as_milliamps(), 50); | ||
| 28 | /// assert_eq!(CurrentLim::Milli100.as_milliamps(), 100); | ||
| 29 | /// assert_eq!(CurrentLim::Milli200.as_milliamps(), 200); | ||
| 30 | /// ``` | ||
| 31 | pub const fn as_milliamps(&self) -> u8 { | ||
| 32 | match self { | ||
| 33 | CurrentLim::Milli25 => 25, | ||
| 34 | CurrentLim::Milli50 => 50, | ||
| 35 | CurrentLim::Milli100 => 100, | ||
| 36 | CurrentLim::Milli200 => 200, | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | impl Default for CurrentLim { | ||
| 42 | fn default() -> Self { | ||
| 43 | CurrentLim::Milli50 | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Power control. | ||
| 48 | /// | ||
| 49 | /// Argument of [`set_bit_sync`](super::SubGhz::set_bit_sync). | ||
| 50 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 51 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 52 | pub struct PwrCtrl { | ||
| 53 | val: u8, | ||
| 54 | } | ||
| 55 | |||
| 56 | impl PwrCtrl { | ||
| 57 | /// Power control register reset value. | ||
| 58 | pub const RESET: PwrCtrl = PwrCtrl { val: 0x50 }; | ||
| 59 | |||
| 60 | /// Create a new [`PwrCtrl`] structure from a raw value. | ||
| 61 | /// | ||
| 62 | /// Reserved bits will be masked. | ||
| 63 | pub const fn from_raw(raw: u8) -> Self { | ||
| 64 | Self { val: raw & 0x70 } | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Get the raw value of the [`PwrCtrl`] register. | ||
| 68 | pub const fn as_bits(&self) -> u8 { | ||
| 69 | self.val | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Set the current limiter enable. | ||
| 73 | /// | ||
| 74 | /// # Example | ||
| 75 | /// | ||
| 76 | /// ``` | ||
| 77 | /// use stm32wlxx_hal::subghz::PwrCtrl; | ||
| 78 | /// | ||
| 79 | /// const PWR_CTRL: PwrCtrl = PwrCtrl::RESET.set_current_lim_en(true); | ||
| 80 | /// # assert_eq!(u8::from(PWR_CTRL), 0x50u8); | ||
| 81 | /// ``` | ||
| 82 | #[must_use = "set_current_lim_en returns a modified PwrCtrl"] | ||
| 83 | pub const fn set_current_lim_en(mut self, en: bool) -> PwrCtrl { | ||
| 84 | if en { | ||
| 85 | self.val |= 1 << 6; | ||
| 86 | } else { | ||
| 87 | self.val &= !(1 << 6); | ||
| 88 | } | ||
| 89 | self | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Returns `true` if current limiting is enabled | ||
| 93 | /// | ||
| 94 | /// # Example | ||
| 95 | /// | ||
| 96 | /// ``` | ||
| 97 | /// use stm32wlxx_hal::subghz::PwrCtrl; | ||
| 98 | /// | ||
| 99 | /// let pc: PwrCtrl = PwrCtrl::RESET; | ||
| 100 | /// assert_eq!(pc.current_limit_en(), true); | ||
| 101 | /// let pc: PwrCtrl = pc.set_current_lim_en(false); | ||
| 102 | /// assert_eq!(pc.current_limit_en(), false); | ||
| 103 | /// let pc: PwrCtrl = pc.set_current_lim_en(true); | ||
| 104 | /// assert_eq!(pc.current_limit_en(), true); | ||
| 105 | /// ``` | ||
| 106 | pub const fn current_limit_en(&self) -> bool { | ||
| 107 | self.val & (1 << 6) != 0 | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Set the current limit. | ||
| 111 | #[must_use = "set_current_lim returns a modified PwrCtrl"] | ||
| 112 | pub const fn set_current_lim(mut self, lim: CurrentLim) -> PwrCtrl { | ||
| 113 | self.val &= !(0x30); | ||
| 114 | self.val |= (lim as u8) << 4; | ||
| 115 | self | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Get the current limit. | ||
| 119 | /// | ||
| 120 | /// # Example | ||
| 121 | /// | ||
| 122 | /// ``` | ||
| 123 | /// use stm32wlxx_hal::subghz::{CurrentLim, PwrCtrl}; | ||
| 124 | /// | ||
| 125 | /// let pc: PwrCtrl = PwrCtrl::RESET; | ||
| 126 | /// assert_eq!(pc.current_lim(), CurrentLim::Milli50); | ||
| 127 | /// | ||
| 128 | /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli25); | ||
| 129 | /// assert_eq!(pc.current_lim(), CurrentLim::Milli25); | ||
| 130 | /// | ||
| 131 | /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli50); | ||
| 132 | /// assert_eq!(pc.current_lim(), CurrentLim::Milli50); | ||
| 133 | /// | ||
| 134 | /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli100); | ||
| 135 | /// assert_eq!(pc.current_lim(), CurrentLim::Milli100); | ||
| 136 | /// | ||
| 137 | /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli200); | ||
| 138 | /// assert_eq!(pc.current_lim(), CurrentLim::Milli200); | ||
| 139 | /// ``` | ||
| 140 | pub const fn current_lim(&self) -> CurrentLim { | ||
| 141 | match (self.val >> 4) & 0b11 { | ||
| 142 | 0x0 => CurrentLim::Milli25, | ||
| 143 | 0x1 => CurrentLim::Milli50, | ||
| 144 | 0x2 => CurrentLim::Milli100, | ||
| 145 | _ => CurrentLim::Milli200, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | impl From<PwrCtrl> for u8 { | ||
| 151 | fn from(bs: PwrCtrl) -> Self { | ||
| 152 | bs.val | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | impl Default for PwrCtrl { | ||
| 157 | fn default() -> Self { | ||
| 158 | Self::RESET | ||
| 159 | } | ||
| 160 | } | ||
diff --git a/embassy-stm32/src/subghz/reg_mode.rs b/embassy-stm32/src/subghz/reg_mode.rs deleted file mode 100644 index b83226954..000000000 --- a/embassy-stm32/src/subghz/reg_mode.rs +++ /dev/null | |||
| @@ -1,18 +0,0 @@ | |||
| 1 | /// Radio power supply selection. | ||
| 2 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
| 3 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 4 | #[repr(u8)] | ||
| 5 | pub enum RegMode { | ||
| 6 | /// Linear dropout regulator | ||
| 7 | Ldo = 0b0, | ||
| 8 | /// Switch mode power supply. | ||
| 9 | /// | ||
| 10 | /// Used in standby with HSE32, FS, RX, and TX modes. | ||
| 11 | Smps = 0b1, | ||
| 12 | } | ||
| 13 | |||
| 14 | impl Default for RegMode { | ||
| 15 | fn default() -> Self { | ||
| 16 | RegMode::Ldo | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/embassy-stm32/src/subghz/rf_frequency.rs b/embassy-stm32/src/subghz/rf_frequency.rs deleted file mode 100644 index 3de2f50c4..000000000 --- a/embassy-stm32/src/subghz/rf_frequency.rs +++ /dev/null | |||
| @@ -1,135 +0,0 @@ | |||
| 1 | /// RF frequency structure. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_rf_frequency`]. | ||
| 4 | /// | ||
| 5 | /// [`set_rf_frequency`]: super::SubGhz::set_rf_frequency | ||
| 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct RfFreq { | ||
| 9 | buf: [u8; 5], | ||
| 10 | } | ||
| 11 | |||
| 12 | impl RfFreq { | ||
| 13 | /// 915MHz, often used in Australia and North America. | ||
| 14 | /// | ||
| 15 | /// # Example | ||
| 16 | /// | ||
| 17 | /// ``` | ||
| 18 | /// use stm32wlxx_hal::subghz::RfFreq; | ||
| 19 | /// | ||
| 20 | /// assert_eq!(RfFreq::F915.freq(), 915_000_000); | ||
| 21 | /// ``` | ||
| 22 | pub const F915: RfFreq = RfFreq::from_raw(0x39_30_00_00); | ||
| 23 | |||
| 24 | /// 868MHz, often used in Europe. | ||
| 25 | /// | ||
| 26 | /// # Example | ||
| 27 | /// | ||
| 28 | /// ``` | ||
| 29 | /// use stm32wlxx_hal::subghz::RfFreq; | ||
| 30 | /// | ||
| 31 | /// assert_eq!(RfFreq::F868.freq(), 868_000_000); | ||
| 32 | /// ``` | ||
| 33 | pub const F868: RfFreq = RfFreq::from_raw(0x36_40_00_00); | ||
| 34 | |||
| 35 | /// 433MHz, often used in Europe. | ||
| 36 | /// | ||
| 37 | /// # Example | ||
| 38 | /// | ||
| 39 | /// ``` | ||
| 40 | /// use stm32wlxx_hal::subghz::RfFreq; | ||
| 41 | /// | ||
| 42 | /// assert_eq!(RfFreq::F433.freq(), 433_000_000); | ||
| 43 | /// ``` | ||
| 44 | pub const F433: RfFreq = RfFreq::from_raw(0x1B_10_00_00); | ||
| 45 | |||
| 46 | /// Create a new `RfFreq` from a raw bit value. | ||
| 47 | /// | ||
| 48 | /// The equation used to get the PLL frequency from the raw bits is: | ||
| 49 | /// | ||
| 50 | /// RF<sub>PLL</sub> = 32e6 × bits / 2<sup>25</sup> | ||
| 51 | /// | ||
| 52 | /// # Example | ||
| 53 | /// | ||
| 54 | /// ``` | ||
| 55 | /// use stm32wlxx_hal::subghz::RfFreq; | ||
| 56 | /// | ||
| 57 | /// const FREQ: RfFreq = RfFreq::from_raw(0x39300000); | ||
| 58 | /// assert_eq!(FREQ, RfFreq::F915); | ||
| 59 | /// ``` | ||
| 60 | pub const fn from_raw(bits: u32) -> RfFreq { | ||
| 61 | RfFreq { | ||
| 62 | buf: [ | ||
| 63 | super::OpCode::SetRfFrequency as u8, | ||
| 64 | ((bits >> 24) & 0xFF) as u8, | ||
| 65 | ((bits >> 16) & 0xFF) as u8, | ||
| 66 | ((bits >> 8) & 0xFF) as u8, | ||
| 67 | (bits & 0xFF) as u8, | ||
| 68 | ], | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Create a new `RfFreq` from a PLL frequency. | ||
| 73 | /// | ||
| 74 | /// The equation used to get the raw bits from the PLL frequency is: | ||
| 75 | /// | ||
| 76 | /// bits = RF<sub>PLL</sub> * 2<sup>25</sup> / 32e6 | ||
| 77 | /// | ||
| 78 | /// # Example | ||
| 79 | /// | ||
| 80 | /// ``` | ||
| 81 | /// use stm32wlxx_hal::subghz::RfFreq; | ||
| 82 | /// | ||
| 83 | /// const FREQ: RfFreq = RfFreq::from_frequency(915_000_000); | ||
| 84 | /// assert_eq!(FREQ, RfFreq::F915); | ||
| 85 | /// ``` | ||
| 86 | pub const fn from_frequency(freq: u32) -> RfFreq { | ||
| 87 | Self::from_raw((((freq as u64) * (1 << 25)) / 32_000_000) as u32) | ||
| 88 | } | ||
| 89 | |||
| 90 | // Get the frequency bit value. | ||
| 91 | const fn as_bits(&self) -> u32 { | ||
| 92 | ((self.buf[1] as u32) << 24) | ((self.buf[2] as u32) << 16) | ((self.buf[3] as u32) << 8) | (self.buf[4] as u32) | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Get the actual frequency. | ||
| 96 | /// | ||
| 97 | /// # Example | ||
| 98 | /// | ||
| 99 | /// ``` | ||
| 100 | /// use stm32wlxx_hal::subghz::RfFreq; | ||
| 101 | /// | ||
| 102 | /// assert_eq!(RfFreq::from_raw(0x39300000).freq(), 915_000_000); | ||
| 103 | /// ``` | ||
| 104 | pub fn freq(&self) -> u32 { | ||
| 105 | (32_000_000 * (self.as_bits() as u64) / (1 << 25)) as u32 | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Extracts a slice containing the packet. | ||
| 109 | /// | ||
| 110 | /// # Example | ||
| 111 | /// | ||
| 112 | /// ``` | ||
| 113 | /// use stm32wlxx_hal::subghz::RfFreq; | ||
| 114 | /// | ||
| 115 | /// assert_eq!(RfFreq::F915.as_slice(), &[0x86, 0x39, 0x30, 0x00, 0x00]); | ||
| 116 | /// ``` | ||
| 117 | pub const fn as_slice(&self) -> &[u8] { | ||
| 118 | &self.buf | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | #[cfg(test)] | ||
| 123 | mod test { | ||
| 124 | use super::RfFreq; | ||
| 125 | |||
| 126 | #[test] | ||
| 127 | fn max() { | ||
| 128 | assert_eq!(RfFreq::from_raw(u32::MAX).freq(), 4_095_999_999); | ||
| 129 | } | ||
| 130 | |||
| 131 | #[test] | ||
| 132 | fn min() { | ||
| 133 | assert_eq!(RfFreq::from_raw(u32::MIN).freq(), 0); | ||
| 134 | } | ||
| 135 | } | ||
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 1d4aaecee..000000000 --- a/embassy-stm32/src/subghz/rx_timeout_stop.rs +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | /// Receiver event which stops the RX timeout timer. | ||
| 2 | /// | ||
| 3 | /// Used by [`set_rx_timeout_stop`]. | ||
| 4 | /// | ||
| 5 | /// [`set_rx_timeout_stop`]: super::SubGhz::set_rx_timeout_stop | ||
| 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | #[repr(u8)] | ||
| 9 | pub enum RxTimeoutStop { | ||
| 10 | /// Receive timeout stopped on synchronization word detection in generic | ||
| 11 | /// packet mode or header detection in LoRa packet mode. | ||
| 12 | Sync = 0b0, | ||
| 13 | /// Receive timeout stopped on preamble detection. | ||
| 14 | Preamble = 0b1, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl From<RxTimeoutStop> for u8 { | ||
| 18 | fn from(rx_ts: RxTimeoutStop) -> Self { | ||
| 19 | rx_ts as u8 | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/embassy-stm32/src/subghz/sleep_cfg.rs b/embassy-stm32/src/subghz/sleep_cfg.rs deleted file mode 100644 index 0a50e9704..000000000 --- a/embassy-stm32/src/subghz/sleep_cfg.rs +++ /dev/null | |||
| @@ -1,107 +0,0 @@ | |||
| 1 | /// Startup configurations when exiting sleep mode. | ||
| 2 | /// | ||
| 3 | /// Argument of [`SleepCfg::set_startup`]. | ||
| 4 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | #[repr(u8)] | ||
| 7 | pub enum Startup { | ||
| 8 | /// Cold startup when exiting Sleep mode, configuration registers reset. | ||
| 9 | Cold = 0, | ||
| 10 | /// Warm startup when exiting Sleep mode, | ||
| 11 | /// configuration registers kept in retention. | ||
| 12 | /// | ||
| 13 | /// **Note:** Only the configuration of the activated modem, | ||
| 14 | /// before going to sleep mode, is retained. | ||
| 15 | /// The configuration of the other modes is lost and must be re-configured | ||
| 16 | /// when exiting sleep mode. | ||
| 17 | Warm = 1, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl Default for Startup { | ||
| 21 | fn default() -> Self { | ||
| 22 | Startup::Warm | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | /// Sleep configuration. | ||
| 27 | /// | ||
| 28 | /// Argument of [`set_sleep`]. | ||
| 29 | /// | ||
| 30 | /// [`set_sleep`]: super::SubGhz::set_sleep | ||
| 31 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 32 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 33 | pub struct SleepCfg(u8); | ||
| 34 | |||
| 35 | impl SleepCfg { | ||
| 36 | /// Create a new `SleepCfg` structure. | ||
| 37 | /// | ||
| 38 | /// This is the same as `default`, but in a `const` function. | ||
| 39 | /// | ||
| 40 | /// The defaults are a warm startup, with RTC wakeup enabled. | ||
| 41 | /// | ||
| 42 | /// # Example | ||
| 43 | /// | ||
| 44 | /// ``` | ||
| 45 | /// use stm32wlxx_hal::subghz::SleepCfg; | ||
| 46 | /// | ||
| 47 | /// const SLEEP_CFG: SleepCfg = SleepCfg::new(); | ||
| 48 | /// assert_eq!(SLEEP_CFG, SleepCfg::default()); | ||
| 49 | /// # assert_eq!(u8::from(SLEEP_CFG), 0b101); | ||
| 50 | /// ``` | ||
| 51 | pub const fn new() -> SleepCfg { | ||
| 52 | SleepCfg(0).set_startup(Startup::Warm).set_rtc_wakeup_en(true) | ||
| 53 | } | ||
| 54 | |||
| 55 | /// Set the startup mode. | ||
| 56 | /// | ||
| 57 | /// # Example | ||
| 58 | /// | ||
| 59 | /// ``` | ||
| 60 | /// use stm32wlxx_hal::subghz::{SleepCfg, Startup}; | ||
| 61 | /// | ||
| 62 | /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_startup(Startup::Cold); | ||
| 63 | /// # assert_eq!(u8::from(SLEEP_CFG), 0b001); | ||
| 64 | /// # assert_eq!(u8::from(SLEEP_CFG.set_startup(Startup::Warm)), 0b101); | ||
| 65 | /// ``` | ||
| 66 | pub const fn set_startup(mut self, startup: Startup) -> SleepCfg { | ||
| 67 | if startup as u8 == 1 { | ||
| 68 | self.0 |= 1 << 2 | ||
| 69 | } else { | ||
| 70 | self.0 &= !(1 << 2) | ||
| 71 | } | ||
| 72 | self | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Set the RTC wakeup enable. | ||
| 76 | /// | ||
| 77 | /// # Example | ||
| 78 | /// | ||
| 79 | /// ``` | ||
| 80 | /// use stm32wlxx_hal::subghz::SleepCfg; | ||
| 81 | /// | ||
| 82 | /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_rtc_wakeup_en(false); | ||
| 83 | /// # assert_eq!(u8::from(SLEEP_CFG), 0b100); | ||
| 84 | /// # assert_eq!(u8::from(SLEEP_CFG.set_rtc_wakeup_en(true)), 0b101); | ||
| 85 | /// ``` | ||
| 86 | #[must_use = "set_rtc_wakeup_en returns a modified SleepCfg"] | ||
| 87 | pub const fn set_rtc_wakeup_en(mut self, en: bool) -> SleepCfg { | ||
| 88 | if en { | ||
| 89 | self.0 |= 0b1 | ||
| 90 | } else { | ||
| 91 | self.0 &= !0b1 | ||
| 92 | } | ||
| 93 | self | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl From<SleepCfg> for u8 { | ||
| 98 | fn from(sc: SleepCfg) -> Self { | ||
| 99 | sc.0 | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl Default for SleepCfg { | ||
| 104 | fn default() -> Self { | ||
| 105 | Self::new() | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/embassy-stm32/src/subghz/smps.rs b/embassy-stm32/src/subghz/smps.rs deleted file mode 100644 index 81615ea7b..000000000 --- a/embassy-stm32/src/subghz/smps.rs +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | /// SMPS maximum drive capability. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_smps_drv`](super::SubGhz::set_smps_drv). | ||
| 4 | #[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | #[repr(u8)] | ||
| 7 | pub enum SmpsDrv { | ||
| 8 | /// 20 mA | ||
| 9 | Milli20 = 0x0, | ||
| 10 | /// 40 mA | ||
| 11 | Milli40 = 0x1, | ||
| 12 | /// 60 mA | ||
| 13 | Milli60 = 0x2, | ||
| 14 | /// 100 mA (default) | ||
| 15 | Milli100 = 0x3, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl SmpsDrv { | ||
| 19 | /// Get the SMPS drive value as milliamps. | ||
| 20 | /// | ||
| 21 | /// # Example | ||
| 22 | /// | ||
| 23 | /// ``` | ||
| 24 | /// use stm32wlxx_hal::subghz::SmpsDrv; | ||
| 25 | /// | ||
| 26 | /// assert_eq!(SmpsDrv::Milli20.as_milliamps(), 20); | ||
| 27 | /// assert_eq!(SmpsDrv::Milli40.as_milliamps(), 40); | ||
| 28 | /// assert_eq!(SmpsDrv::Milli60.as_milliamps(), 60); | ||
| 29 | /// assert_eq!(SmpsDrv::Milli100.as_milliamps(), 100); | ||
| 30 | /// ``` | ||
| 31 | pub const fn as_milliamps(&self) -> u8 { | ||
| 32 | match self { | ||
| 33 | SmpsDrv::Milli20 => 20, | ||
| 34 | SmpsDrv::Milli40 => 40, | ||
| 35 | SmpsDrv::Milli60 => 60, | ||
| 36 | SmpsDrv::Milli100 => 100, | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | impl Default for SmpsDrv { | ||
| 42 | fn default() -> Self { | ||
| 43 | SmpsDrv::Milli100 | ||
| 44 | } | ||
| 45 | } | ||
diff --git a/embassy-stm32/src/subghz/standby_clk.rs b/embassy-stm32/src/subghz/standby_clk.rs deleted file mode 100644 index c130bbee4..000000000 --- a/embassy-stm32/src/subghz/standby_clk.rs +++ /dev/null | |||
| @@ -1,20 +0,0 @@ | |||
| 1 | /// Clock in standby mode. | ||
| 2 | /// | ||
| 3 | /// Used by [`set_standby`]. | ||
| 4 | /// | ||
| 5 | /// [`set_standby`]: super::SubGhz::set_standby | ||
| 6 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | #[repr(u8)] | ||
| 9 | pub enum StandbyClk { | ||
| 10 | /// RC 13 MHz used in standby mode. | ||
| 11 | Rc = 0b0, | ||
| 12 | /// HSE32 used in standby mode. | ||
| 13 | Hse = 0b1, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl From<StandbyClk> for u8 { | ||
| 17 | fn from(sc: StandbyClk) -> Self { | ||
| 18 | sc as u8 | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/embassy-stm32/src/subghz/stats.rs b/embassy-stm32/src/subghz/stats.rs deleted file mode 100644 index 41b7a200f..000000000 --- a/embassy-stm32/src/subghz/stats.rs +++ /dev/null | |||
| @@ -1,184 +0,0 @@ | |||
| 1 | use super::Status; | ||
| 2 | |||
| 3 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 4 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 5 | pub struct LoRaStats; | ||
| 6 | |||
| 7 | impl LoRaStats { | ||
| 8 | pub const fn new() -> Self { | ||
| 9 | Self {} | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 15 | pub struct FskStats; | ||
| 16 | |||
| 17 | impl FskStats { | ||
| 18 | pub const fn new() -> Self { | ||
| 19 | Self {} | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Packet statistics. | ||
| 24 | /// | ||
| 25 | /// Returned by [`fsk_stats`] and [`lora_stats`]. | ||
| 26 | /// | ||
| 27 | /// [`fsk_stats`]: super::SubGhz::fsk_stats | ||
| 28 | /// [`lora_stats`]: super::SubGhz::lora_stats | ||
| 29 | #[derive(Eq, PartialEq, Clone, Copy)] | ||
| 30 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 31 | pub struct Stats<ModType> { | ||
| 32 | status: Status, | ||
| 33 | pkt_rx: u16, | ||
| 34 | pkt_crc: u16, | ||
| 35 | pkt_len_or_hdr_err: u16, | ||
| 36 | ty: ModType, | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<ModType> Stats<ModType> { | ||
| 40 | const fn from_buf(buf: [u8; 7], ty: ModType) -> Stats<ModType> { | ||
| 41 | Stats { | ||
| 42 | status: Status::from_raw(buf[0]), | ||
| 43 | pkt_rx: u16::from_be_bytes([buf[1], buf[2]]), | ||
| 44 | pkt_crc: u16::from_be_bytes([buf[3], buf[4]]), | ||
| 45 | pkt_len_or_hdr_err: u16::from_be_bytes([buf[5], buf[6]]), | ||
| 46 | ty, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Get the radio status returned with the packet statistics. | ||
| 51 | /// | ||
| 52 | /// # Example | ||
| 53 | /// | ||
| 54 | /// ``` | ||
| 55 | /// use stm32wlxx_hal::subghz::{CmdStatus, FskStats, Stats, StatusMode}; | ||
| 56 | /// | ||
| 57 | /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0]; | ||
| 58 | /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio); | ||
| 59 | /// assert_eq!(stats.status().mode(), Ok(StatusMode::Rx)); | ||
| 60 | /// assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable)); | ||
| 61 | /// ``` | ||
| 62 | pub const fn status(&self) -> Status { | ||
| 63 | self.status | ||
| 64 | } | ||
| 65 | |||
| 66 | /// Number of packets received. | ||
| 67 | /// | ||
| 68 | /// # Example | ||
| 69 | /// | ||
| 70 | /// ``` | ||
| 71 | /// use stm32wlxx_hal::subghz::{FskStats, Stats}; | ||
| 72 | /// | ||
| 73 | /// let example_data_from_radio: [u8; 7] = [0x54, 0, 3, 0, 0, 0, 0]; | ||
| 74 | /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio); | ||
| 75 | /// assert_eq!(stats.pkt_rx(), 3); | ||
| 76 | /// ``` | ||
| 77 | pub const fn pkt_rx(&self) -> u16 { | ||
| 78 | self.pkt_rx | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Number of packets received with a payload CRC error | ||
| 82 | /// | ||
| 83 | /// # Example | ||
| 84 | /// | ||
| 85 | /// ``` | ||
| 86 | /// use stm32wlxx_hal::subghz::{LoRaStats, Stats}; | ||
| 87 | /// | ||
| 88 | /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 1, 0, 0]; | ||
| 89 | /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio); | ||
| 90 | /// assert_eq!(stats.pkt_crc(), 1); | ||
| 91 | /// ``` | ||
| 92 | pub const fn pkt_crc(&self) -> u16 { | ||
| 93 | self.pkt_crc | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Stats<FskStats> { | ||
| 98 | /// Create a new FSK packet statistics structure from a raw buffer. | ||
| 99 | /// | ||
| 100 | /// # Example | ||
| 101 | /// | ||
| 102 | /// ``` | ||
| 103 | /// use stm32wlxx_hal::subghz::{FskStats, Stats}; | ||
| 104 | /// | ||
| 105 | /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0]; | ||
| 106 | /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio); | ||
| 107 | /// ``` | ||
| 108 | pub const fn from_raw_fsk(buf: [u8; 7]) -> Stats<FskStats> { | ||
| 109 | Self::from_buf(buf, FskStats::new()) | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Number of packets received with a payload length error. | ||
| 113 | /// | ||
| 114 | /// # Example | ||
| 115 | /// | ||
| 116 | /// ``` | ||
| 117 | /// use stm32wlxx_hal::subghz::{FskStats, Stats}; | ||
| 118 | /// | ||
| 119 | /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1]; | ||
| 120 | /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio); | ||
| 121 | /// assert_eq!(stats.pkt_len_err(), 1); | ||
| 122 | /// ``` | ||
| 123 | pub const fn pkt_len_err(&self) -> u16 { | ||
| 124 | self.pkt_len_or_hdr_err | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | impl Stats<LoRaStats> { | ||
| 129 | /// Create a new LoRa packet statistics structure from a raw buffer. | ||
| 130 | /// | ||
| 131 | /// # Example | ||
| 132 | /// | ||
| 133 | /// ``` | ||
| 134 | /// use stm32wlxx_hal::subghz::{LoRaStats, Stats}; | ||
| 135 | /// | ||
| 136 | /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0]; | ||
| 137 | /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio); | ||
| 138 | /// ``` | ||
| 139 | pub const fn from_raw_lora(buf: [u8; 7]) -> Stats<LoRaStats> { | ||
| 140 | Self::from_buf(buf, LoRaStats::new()) | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Number of packets received with a header CRC error. | ||
| 144 | /// | ||
| 145 | /// # Example | ||
| 146 | /// | ||
| 147 | /// ``` | ||
| 148 | /// use stm32wlxx_hal::subghz::{LoRaStats, Stats}; | ||
| 149 | /// | ||
| 150 | /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1]; | ||
| 151 | /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio); | ||
| 152 | /// assert_eq!(stats.pkt_hdr_err(), 1); | ||
| 153 | /// ``` | ||
| 154 | pub const fn pkt_hdr_err(&self) -> u16 { | ||
| 155 | self.pkt_len_or_hdr_err | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | impl core::fmt::Debug for Stats<FskStats> { | ||
| 160 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 161 | f.debug_struct("Stats") | ||
| 162 | .field("status", &self.status()) | ||
| 163 | .field("pkt_rx", &self.pkt_rx()) | ||
| 164 | .field("pkt_crc", &self.pkt_crc()) | ||
| 165 | .field("pkt_len_err", &self.pkt_len_err()) | ||
| 166 | .finish() | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | #[cfg(test)] | ||
| 171 | mod test { | ||
| 172 | use super::super::{CmdStatus, LoRaStats, Stats, StatusMode}; | ||
| 173 | |||
| 174 | #[test] | ||
| 175 | fn mixed() { | ||
| 176 | let example_data_from_radio: [u8; 7] = [0x54, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]; | ||
| 177 | let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio); | ||
| 178 | assert_eq!(stats.status().mode(), Ok(StatusMode::Rx)); | ||
| 179 | assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable)); | ||
| 180 | assert_eq!(stats.pkt_rx(), 0x0102); | ||
| 181 | assert_eq!(stats.pkt_crc(), 0x0304); | ||
| 182 | assert_eq!(stats.pkt_hdr_err(), 0x0506); | ||
| 183 | } | ||
| 184 | } | ||
diff --git a/embassy-stm32/src/subghz/status.rs b/embassy-stm32/src/subghz/status.rs deleted file mode 100644 index b84034f68..000000000 --- a/embassy-stm32/src/subghz/status.rs +++ /dev/null | |||
| @@ -1,197 +0,0 @@ | |||
| 1 | /// sub-GHz radio operating mode. | ||
| 2 | /// | ||
| 3 | /// See `Get_Status` under section 5.8.5 "Communication status information commands" | ||
| 4 | /// in the reference manual. | ||
| 5 | /// | ||
| 6 | /// This is returned by [`Status::mode`]. | ||
| 7 | #[repr(u8)] | ||
| 8 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 9 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 10 | pub enum StatusMode { | ||
| 11 | /// Standby mode with RC 13MHz. | ||
| 12 | StandbyRc = 0x2, | ||
| 13 | /// Standby mode with HSE32. | ||
| 14 | StandbyHse = 0x3, | ||
| 15 | /// Frequency Synthesis mode. | ||
| 16 | Fs = 0x4, | ||
| 17 | /// Receive mode. | ||
| 18 | Rx = 0x5, | ||
| 19 | /// Transmit mode. | ||
| 20 | Tx = 0x6, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl StatusMode { | ||
| 24 | /// Create a new `StatusMode` from bits. | ||
| 25 | /// | ||
| 26 | /// # Example | ||
| 27 | /// | ||
| 28 | /// ``` | ||
| 29 | /// use stm32wlxx_hal::subghz::StatusMode; | ||
| 30 | /// | ||
| 31 | /// assert_eq!(StatusMode::from_raw(0x2), Ok(StatusMode::StandbyRc)); | ||
| 32 | /// assert_eq!(StatusMode::from_raw(0x3), Ok(StatusMode::StandbyHse)); | ||
| 33 | /// assert_eq!(StatusMode::from_raw(0x4), Ok(StatusMode::Fs)); | ||
| 34 | /// assert_eq!(StatusMode::from_raw(0x5), Ok(StatusMode::Rx)); | ||
| 35 | /// assert_eq!(StatusMode::from_raw(0x6), Ok(StatusMode::Tx)); | ||
| 36 | /// // Other values are reserved | ||
| 37 | /// assert_eq!(StatusMode::from_raw(0), Err(0)); | ||
| 38 | /// ``` | ||
| 39 | pub const fn from_raw(bits: u8) -> Result<Self, u8> { | ||
| 40 | match bits { | ||
| 41 | 0x2 => Ok(StatusMode::StandbyRc), | ||
| 42 | 0x3 => Ok(StatusMode::StandbyHse), | ||
| 43 | 0x4 => Ok(StatusMode::Fs), | ||
| 44 | 0x5 => Ok(StatusMode::Rx), | ||
| 45 | 0x6 => Ok(StatusMode::Tx), | ||
| 46 | _ => Err(bits), | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Command status. | ||
| 52 | /// | ||
| 53 | /// See `Get_Status` under section 5.8.5 "Communication status information commands" | ||
| 54 | /// in the reference manual. | ||
| 55 | /// | ||
| 56 | /// This is returned by [`Status::cmd`]. | ||
| 57 | #[repr(u8)] | ||
| 58 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 59 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 60 | pub enum CmdStatus { | ||
| 61 | /// Data available to host. | ||
| 62 | /// | ||
| 63 | /// Packet received successfully and data can be retrieved. | ||
| 64 | Avaliable = 0x2, | ||
| 65 | /// Command time out. | ||
| 66 | /// | ||
| 67 | /// Command took too long to complete triggering a sub-GHz radio watchdog | ||
| 68 | /// timeout. | ||
| 69 | Timeout = 0x3, | ||
| 70 | /// Command processing error. | ||
| 71 | /// | ||
| 72 | /// Invalid opcode or incorrect number of parameters. | ||
| 73 | ProcessingError = 0x4, | ||
| 74 | /// Command execution failure. | ||
| 75 | /// | ||
| 76 | /// Command successfully received but cannot be executed at this time, | ||
| 77 | /// requested operating mode cannot be entered or requested data cannot be | ||
| 78 | /// sent. | ||
| 79 | ExecutionFailure = 0x5, | ||
| 80 | /// Transmit command completed. | ||
| 81 | /// | ||
| 82 | /// Current packet transmission completed. | ||
| 83 | Complete = 0x6, | ||
| 84 | } | ||
| 85 | |||
| 86 | impl CmdStatus { | ||
| 87 | /// Create a new `CmdStatus` from bits. | ||
| 88 | /// | ||
| 89 | /// # Example | ||
| 90 | /// | ||
| 91 | /// ``` | ||
| 92 | /// use stm32wlxx_hal::subghz::CmdStatus; | ||
| 93 | /// | ||
| 94 | /// assert_eq!(CmdStatus::from_raw(0x2), Ok(CmdStatus::Avaliable)); | ||
| 95 | /// assert_eq!(CmdStatus::from_raw(0x3), Ok(CmdStatus::Timeout)); | ||
| 96 | /// assert_eq!(CmdStatus::from_raw(0x4), Ok(CmdStatus::ProcessingError)); | ||
| 97 | /// assert_eq!(CmdStatus::from_raw(0x5), Ok(CmdStatus::ExecutionFailure)); | ||
| 98 | /// assert_eq!(CmdStatus::from_raw(0x6), Ok(CmdStatus::Complete)); | ||
| 99 | /// // Other values are reserved | ||
| 100 | /// assert_eq!(CmdStatus::from_raw(0), Err(0)); | ||
| 101 | /// ``` | ||
| 102 | pub const fn from_raw(bits: u8) -> Result<Self, u8> { | ||
| 103 | match bits { | ||
| 104 | 0x2 => Ok(CmdStatus::Avaliable), | ||
| 105 | 0x3 => Ok(CmdStatus::Timeout), | ||
| 106 | 0x4 => Ok(CmdStatus::ProcessingError), | ||
| 107 | 0x5 => Ok(CmdStatus::ExecutionFailure), | ||
| 108 | 0x6 => Ok(CmdStatus::Complete), | ||
| 109 | _ => Err(bits), | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Radio status. | ||
| 115 | /// | ||
| 116 | /// This is returned by [`status`]. | ||
| 117 | /// | ||
| 118 | /// [`status`]: super::SubGhz::status | ||
| 119 | #[derive(PartialEq, Eq, Clone, Copy)] | ||
| 120 | pub struct Status(u8); | ||
| 121 | |||
| 122 | impl From<u8> for Status { | ||
| 123 | fn from(x: u8) -> Self { | ||
| 124 | Status(x) | ||
| 125 | } | ||
| 126 | } | ||
| 127 | impl From<Status> for u8 { | ||
| 128 | fn from(x: Status) -> Self { | ||
| 129 | x.0 | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | impl Status { | ||
| 134 | /// Create a new `Status` from a raw `u8` value. | ||
| 135 | /// | ||
| 136 | /// This is the same as `Status::from(u8)`, but in a `const` function. | ||
| 137 | /// | ||
| 138 | /// # Example | ||
| 139 | /// | ||
| 140 | /// ``` | ||
| 141 | /// use stm32wlxx_hal::subghz::{CmdStatus, Status, StatusMode}; | ||
| 142 | /// | ||
| 143 | /// const STATUS: Status = Status::from_raw(0x54_u8); | ||
| 144 | /// assert_eq!(STATUS.mode(), Ok(StatusMode::Rx)); | ||
| 145 | /// assert_eq!(STATUS.cmd(), Ok(CmdStatus::Avaliable)); | ||
| 146 | /// ``` | ||
| 147 | pub const fn from_raw(value: u8) -> Status { | ||
| 148 | Status(value) | ||
| 149 | } | ||
| 150 | |||
| 151 | /// sub-GHz radio operating mode. | ||
| 152 | /// | ||
| 153 | /// # Example | ||
| 154 | /// | ||
| 155 | /// ``` | ||
| 156 | /// use stm32wlxx_hal::subghz::{Status, StatusMode}; | ||
| 157 | /// | ||
| 158 | /// let status: Status = 0xACu8.into(); | ||
| 159 | /// assert_eq!(status.mode(), Ok(StatusMode::StandbyRc)); | ||
| 160 | /// ``` | ||
| 161 | pub const fn mode(&self) -> Result<StatusMode, u8> { | ||
| 162 | StatusMode::from_raw((self.0 >> 4) & 0b111) | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Command status. | ||
| 166 | /// | ||
| 167 | /// This method frequently returns reserved values such as `Err(1)`. | ||
| 168 | /// ST support has confirmed that this is normal and should be ignored. | ||
| 169 | /// | ||
| 170 | /// # Example | ||
| 171 | /// | ||
| 172 | /// ``` | ||
| 173 | /// use stm32wlxx_hal::subghz::{CmdStatus, Status}; | ||
| 174 | /// | ||
| 175 | /// let status: Status = 0xACu8.into(); | ||
| 176 | /// assert_eq!(status.cmd(), Ok(CmdStatus::Complete)); | ||
| 177 | /// ``` | ||
| 178 | pub const fn cmd(&self) -> Result<CmdStatus, u8> { | ||
| 179 | CmdStatus::from_raw((self.0 >> 1) & 0b111) | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | impl core::fmt::Debug for Status { | ||
| 184 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 185 | f.debug_struct("Status") | ||
| 186 | .field("mode", &self.mode()) | ||
| 187 | .field("cmd", &self.cmd()) | ||
| 188 | .finish() | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | #[cfg(feature = "defmt")] | ||
| 193 | impl defmt::Format for Status { | ||
| 194 | fn format(&self, fmt: defmt::Formatter) { | ||
| 195 | defmt::write!(fmt, "Status {{ mode: {}, cmd: {} }}", self.mode(), self.cmd()) | ||
| 196 | } | ||
| 197 | } | ||
diff --git a/embassy-stm32/src/subghz/tcxo_mode.rs b/embassy-stm32/src/subghz/tcxo_mode.rs deleted file mode 100644 index 698dee0a6..000000000 --- a/embassy-stm32/src/subghz/tcxo_mode.rs +++ /dev/null | |||
| @@ -1,170 +0,0 @@ | |||
| 1 | use super::Timeout; | ||
| 2 | |||
| 3 | /// TCXO trim. | ||
| 4 | /// | ||
| 5 | /// **Note:** To use V<sub>DDTCXO</sub>, the V<sub>DDRF</sub> supply must be at | ||
| 6 | /// least + 200 mV higher than the selected `TcxoTrim` voltage level. | ||
| 7 | /// | ||
| 8 | /// Used by [`TcxoMode`]. | ||
| 9 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
| 10 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 11 | #[repr(u8)] | ||
| 12 | pub enum TcxoTrim { | ||
| 13 | /// 1.6V | ||
| 14 | Volts1pt6 = 0x0, | ||
| 15 | /// 1.7V | ||
| 16 | Volts1pt7 = 0x1, | ||
| 17 | /// 1.8V | ||
| 18 | Volts1pt8 = 0x2, | ||
| 19 | /// 2.2V | ||
| 20 | Volts2pt2 = 0x3, | ||
| 21 | /// 2.4V | ||
| 22 | Volts2pt4 = 0x4, | ||
| 23 | /// 2.7V | ||
| 24 | Volts2pt7 = 0x5, | ||
| 25 | /// 3.0V | ||
| 26 | Volts3pt0 = 0x6, | ||
| 27 | /// 3.3V | ||
| 28 | Volts3pt3 = 0x7, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl core::fmt::Display for TcxoTrim { | ||
| 32 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 33 | match self { | ||
| 34 | TcxoTrim::Volts1pt6 => write!(f, "1.6V"), | ||
| 35 | TcxoTrim::Volts1pt7 => write!(f, "1.7V"), | ||
| 36 | TcxoTrim::Volts1pt8 => write!(f, "1.8V"), | ||
| 37 | TcxoTrim::Volts2pt2 => write!(f, "2.2V"), | ||
| 38 | TcxoTrim::Volts2pt4 => write!(f, "2.4V"), | ||
| 39 | TcxoTrim::Volts2pt7 => write!(f, "2.7V"), | ||
| 40 | TcxoTrim::Volts3pt0 => write!(f, "3.0V"), | ||
| 41 | TcxoTrim::Volts3pt3 => write!(f, "3.3V"), | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | impl TcxoTrim { | ||
| 47 | /// Get the value of the TXCO trim in millivolts. | ||
| 48 | /// | ||
| 49 | /// # Example | ||
| 50 | /// | ||
| 51 | /// ``` | ||
| 52 | /// use stm32wlxx_hal::subghz::TcxoTrim; | ||
| 53 | /// | ||
| 54 | /// assert_eq!(TcxoTrim::Volts1pt6.as_millivolts(), 1600); | ||
| 55 | /// assert_eq!(TcxoTrim::Volts1pt7.as_millivolts(), 1700); | ||
| 56 | /// assert_eq!(TcxoTrim::Volts1pt8.as_millivolts(), 1800); | ||
| 57 | /// assert_eq!(TcxoTrim::Volts2pt2.as_millivolts(), 2200); | ||
| 58 | /// assert_eq!(TcxoTrim::Volts2pt4.as_millivolts(), 2400); | ||
| 59 | /// assert_eq!(TcxoTrim::Volts2pt7.as_millivolts(), 2700); | ||
| 60 | /// assert_eq!(TcxoTrim::Volts3pt0.as_millivolts(), 3000); | ||
| 61 | /// assert_eq!(TcxoTrim::Volts3pt3.as_millivolts(), 3300); | ||
| 62 | /// ``` | ||
| 63 | pub const fn as_millivolts(&self) -> u16 { | ||
| 64 | match self { | ||
| 65 | TcxoTrim::Volts1pt6 => 1600, | ||
| 66 | TcxoTrim::Volts1pt7 => 1700, | ||
| 67 | TcxoTrim::Volts1pt8 => 1800, | ||
| 68 | TcxoTrim::Volts2pt2 => 2200, | ||
| 69 | TcxoTrim::Volts2pt4 => 2400, | ||
| 70 | TcxoTrim::Volts2pt7 => 2700, | ||
| 71 | TcxoTrim::Volts3pt0 => 3000, | ||
| 72 | TcxoTrim::Volts3pt3 => 3300, | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | /// TCXO trim and HSE32 ready timeout. | ||
| 78 | /// | ||
| 79 | /// Argument of [`set_tcxo_mode`]. | ||
| 80 | /// | ||
| 81 | /// [`set_tcxo_mode`]: super::SubGhz::set_tcxo_mode | ||
| 82 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 83 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 84 | pub struct TcxoMode { | ||
| 85 | buf: [u8; 5], | ||
| 86 | } | ||
| 87 | |||
| 88 | impl TcxoMode { | ||
| 89 | /// Create a new `TcxoMode` struct. | ||
| 90 | /// | ||
| 91 | /// This is the same as `default`, but in a `const` function. | ||
| 92 | /// | ||
| 93 | /// # Example | ||
| 94 | /// | ||
| 95 | /// ``` | ||
| 96 | /// use stm32wlxx_hal::subghz::TcxoMode; | ||
| 97 | /// | ||
| 98 | /// const TCXO_MODE: TcxoMode = TcxoMode::new(); | ||
| 99 | /// ``` | ||
| 100 | pub const fn new() -> TcxoMode { | ||
| 101 | TcxoMode { | ||
| 102 | buf: [super::OpCode::SetTcxoMode as u8, 0x00, 0x00, 0x00, 0x00], | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Set the TCXO trim. | ||
| 107 | /// | ||
| 108 | /// **Note:** To use V<sub>DDTCXO</sub>, the V<sub>DDRF</sub> supply must be | ||
| 109 | /// at least + 200 mV higher than the selected `TcxoTrim` voltage level. | ||
| 110 | /// | ||
| 111 | /// # Example | ||
| 112 | /// | ||
| 113 | /// ``` | ||
| 114 | /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim}; | ||
| 115 | /// | ||
| 116 | /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_txco_trim(TcxoTrim::Volts1pt6); | ||
| 117 | /// # assert_eq!(TCXO_MODE.as_slice()[1], 0x00); | ||
| 118 | /// ``` | ||
| 119 | #[must_use = "set_txco_trim returns a modified TcxoMode"] | ||
| 120 | pub const fn set_txco_trim(mut self, tcxo_trim: TcxoTrim) -> TcxoMode { | ||
| 121 | self.buf[1] = tcxo_trim as u8; | ||
| 122 | self | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Set the ready timeout duration. | ||
| 126 | /// | ||
| 127 | /// # Example | ||
| 128 | /// | ||
| 129 | /// ``` | ||
| 130 | /// use core::time::Duration; | ||
| 131 | /// use stm32wlxx_hal::subghz::{TcxoMode, Timeout}; | ||
| 132 | /// | ||
| 133 | /// // 15.625 ms timeout | ||
| 134 | /// const TIMEOUT: Timeout = Timeout::from_duration_sat(Duration::from_millis(15_625)); | ||
| 135 | /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_timeout(TIMEOUT); | ||
| 136 | /// # assert_eq!(TCXO_MODE.as_slice()[2], 0x0F); | ||
| 137 | /// # assert_eq!(TCXO_MODE.as_slice()[3], 0x42); | ||
| 138 | /// # assert_eq!(TCXO_MODE.as_slice()[4], 0x40); | ||
| 139 | /// ``` | ||
| 140 | #[must_use = "set_timeout returns a modified TcxoMode"] | ||
| 141 | pub const fn set_timeout(mut self, timeout: Timeout) -> TcxoMode { | ||
| 142 | let timeout_bits: u32 = timeout.into_bits(); | ||
| 143 | self.buf[2] = ((timeout_bits >> 16) & 0xFF) as u8; | ||
| 144 | self.buf[3] = ((timeout_bits >> 8) & 0xFF) as u8; | ||
| 145 | self.buf[4] = (timeout_bits & 0xFF) as u8; | ||
| 146 | self | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Extracts a slice containing the packet. | ||
| 150 | /// | ||
| 151 | /// # Example | ||
| 152 | /// | ||
| 153 | /// ``` | ||
| 154 | /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim, Timeout}; | ||
| 155 | /// | ||
| 156 | /// const TCXO_MODE: TcxoMode = TcxoMode::new() | ||
| 157 | /// .set_txco_trim(TcxoTrim::Volts1pt7) | ||
| 158 | /// .set_timeout(Timeout::from_raw(0x123456)); | ||
| 159 | /// assert_eq!(TCXO_MODE.as_slice(), &[0x97, 0x1, 0x12, 0x34, 0x56]); | ||
| 160 | /// ``` | ||
| 161 | pub const fn as_slice(&self) -> &[u8] { | ||
| 162 | &self.buf | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | impl Default for TcxoMode { | ||
| 167 | fn default() -> Self { | ||
| 168 | Self::new() | ||
| 169 | } | ||
| 170 | } | ||
diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs deleted file mode 100644 index 0ae49dd90..000000000 --- a/embassy-stm32/src/subghz/timeout.rs +++ /dev/null | |||
| @@ -1,492 +0,0 @@ | |||
| 1 | use core::time::Duration; | ||
| 2 | |||
| 3 | use super::ValueError; | ||
| 4 | |||
| 5 | const fn abs_diff(a: u64, b: u64) -> u64 { | ||
| 6 | if a > b { | ||
| 7 | a - b | ||
| 8 | } else { | ||
| 9 | b - a | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | /// Timeout argument. | ||
| 14 | /// | ||
| 15 | /// This is used by: | ||
| 16 | /// * [`set_rx`] | ||
| 17 | /// * [`set_tx`] | ||
| 18 | /// * [`TcxoMode`] | ||
| 19 | /// | ||
| 20 | /// Each timeout has 3 bytes, with a resolution of 15.625µs per bit, giving a | ||
| 21 | /// range of 0s to 262.143984375s. | ||
| 22 | /// | ||
| 23 | /// [`set_rx`]: super::SubGhz::set_rx | ||
| 24 | /// [`set_tx`]: super::SubGhz::set_tx | ||
| 25 | /// [`TcxoMode`]: super::TcxoMode | ||
| 26 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | pub struct Timeout { | ||
| 29 | bits: u32, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl Timeout { | ||
| 33 | const BITS_PER_MILLI: u32 = 64; // 1e-3 / 15.625e-6 | ||
| 34 | const BITS_PER_SEC: u32 = 64_000; // 1 / 15.625e-6 | ||
| 35 | |||
| 36 | /// Disable the timeout (0s timeout). | ||
| 37 | /// | ||
| 38 | /// # Example | ||
| 39 | /// | ||
| 40 | /// ``` | ||
| 41 | /// use core::time::Duration; | ||
| 42 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 43 | /// | ||
| 44 | /// const TIMEOUT: Timeout = Timeout::DISABLED; | ||
| 45 | /// assert_eq!(TIMEOUT.as_duration(), Duration::from_secs(0)); | ||
| 46 | /// ``` | ||
| 47 | pub const DISABLED: Timeout = Timeout { bits: 0x0 }; | ||
| 48 | |||
| 49 | /// Minimum timeout, 15.625µs. | ||
| 50 | /// | ||
| 51 | /// # Example | ||
| 52 | /// | ||
| 53 | /// ``` | ||
| 54 | /// use core::time::Duration; | ||
| 55 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 56 | /// | ||
| 57 | /// const TIMEOUT: Timeout = Timeout::MIN; | ||
| 58 | /// assert_eq!(TIMEOUT.into_bits(), 1); | ||
| 59 | /// ``` | ||
| 60 | pub const MIN: Timeout = Timeout { bits: 1 }; | ||
| 61 | |||
| 62 | /// Maximum timeout, 262.143984375s. | ||
| 63 | /// | ||
| 64 | /// # Example | ||
| 65 | /// | ||
| 66 | /// ``` | ||
| 67 | /// use core::time::Duration; | ||
| 68 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 69 | /// | ||
| 70 | /// const TIMEOUT: Timeout = Timeout::MAX; | ||
| 71 | /// assert_eq!(TIMEOUT.as_duration(), Duration::from_nanos(262_143_984_375)); | ||
| 72 | /// ``` | ||
| 73 | pub const MAX: Timeout = Timeout { bits: 0x00FF_FFFF }; | ||
| 74 | |||
| 75 | /// Timeout resolution in nanoseconds, 15.625µs. | ||
| 76 | pub const RESOLUTION_NANOS: u16 = 15_625; | ||
| 77 | |||
| 78 | /// Timeout resolution, 15.625µs. | ||
| 79 | /// | ||
| 80 | /// # Example | ||
| 81 | /// | ||
| 82 | /// ``` | ||
| 83 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 84 | /// | ||
| 85 | /// assert_eq!( | ||
| 86 | /// Timeout::RESOLUTION.as_nanos(), | ||
| 87 | /// Timeout::RESOLUTION_NANOS as u128 | ||
| 88 | /// ); | ||
| 89 | /// ``` | ||
| 90 | pub const RESOLUTION: Duration = Duration::from_nanos(Self::RESOLUTION_NANOS as u64); | ||
| 91 | |||
| 92 | /// Create a new timeout from a [`Duration`]. | ||
| 93 | /// | ||
| 94 | /// This will return the nearest timeout value possible, or a | ||
| 95 | /// [`ValueError`] if the value is out of bounds. | ||
| 96 | /// | ||
| 97 | /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout | ||
| 98 | /// construction. | ||
| 99 | /// This is not _that_ useful right now, it is simply future proofing for a | ||
| 100 | /// time when `Result::unwrap` is available for `const fn`. | ||
| 101 | /// | ||
| 102 | /// # Example | ||
| 103 | /// | ||
| 104 | /// Value within bounds: | ||
| 105 | /// | ||
| 106 | /// ``` | ||
| 107 | /// use core::time::Duration; | ||
| 108 | /// use stm32wlxx_hal::subghz::{Timeout, ValueError}; | ||
| 109 | /// | ||
| 110 | /// const MIN: Duration = Timeout::RESOLUTION; | ||
| 111 | /// assert_eq!(Timeout::from_duration(MIN).unwrap(), Timeout::MIN); | ||
| 112 | /// ``` | ||
| 113 | /// | ||
| 114 | /// Value too low: | ||
| 115 | /// | ||
| 116 | /// ``` | ||
| 117 | /// use core::time::Duration; | ||
| 118 | /// use stm32wlxx_hal::subghz::{Timeout, ValueError}; | ||
| 119 | /// | ||
| 120 | /// const LOWER_LIMIT_NANOS: u128 = 7813; | ||
| 121 | /// const TOO_LOW_NANOS: u128 = LOWER_LIMIT_NANOS - 1; | ||
| 122 | /// const TOO_LOW_DURATION: Duration = Duration::from_nanos(TOO_LOW_NANOS as u64); | ||
| 123 | /// assert_eq!( | ||
| 124 | /// Timeout::from_duration(TOO_LOW_DURATION), | ||
| 125 | /// Err(ValueError::too_low(TOO_LOW_NANOS, LOWER_LIMIT_NANOS)) | ||
| 126 | /// ); | ||
| 127 | /// ``` | ||
| 128 | /// | ||
| 129 | /// Value too high: | ||
| 130 | /// | ||
| 131 | /// ``` | ||
| 132 | /// use core::time::Duration; | ||
| 133 | /// use stm32wlxx_hal::subghz::{Timeout, ValueError}; | ||
| 134 | /// | ||
| 135 | /// const UPPER_LIMIT_NANOS: u128 = Timeout::MAX.as_nanos() as u128 + 7812; | ||
| 136 | /// const TOO_HIGH_NANOS: u128 = UPPER_LIMIT_NANOS + 1; | ||
| 137 | /// const TOO_HIGH_DURATION: Duration = Duration::from_nanos(TOO_HIGH_NANOS as u64); | ||
| 138 | /// assert_eq!( | ||
| 139 | /// Timeout::from_duration(TOO_HIGH_DURATION), | ||
| 140 | /// Err(ValueError::too_high(TOO_HIGH_NANOS, UPPER_LIMIT_NANOS)) | ||
| 141 | /// ); | ||
| 142 | /// ``` | ||
| 143 | pub const fn from_duration(duration: Duration) -> Result<Timeout, ValueError<u128>> { | ||
| 144 | // at the time of development many methods in | ||
| 145 | // `core::Duration` were not `const fn`, which leads to the hacks | ||
| 146 | // you see here. | ||
| 147 | let nanos: u128 = duration.as_nanos(); | ||
| 148 | const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128 + (Timeout::RESOLUTION_NANOS as u128) / 2; | ||
| 149 | const LOWER_LIMIT: u128 = (((Timeout::RESOLUTION_NANOS as u128) + 1) / 2) as u128; | ||
| 150 | |||
| 151 | if nanos > UPPER_LIMIT { | ||
| 152 | Err(ValueError::too_high(nanos, UPPER_LIMIT)) | ||
| 153 | } else if nanos < LOWER_LIMIT { | ||
| 154 | Err(ValueError::too_low(nanos, LOWER_LIMIT)) | ||
| 155 | } else { | ||
| 156 | // safe to truncate here because of previous bounds check. | ||
| 157 | let duration_nanos: u64 = nanos as u64; | ||
| 158 | |||
| 159 | let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64); | ||
| 160 | let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64); | ||
| 161 | |||
| 162 | let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32); | ||
| 163 | let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32); | ||
| 164 | |||
| 165 | let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos); | ||
| 166 | let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos); | ||
| 167 | |||
| 168 | if error_ceil < error_floor { | ||
| 169 | Ok(timeout_ceil) | ||
| 170 | } else { | ||
| 171 | Ok(timeout_floor) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Create a new timeout from a [`Duration`]. | ||
| 177 | /// | ||
| 178 | /// This will return the nearest timeout value possible, saturating at the | ||
| 179 | /// limits. | ||
| 180 | /// | ||
| 181 | /// This is an expensive function to call outside of `const` contexts. | ||
| 182 | /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout | ||
| 183 | /// construction. | ||
| 184 | /// | ||
| 185 | /// # Example | ||
| 186 | /// | ||
| 187 | /// ``` | ||
| 188 | /// use core::time::Duration; | ||
| 189 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 190 | /// | ||
| 191 | /// const DURATION_MAX_NS: u64 = 262_143_984_376; | ||
| 192 | /// | ||
| 193 | /// assert_eq!( | ||
| 194 | /// Timeout::from_duration_sat(Duration::from_millis(0)), | ||
| 195 | /// Timeout::MIN | ||
| 196 | /// ); | ||
| 197 | /// assert_eq!( | ||
| 198 | /// Timeout::from_duration_sat(Duration::from_nanos(DURATION_MAX_NS)), | ||
| 199 | /// Timeout::MAX | ||
| 200 | /// ); | ||
| 201 | /// assert_eq!( | ||
| 202 | /// Timeout::from_duration_sat(Timeout::RESOLUTION).into_bits(), | ||
| 203 | /// 1 | ||
| 204 | /// ); | ||
| 205 | /// ``` | ||
| 206 | pub const fn from_duration_sat(duration: Duration) -> Timeout { | ||
| 207 | // at the time of development many methods in | ||
| 208 | // `core::Duration` were not `const fn`, which leads to the hacks | ||
| 209 | // you see here. | ||
| 210 | let nanos: u128 = duration.as_nanos(); | ||
| 211 | const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128; | ||
| 212 | |||
| 213 | if nanos > UPPER_LIMIT { | ||
| 214 | Timeout::MAX | ||
| 215 | } else if nanos < (Timeout::RESOLUTION_NANOS as u128) { | ||
| 216 | Timeout::from_raw(1) | ||
| 217 | } else { | ||
| 218 | // safe to truncate here because of previous bounds check. | ||
| 219 | let duration_nanos: u64 = duration.as_nanos() as u64; | ||
| 220 | |||
| 221 | let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64); | ||
| 222 | let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64); | ||
| 223 | |||
| 224 | let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32); | ||
| 225 | let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32); | ||
| 226 | |||
| 227 | let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos); | ||
| 228 | let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos); | ||
| 229 | |||
| 230 | if error_ceil < error_floor { | ||
| 231 | timeout_ceil | ||
| 232 | } else { | ||
| 233 | timeout_floor | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Create a new timeout from a milliseconds value. | ||
| 239 | /// | ||
| 240 | /// This will round towards zero and saturate at the limits. | ||
| 241 | /// | ||
| 242 | /// This is the preferred method to call when you need to generate a | ||
| 243 | /// timeout value at runtime. | ||
| 244 | /// | ||
| 245 | /// # Example | ||
| 246 | /// | ||
| 247 | /// ``` | ||
| 248 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 249 | /// | ||
| 250 | /// assert_eq!(Timeout::from_millis_sat(0), Timeout::MIN); | ||
| 251 | /// assert_eq!(Timeout::from_millis_sat(262_144), Timeout::MAX); | ||
| 252 | /// assert_eq!(Timeout::from_millis_sat(1).into_bits(), 64); | ||
| 253 | /// ``` | ||
| 254 | pub const fn from_millis_sat(millis: u32) -> Timeout { | ||
| 255 | if millis == 0 { | ||
| 256 | Timeout::MIN | ||
| 257 | } else if millis >= 262_144 { | ||
| 258 | Timeout::MAX | ||
| 259 | } else { | ||
| 260 | Timeout::from_raw(millis * Self::BITS_PER_MILLI) | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Create a timeout from raw bits, where each bit has the resolution of | ||
| 265 | /// [`Timeout::RESOLUTION`]. | ||
| 266 | /// | ||
| 267 | /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` | ||
| 268 | /// argument will be masked. | ||
| 269 | /// | ||
| 270 | /// # Example | ||
| 271 | /// | ||
| 272 | /// ``` | ||
| 273 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 274 | /// | ||
| 275 | /// assert_eq!(Timeout::from_raw(u32::MAX), Timeout::MAX); | ||
| 276 | /// assert_eq!(Timeout::from_raw(0x00_FF_FF_FF), Timeout::MAX); | ||
| 277 | /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION); | ||
| 278 | /// assert_eq!(Timeout::from_raw(0), Timeout::DISABLED); | ||
| 279 | /// ``` | ||
| 280 | pub const fn from_raw(bits: u32) -> Timeout { | ||
| 281 | Timeout { | ||
| 282 | bits: bits & 0x00FF_FFFF, | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | /// Get the timeout as nanoseconds. | ||
| 287 | /// | ||
| 288 | /// # Example | ||
| 289 | /// | ||
| 290 | /// ``` | ||
| 291 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 292 | /// | ||
| 293 | /// assert_eq!(Timeout::MAX.as_nanos(), 262_143_984_375); | ||
| 294 | /// assert_eq!(Timeout::DISABLED.as_nanos(), 0); | ||
| 295 | /// assert_eq!(Timeout::from_raw(1).as_nanos(), 15_625); | ||
| 296 | /// assert_eq!(Timeout::from_raw(64_000).as_nanos(), 1_000_000_000); | ||
| 297 | /// ``` | ||
| 298 | pub const fn as_nanos(&self) -> u64 { | ||
| 299 | (self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64) | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Get the timeout as microseconds, rounding towards zero. | ||
| 303 | /// | ||
| 304 | /// # Example | ||
| 305 | /// | ||
| 306 | /// ``` | ||
| 307 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 308 | /// | ||
| 309 | /// assert_eq!(Timeout::MAX.as_micros(), 262_143_984); | ||
| 310 | /// assert_eq!(Timeout::DISABLED.as_micros(), 0); | ||
| 311 | /// assert_eq!(Timeout::from_raw(1).as_micros(), 15); | ||
| 312 | /// assert_eq!(Timeout::from_raw(64_000).as_micros(), 1_000_000); | ||
| 313 | /// ``` | ||
| 314 | pub const fn as_micros(&self) -> u32 { | ||
| 315 | (self.as_nanos() / 1_000) as u32 | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Get the timeout as milliseconds, rounding towards zero. | ||
| 319 | /// | ||
| 320 | /// # Example | ||
| 321 | /// | ||
| 322 | /// ``` | ||
| 323 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 324 | /// | ||
| 325 | /// assert_eq!(Timeout::MAX.as_millis(), 262_143); | ||
| 326 | /// assert_eq!(Timeout::DISABLED.as_millis(), 0); | ||
| 327 | /// assert_eq!(Timeout::from_raw(1).as_millis(), 0); | ||
| 328 | /// assert_eq!(Timeout::from_raw(64_000).as_millis(), 1_000); | ||
| 329 | /// ``` | ||
| 330 | pub const fn as_millis(&self) -> u32 { | ||
| 331 | self.into_bits() / Self::BITS_PER_MILLI | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Get the timeout as seconds, rounding towards zero. | ||
| 335 | /// | ||
| 336 | /// # Example | ||
| 337 | /// | ||
| 338 | /// ``` | ||
| 339 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 340 | /// | ||
| 341 | /// assert_eq!(Timeout::MAX.as_secs(), 262); | ||
| 342 | /// assert_eq!(Timeout::DISABLED.as_secs(), 0); | ||
| 343 | /// assert_eq!(Timeout::from_raw(1).as_secs(), 0); | ||
| 344 | /// assert_eq!(Timeout::from_raw(64_000).as_secs(), 1); | ||
| 345 | /// ``` | ||
| 346 | pub const fn as_secs(&self) -> u16 { | ||
| 347 | (self.into_bits() / Self::BITS_PER_SEC) as u16 | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Get the timeout as a [`Duration`]. | ||
| 351 | /// | ||
| 352 | /// # Example | ||
| 353 | /// | ||
| 354 | /// ``` | ||
| 355 | /// use core::time::Duration; | ||
| 356 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 357 | /// | ||
| 358 | /// assert_eq!( | ||
| 359 | /// Timeout::MAX.as_duration(), | ||
| 360 | /// Duration::from_nanos(262_143_984_375) | ||
| 361 | /// ); | ||
| 362 | /// assert_eq!(Timeout::DISABLED.as_duration(), Duration::from_nanos(0)); | ||
| 363 | /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION); | ||
| 364 | /// ``` | ||
| 365 | pub const fn as_duration(&self) -> Duration { | ||
| 366 | Duration::from_nanos((self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64)) | ||
| 367 | } | ||
| 368 | |||
| 369 | /// Get the bit value for the timeout. | ||
| 370 | /// | ||
| 371 | /// # Example | ||
| 372 | /// | ||
| 373 | /// ``` | ||
| 374 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 375 | /// | ||
| 376 | /// assert_eq!(Timeout::from_raw(u32::MAX).into_bits(), 0x00FF_FFFF); | ||
| 377 | /// assert_eq!(Timeout::from_raw(1).into_bits(), 1); | ||
| 378 | /// ``` | ||
| 379 | pub const fn into_bits(self) -> u32 { | ||
| 380 | self.bits | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Get the byte value for the timeout. | ||
| 384 | /// | ||
| 385 | /// # Example | ||
| 386 | /// | ||
| 387 | /// ``` | ||
| 388 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 389 | /// | ||
| 390 | /// assert_eq!(Timeout::from_raw(u32::MAX).as_bytes(), [0xFF, 0xFF, 0xFF]); | ||
| 391 | /// assert_eq!(Timeout::from_raw(1).as_bytes(), [0, 0, 1]); | ||
| 392 | /// ``` | ||
| 393 | pub const fn as_bytes(self) -> [u8; 3] { | ||
| 394 | [ | ||
| 395 | ((self.bits >> 16) & 0xFF) as u8, | ||
| 396 | ((self.bits >> 8) & 0xFF) as u8, | ||
| 397 | (self.bits & 0xFF) as u8, | ||
| 398 | ] | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Saturating timeout addition. Computes `self + rhs`, saturating at the | ||
| 402 | /// numeric bounds instead of overflowing. | ||
| 403 | /// | ||
| 404 | /// # Example | ||
| 405 | /// | ||
| 406 | /// ``` | ||
| 407 | /// use stm32wlxx_hal::subghz::Timeout; | ||
| 408 | /// | ||
| 409 | /// assert_eq!( | ||
| 410 | /// Timeout::from_raw(0xFF_FF_F0).saturating_add(Timeout::from_raw(0xFF)), | ||
| 411 | /// Timeout::from_raw(0xFF_FF_FF) | ||
| 412 | /// ); | ||
| 413 | /// assert_eq!( | ||
| 414 | /// Timeout::from_raw(100).saturating_add(Timeout::from_raw(23)), | ||
| 415 | /// Timeout::from_raw(123) | ||
| 416 | /// ); | ||
| 417 | /// ``` | ||
| 418 | #[must_use = "saturating_add returns a new Timeout"] | ||
| 419 | pub const fn saturating_add(self, rhs: Self) -> Self { | ||
| 420 | // TODO: use core::cmp::min when it is const | ||
| 421 | let bits: u32 = self.bits.saturating_add(rhs.bits); | ||
| 422 | if bits > Self::MAX.bits { | ||
| 423 | Self::MAX | ||
| 424 | } else { | ||
| 425 | Self { bits } | ||
| 426 | } | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | impl From<Timeout> for Duration { | ||
| 431 | fn from(to: Timeout) -> Self { | ||
| 432 | to.as_duration() | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | impl From<Timeout> for [u8; 3] { | ||
| 437 | fn from(to: Timeout) -> Self { | ||
| 438 | to.as_bytes() | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | #[cfg(feature = "time")] | ||
| 443 | impl From<Timeout> for embassy_time::Duration { | ||
| 444 | fn from(to: Timeout) -> Self { | ||
| 445 | embassy_time::Duration::from_micros(to.as_micros().into()) | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | #[cfg(test)] | ||
| 450 | mod tests { | ||
| 451 | use core::time::Duration; | ||
| 452 | |||
| 453 | use super::{Timeout, ValueError}; | ||
| 454 | |||
| 455 | #[test] | ||
| 456 | fn saturate() { | ||
| 457 | assert_eq!(Timeout::from_duration_sat(Duration::from_secs(u64::MAX)), Timeout::MAX); | ||
| 458 | } | ||
| 459 | |||
| 460 | #[test] | ||
| 461 | fn rounding() { | ||
| 462 | const NANO1: Duration = Duration::from_nanos(1); | ||
| 463 | let res_sub_1_ns: Duration = Timeout::RESOLUTION - NANO1; | ||
| 464 | let res_add_1_ns: Duration = Timeout::RESOLUTION + NANO1; | ||
| 465 | assert_eq!(Timeout::from_duration_sat(res_sub_1_ns).into_bits(), 1); | ||
| 466 | assert_eq!(Timeout::from_duration_sat(res_add_1_ns).into_bits(), 1); | ||
| 467 | } | ||
| 468 | |||
| 469 | #[test] | ||
| 470 | fn lower_limit() { | ||
| 471 | let low: Duration = (Timeout::RESOLUTION + Duration::from_nanos(1)) / 2; | ||
| 472 | assert_eq!(Timeout::from_duration(low), Ok(Timeout::from_raw(1))); | ||
| 473 | |||
| 474 | let too_low: Duration = low - Duration::from_nanos(1); | ||
| 475 | assert_eq!( | ||
| 476 | Timeout::from_duration(too_low), | ||
| 477 | Err(ValueError::too_low(too_low.as_nanos(), low.as_nanos())) | ||
| 478 | ); | ||
| 479 | } | ||
| 480 | |||
| 481 | #[test] | ||
| 482 | fn upper_limit() { | ||
| 483 | let high: Duration = Timeout::MAX.as_duration() + Timeout::RESOLUTION / 2; | ||
| 484 | assert_eq!(Timeout::from_duration(high), Ok(Timeout::from_raw(0xFFFFFF))); | ||
| 485 | |||
| 486 | let too_high: Duration = high + Duration::from_nanos(1); | ||
| 487 | assert_eq!( | ||
| 488 | Timeout::from_duration(too_high), | ||
| 489 | Err(ValueError::too_high(too_high.as_nanos(), high.as_nanos())) | ||
| 490 | ); | ||
| 491 | } | ||
| 492 | } | ||
diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs deleted file mode 100644 index 03bdb1ea8..000000000 --- a/embassy-stm32/src/subghz/tx_params.rs +++ /dev/null | |||
| @@ -1,192 +0,0 @@ | |||
| 1 | /// Power amplifier ramp time for FSK, MSK, and LoRa modulation. | ||
| 2 | /// | ||
| 3 | /// Argument of [`set_ramp_time`][`super::TxParams::set_ramp_time`]. | ||
| 4 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | #[repr(u8)] | ||
| 7 | pub enum RampTime { | ||
| 8 | /// 10µs | ||
| 9 | Micros10 = 0x00, | ||
| 10 | /// 20µs | ||
| 11 | Micros20 = 0x01, | ||
| 12 | /// 40µs | ||
| 13 | Micros40 = 0x02, | ||
| 14 | /// 80µs | ||
| 15 | Micros80 = 0x03, | ||
| 16 | /// 200µs | ||
| 17 | Micros200 = 0x04, | ||
| 18 | /// 800µs | ||
| 19 | Micros800 = 0x05, | ||
| 20 | /// 1.7ms | ||
| 21 | Micros1700 = 0x06, | ||
| 22 | /// 3.4ms | ||
| 23 | Micros3400 = 0x07, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl From<RampTime> for u8 { | ||
| 27 | fn from(rt: RampTime) -> Self { | ||
| 28 | rt as u8 | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | impl From<RampTime> for core::time::Duration { | ||
| 33 | fn from(rt: RampTime) -> Self { | ||
| 34 | match rt { | ||
| 35 | RampTime::Micros10 => core::time::Duration::from_micros(10), | ||
| 36 | RampTime::Micros20 => core::time::Duration::from_micros(20), | ||
| 37 | RampTime::Micros40 => core::time::Duration::from_micros(40), | ||
| 38 | RampTime::Micros80 => core::time::Duration::from_micros(80), | ||
| 39 | RampTime::Micros200 => core::time::Duration::from_micros(200), | ||
| 40 | RampTime::Micros800 => core::time::Duration::from_micros(800), | ||
| 41 | RampTime::Micros1700 => core::time::Duration::from_micros(1700), | ||
| 42 | RampTime::Micros3400 => core::time::Duration::from_micros(3400), | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | #[cfg(feature = "time")] | ||
| 48 | impl From<RampTime> for embassy_time::Duration { | ||
| 49 | fn from(rt: RampTime) -> Self { | ||
| 50 | match rt { | ||
| 51 | RampTime::Micros10 => embassy_time::Duration::from_micros(10), | ||
| 52 | RampTime::Micros20 => embassy_time::Duration::from_micros(20), | ||
| 53 | RampTime::Micros40 => embassy_time::Duration::from_micros(40), | ||
| 54 | RampTime::Micros80 => embassy_time::Duration::from_micros(80), | ||
| 55 | RampTime::Micros200 => embassy_time::Duration::from_micros(200), | ||
| 56 | RampTime::Micros800 => embassy_time::Duration::from_micros(800), | ||
| 57 | RampTime::Micros1700 => embassy_time::Duration::from_micros(1700), | ||
| 58 | RampTime::Micros3400 => embassy_time::Duration::from_micros(3400), | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | /// Transmit parameters, output power and power amplifier ramp up time. | ||
| 63 | /// | ||
| 64 | /// Argument of [`set_tx_params`][`super::SubGhz::set_tx_params`]. | ||
| 65 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 66 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 67 | pub struct TxParams { | ||
| 68 | buf: [u8; 3], | ||
| 69 | } | ||
| 70 | |||
| 71 | impl TxParams { | ||
| 72 | /// Optimal power setting for +15dBm output power with the low-power PA. | ||
| 73 | /// | ||
| 74 | /// This must be used with [`PaConfig::LP_15`](super::PaConfig::LP_15). | ||
| 75 | pub const LP_15: TxParams = TxParams::new().set_power(0x0E); | ||
| 76 | |||
| 77 | /// Optimal power setting for +14dBm output power with the low-power PA. | ||
| 78 | /// | ||
| 79 | /// This must be used with [`PaConfig::LP_14`](super::PaConfig::LP_14). | ||
| 80 | pub const LP_14: TxParams = TxParams::new().set_power(0x0E); | ||
| 81 | |||
| 82 | /// Optimal power setting for +10dBm output power with the low-power PA. | ||
| 83 | /// | ||
| 84 | /// This must be used with [`PaConfig::LP_10`](super::PaConfig::LP_10). | ||
| 85 | pub const LP_10: TxParams = TxParams::new().set_power(0x0D); | ||
| 86 | |||
| 87 | /// Optimal power setting for the high-power PA. | ||
| 88 | /// | ||
| 89 | /// This must be used with one of: | ||
| 90 | /// | ||
| 91 | /// * [`PaConfig::HP_22`](super::PaConfig::HP_22) | ||
| 92 | /// * [`PaConfig::HP_20`](super::PaConfig::HP_20) | ||
| 93 | /// * [`PaConfig::HP_17`](super::PaConfig::HP_17) | ||
| 94 | /// * [`PaConfig::HP_14`](super::PaConfig::HP_14) | ||
| 95 | pub const HP: TxParams = TxParams::new().set_power(0x16); | ||
| 96 | |||
| 97 | /// Create a new `TxParams` struct. | ||
| 98 | /// | ||
| 99 | /// This is the same as `default`, but in a `const` function. | ||
| 100 | /// | ||
| 101 | /// # Example | ||
| 102 | /// | ||
| 103 | /// ``` | ||
| 104 | /// use stm32wlxx_hal::subghz::TxParams; | ||
| 105 | /// | ||
| 106 | /// const TX_PARAMS: TxParams = TxParams::new(); | ||
| 107 | /// assert_eq!(TX_PARAMS, TxParams::default()); | ||
| 108 | /// ``` | ||
| 109 | pub const fn new() -> TxParams { | ||
| 110 | TxParams { | ||
| 111 | buf: [super::OpCode::SetTxParams as u8, 0x00, 0x00], | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Set the output power. | ||
| 116 | /// | ||
| 117 | /// For low power selected in [`set_pa_config`]: | ||
| 118 | /// | ||
| 119 | /// * 0x0E: +14 dB | ||
| 120 | /// * ... | ||
| 121 | /// * 0x00: 0 dB | ||
| 122 | /// * ... | ||
| 123 | /// * 0xEF: -17 dB | ||
| 124 | /// * Others: reserved | ||
| 125 | /// | ||
| 126 | /// For high power selected in [`set_pa_config`]: | ||
| 127 | /// | ||
| 128 | /// * 0x16: +22 dB | ||
| 129 | /// * ... | ||
| 130 | /// * 0x00: 0 dB | ||
| 131 | /// * ... | ||
| 132 | /// * 0xF7: -9 dB | ||
| 133 | /// * Others: reserved | ||
| 134 | /// | ||
| 135 | /// # Example | ||
| 136 | /// | ||
| 137 | /// Set the output power to 0 dB. | ||
| 138 | /// | ||
| 139 | /// ``` | ||
| 140 | /// use stm32wlxx_hal::subghz::{RampTime, TxParams}; | ||
| 141 | /// | ||
| 142 | /// const TX_PARAMS: TxParams = TxParams::new().set_power(0x00); | ||
| 143 | /// # assert_eq!(TX_PARAMS.as_slice()[1], 0x00); | ||
| 144 | /// ``` | ||
| 145 | /// | ||
| 146 | /// [`set_pa_config`]: super::SubGhz::set_pa_config | ||
| 147 | #[must_use = "set_power returns a modified TxParams"] | ||
| 148 | pub const fn set_power(mut self, power: u8) -> TxParams { | ||
| 149 | self.buf[1] = power; | ||
| 150 | self | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Set the Power amplifier ramp time for FSK, MSK, and LoRa modulation. | ||
| 154 | /// | ||
| 155 | /// # Example | ||
| 156 | /// | ||
| 157 | /// Set the ramp time to 200 microseconds. | ||
| 158 | /// | ||
| 159 | /// ``` | ||
| 160 | /// use stm32wlxx_hal::subghz::{RampTime, TxParams}; | ||
| 161 | /// | ||
| 162 | /// const TX_PARAMS: TxParams = TxParams::new().set_ramp_time(RampTime::Micros200); | ||
| 163 | /// # assert_eq!(TX_PARAMS.as_slice()[2], 0x04); | ||
| 164 | /// ``` | ||
| 165 | #[must_use = "set_ramp_time returns a modified TxParams"] | ||
| 166 | pub const fn set_ramp_time(mut self, rt: RampTime) -> TxParams { | ||
| 167 | self.buf[2] = rt as u8; | ||
| 168 | self | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Extracts a slice containing the packet. | ||
| 172 | /// | ||
| 173 | /// # Example | ||
| 174 | /// | ||
| 175 | /// ``` | ||
| 176 | /// use stm32wlxx_hal::subghz::{RampTime, TxParams}; | ||
| 177 | /// | ||
| 178 | /// const TX_PARAMS: TxParams = TxParams::new() | ||
| 179 | /// .set_ramp_time(RampTime::Micros80) | ||
| 180 | /// .set_power(0x0E); | ||
| 181 | /// assert_eq!(TX_PARAMS.as_slice(), &[0x8E, 0x0E, 0x03]); | ||
| 182 | /// ``` | ||
| 183 | pub const fn as_slice(&self) -> &[u8] { | ||
| 184 | &self.buf | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | impl Default for TxParams { | ||
| 189 | fn default() -> Self { | ||
| 190 | Self::new() | ||
| 191 | } | ||
| 192 | } | ||
diff --git a/embassy-stm32/src/subghz/value_error.rs b/embassy-stm32/src/subghz/value_error.rs deleted file mode 100644 index 6a0b489a8..000000000 --- a/embassy-stm32/src/subghz/value_error.rs +++ /dev/null | |||
| @@ -1,129 +0,0 @@ | |||
| 1 | /// Error for a value that is out-of-bounds. | ||
| 2 | /// | ||
| 3 | /// Used by [`Timeout::from_duration`]. | ||
| 4 | /// | ||
| 5 | /// [`Timeout::from_duration`]: super::Timeout::from_duration | ||
| 6 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct ValueError<T> { | ||
| 9 | value: T, | ||
| 10 | limit: T, | ||
| 11 | over: bool, | ||
| 12 | } | ||
| 13 | |||
| 14 | impl<T> ValueError<T> { | ||
| 15 | /// Create a new `ValueError` for a value that exceeded an upper bound. | ||
| 16 | /// | ||
| 17 | /// Unfortunately panic is not available in `const fn`, so there are no | ||
| 18 | /// guarantees on the value being greater than the limit. | ||
| 19 | /// | ||
| 20 | /// # Example | ||
| 21 | /// | ||
| 22 | /// ``` | ||
| 23 | /// use stm32wlxx_hal::subghz::ValueError; | ||
| 24 | /// | ||
| 25 | /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8); | ||
| 26 | /// assert!(ERROR.over()); | ||
| 27 | /// assert!(!ERROR.under()); | ||
| 28 | /// ``` | ||
| 29 | pub const fn too_high(value: T, limit: T) -> ValueError<T> { | ||
| 30 | ValueError { | ||
| 31 | value, | ||
| 32 | limit, | ||
| 33 | over: true, | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Create a new `ValueError` for a value that exceeded a lower bound. | ||
| 38 | /// | ||
| 39 | /// Unfortunately panic is not available in `const fn`, so there are no | ||
| 40 | /// guarantees on the value being less than the limit. | ||
| 41 | /// | ||
| 42 | /// # Example | ||
| 43 | /// | ||
| 44 | /// ``` | ||
| 45 | /// use stm32wlxx_hal::subghz::ValueError; | ||
| 46 | /// | ||
| 47 | /// const ERROR: ValueError<u8> = ValueError::too_low(200u8, 201u8); | ||
| 48 | /// assert!(ERROR.under()); | ||
| 49 | /// assert!(!ERROR.over()); | ||
| 50 | /// ``` | ||
| 51 | pub const fn too_low(value: T, limit: T) -> ValueError<T> { | ||
| 52 | ValueError { | ||
| 53 | value, | ||
| 54 | limit, | ||
| 55 | over: false, | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Get the value that caused the error. | ||
| 60 | /// | ||
| 61 | /// # Example | ||
| 62 | /// | ||
| 63 | /// ``` | ||
| 64 | /// use stm32wlxx_hal::subghz::ValueError; | ||
| 65 | /// | ||
| 66 | /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8); | ||
| 67 | /// assert_eq!(ERROR.value(), &101u8); | ||
| 68 | /// ``` | ||
| 69 | pub const fn value(&self) -> &T { | ||
| 70 | &self.value | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Get the limit for the value. | ||
| 74 | /// | ||
| 75 | /// # Example | ||
| 76 | /// | ||
| 77 | /// ``` | ||
| 78 | /// use stm32wlxx_hal::subghz::ValueError; | ||
| 79 | /// | ||
| 80 | /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8); | ||
| 81 | /// assert_eq!(ERROR.limit(), &100u8); | ||
| 82 | /// ``` | ||
| 83 | pub const fn limit(&self) -> &T { | ||
| 84 | &self.limit | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Returns `true` if the value was over the limit. | ||
| 88 | /// | ||
| 89 | /// # Example | ||
| 90 | /// | ||
| 91 | /// ``` | ||
| 92 | /// use stm32wlxx_hal::subghz::ValueError; | ||
| 93 | /// | ||
| 94 | /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8); | ||
| 95 | /// assert!(ERROR.over()); | ||
| 96 | /// assert!(!ERROR.under()); | ||
| 97 | /// ``` | ||
| 98 | pub const fn over(&self) -> bool { | ||
| 99 | self.over | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Returns `true` if the value was under the limit. | ||
| 103 | /// | ||
| 104 | /// # Example | ||
| 105 | /// | ||
| 106 | /// ``` | ||
| 107 | /// use stm32wlxx_hal::subghz::ValueError; | ||
| 108 | /// | ||
| 109 | /// const ERROR: ValueError<u8> = ValueError::too_low(200u8, 201u8); | ||
| 110 | /// assert!(ERROR.under()); | ||
| 111 | /// assert!(!ERROR.over()); | ||
| 112 | /// ``` | ||
| 113 | pub const fn under(&self) -> bool { | ||
| 114 | !self.over | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl<T> core::fmt::Display for ValueError<T> | ||
| 119 | where | ||
| 120 | T: core::fmt::Display, | ||
| 121 | { | ||
| 122 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 123 | if self.over { | ||
| 124 | write!(f, "Value is too high {} > {}", self.value, self.limit) | ||
| 125 | } else { | ||
| 126 | write!(f, "Value is too low {} < {}", self.value, self.limit) | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 59f30a9be..77be46ffd 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 | |||
| 18 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } | 18 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } |
| 19 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } | 19 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } |
| 20 | embedded-io = "0.4.0" | 20 | embedded-io = "0.4.0" |
| 21 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } | 21 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } |
| 22 | lora-phy = { version = "1", optional = true } | 22 | lora-phy = { version = "1", optional = true } |
| 23 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } | 23 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } |
| 24 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } | 24 | 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 ca022e254..e9d2127c5 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 | |||
| 14 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 14 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 15 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 15 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 16 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 16 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 17 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } | 17 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } |
| 18 | lora-phy = { version = "1", optional = true } | 18 | lora-phy = { version = "1", optional = true } |
| 19 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } | 19 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } |
| 20 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } | 20 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } |
