diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 7822d0153..5e1773d55 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,18 +1,18 @@ //! A radio driver integration for the radio found on STM32WL family devices. use core::future::Future; -use core::mem::MaybeUninit; +use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{AnyPin, Output}; -use embassy_stm32::interrupt::{InterruptExt, SUBGHZ_RADIO}; +use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; use embassy_stm32::subghz::{ CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; -use embassy_stm32::Peripheral; -use embassy_sync::signal::Signal; +use embassy_sync::waitqueue::AtomicWaker; +use futures::future::poll_fn; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; @@ -28,65 +28,43 @@ pub enum State { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RadioError; -static IRQ: Signal<(Status, u16)> = Signal::new(); - -struct StateInner<'d> { - radio: SubGhz<'d, NoDma, NoDma>, - switch: RadioSwitch<'d>, -} - -/// External state storage for the radio state -pub struct SubGhzState<'a>(MaybeUninit>); -impl<'d> SubGhzState<'d> { - pub const fn new() -> Self { - Self(MaybeUninit::uninit()) - } -} +static IRQ_WAKER: AtomicWaker = AtomicWaker::new(); /// The radio peripheral keeping the radio state and owning the radio IRQ. -pub struct SubGhzRadio<'d> { - state: *mut StateInner<'d>, - _irq: PeripheralRef<'d, SUBGHZ_RADIO>, +pub struct SubGhzRadio<'d, RS> { + radio: SubGhz<'d, NoDma, NoDma>, + switch: RS, + irq: PeripheralRef<'d, SUBGHZ_RADIO>, } -impl<'d> SubGhzRadio<'d> { +#[derive(Default)] +#[non_exhaustive] +pub struct SubGhzRadioConfig { + pub reg_mode: RegMode, + pub calibrate_image: CalibrateImage, +} + +impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// Create a new instance of a SubGhz radio for LoRaWAN. - /// - /// # Safety - /// Do not leak self or futures - pub unsafe fn new( - state: &'d mut SubGhzState<'d>, - radio: SubGhz<'d, NoDma, NoDma>, - switch: RadioSwitch<'d>, + pub fn new( + mut radio: SubGhz<'d, NoDma, NoDma>, + switch: RS, irq: impl Peripheral

+ 'd, - ) -> Self { + config: SubGhzRadioConfig, + ) -> Result { into_ref!(irq); - let mut inner = StateInner { radio, switch }; - inner.radio.reset(); - - let state_ptr = state.0.as_mut_ptr(); - state_ptr.write(inner); + radio.reset(); irq.disable(); - irq.set_handler(|p| { - // This is safe because we only get interrupts when configured for, so - // the radio will be awaiting on the signal at this point. If not, the ISR will - // anyway only adjust the state in the IRQ signal state. - let state = &mut *(p as *mut StateInner<'d>); - state.on_interrupt(); + irq.set_handler(|_| { + IRQ_WAKER.wake(); + unsafe { SUBGHZ_RADIO::steal().disable() }; }); - irq.set_handler_context(state_ptr as *mut ()); - irq.enable(); - Self { - state: state_ptr, - _irq: irq, - } + Self { radio, switch, irq } } -} -impl<'d> StateInner<'d> { /// Configure radio settings in preparation for TX or RX pub(crate) fn configure(&mut self) -> Result<(), RadioError> { trace!("Configuring STM32WL SUBGHZ radio"); @@ -151,8 +129,7 @@ impl<'d> StateInner<'d> { self.radio.set_tx(Timeout::DISABLED)?; loop { - let (_status, irq_status) = IRQ.wait().await; - IRQ.reset(); + let (_status, irq_status) = self.irq_wait().await; if irq_status & Irq::TxDone.mask() != 0 { let stats = self.radio.lora_stats()?; @@ -214,8 +191,8 @@ impl<'d> StateInner<'d> { trace!("RX started"); loop { - let (status, irq_status) = IRQ.wait().await; - IRQ.reset(); + let (status, irq_status) = self.irq_wait().await; + trace!("RX IRQ {:?}, {:?}", status, irq_status); if irq_status & Irq::RxDone.mask() != 0 { let (status, len, ptr) = self.radio.rx_buffer_status()?; @@ -238,17 +215,24 @@ impl<'d> StateInner<'d> { } } - /// Read interrupt status and store in global signal - fn on_interrupt(&mut self) { - let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); - self.radio - .clear_irq_status(irq_status) - .expect("error clearing irq status"); - if irq_status & Irq::PreambleDetected.mask() != 0 { - trace!("Preamble detected, ignoring"); - } else { - IRQ.signal((status, irq_status)); - } + async fn irq_wait(&mut self) -> (Status, u16) { + poll_fn(|cx| { + self.irq.unpend(); + self.irq.enable(); + IRQ_WAKER.register(cx.waker()); + + let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); + self.radio + .clear_irq_status(irq_status) + .expect("error clearing irq status"); + trace!("IRQ status: {=u16:b}", irq_status); + if irq_status == 0 { + Poll::Pending + } else { + Poll::Ready((status, irq_status)) + } + }) + .await } } @@ -257,18 +241,12 @@ impl PhyRxTx for SubGhzRadio<'static> { type TxFuture<'m> = impl Future> + 'm; fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { - async move { - let inner = unsafe { &mut *self.state }; - inner.do_tx(config, buf).await - } + async move { self.do_tx(config, buf).await } } type RxFuture<'m> = impl Future> + 'm; fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { - async move { - let inner = unsafe { &mut *self.state }; - inner.do_rx(config, buf).await - } + async move { self.do_rx(config, buf).await } } } @@ -278,7 +256,7 @@ impl From for RadioError { } } -impl<'d> Timings for SubGhzRadio<'d> { +impl<'d, RS> Timings for SubGhzRadio<'d, RS> { fn get_rx_window_offset_ms(&self) -> i32 { -200 } diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index fdd7eb8cf..9c390bdf0 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -32,10 +32,9 @@ async fn main(_spawner: Spawner) { let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); - let irq = interrupt::take!(SUBGHZ_RADIO); - static mut RADIO_STATE: SubGhzState<'static> = SubGhzState::new(); - let radio = unsafe { SubGhzRadio::new(&mut RADIO_STATE, radio, rfs, irq) }; + + let radio = SubGhzRadio::new(radio, rfs, irq); let mut region: region::Configuration = region::EU868::default().into();