aboutsummaryrefslogtreecommitdiff
path: root/embassy-lora
diff options
context:
space:
mode:
authorTimo Kröger <[email protected]>2022-06-25 07:01:31 +0200
committerTimo Kröger <[email protected]>2022-08-26 15:44:57 +0200
commit8e8106ef555373f8b0f04fe1f67684efef435a94 (patch)
tree7e53fc6734b38cb3821ffe2b9b19a5cd0f9dd2e3 /embassy-lora
parent61c666212fa04571a74c27f649526459a376aa8a (diff)
lora: Improve IRQ handling
* Interrupt handler only triggers a waker: Do the actual interrupt processing which involves SUBGHZ SPI coms in the task. * Do not require a static state for the constructor. * Remove unsafe from construcor.
Diffstat (limited to 'embassy-lora')
-rw-r--r--embassy-lora/src/stm32wl/mod.rs122
1 files changed, 50 insertions, 72 deletions
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 @@
1//! A radio driver integration for the radio found on STM32WL family devices. 1//! A radio driver integration for the radio found on STM32WL family devices.
2use core::future::Future; 2use core::future::Future;
3use core::mem::MaybeUninit; 3use core::task::Poll;
4 4
5use embassy_hal_common::{into_ref, PeripheralRef}; 5use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
6use embassy_stm32::dma::NoDma; 6use embassy_stm32::dma::NoDma;
7use embassy_stm32::gpio::{AnyPin, Output}; 7use embassy_stm32::gpio::{AnyPin, Output};
8use embassy_stm32::interrupt::{InterruptExt, SUBGHZ_RADIO}; 8use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO};
9use embassy_stm32::subghz::{ 9use embassy_stm32::subghz::{
10 CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams, 10 CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams,
11 LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, 11 LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk,
12 Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, 12 Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams,
13}; 13};
14use embassy_stm32::Peripheral; 14use embassy_sync::waitqueue::AtomicWaker;
15use embassy_sync::signal::Signal; 15use futures::future::poll_fn;
16use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; 16use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
17use lorawan_device::async_device::Timings; 17use lorawan_device::async_device::Timings;
18 18
@@ -28,65 +28,43 @@ pub enum State {
28#[cfg_attr(feature = "defmt", derive(defmt::Format))] 28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub struct RadioError; 29pub struct RadioError;
30 30
31static IRQ: Signal<(Status, u16)> = Signal::new(); 31static IRQ_WAKER: AtomicWaker = AtomicWaker::new();
32 32
33struct StateInner<'d> { 33/// The radio peripheral keeping the radio state and owning the radio IRQ.
34pub struct SubGhzRadio<'d, RS> {
34 radio: SubGhz<'d, NoDma, NoDma>, 35 radio: SubGhz<'d, NoDma, NoDma>,
35 switch: RadioSwitch<'d>, 36 switch: RS,
37 irq: PeripheralRef<'d, SUBGHZ_RADIO>,
36} 38}
37 39
38/// External state storage for the radio state 40#[derive(Default)]
39pub struct SubGhzState<'a>(MaybeUninit<StateInner<'a>>); 41#[non_exhaustive]
40impl<'d> SubGhzState<'d> { 42pub struct SubGhzRadioConfig {
41 pub const fn new() -> Self { 43 pub reg_mode: RegMode,
42 Self(MaybeUninit::uninit()) 44 pub calibrate_image: CalibrateImage,
43 }
44} 45}
45 46
46/// The radio peripheral keeping the radio state and owning the radio IRQ. 47impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
47pub struct SubGhzRadio<'d> {
48 state: *mut StateInner<'d>,
49 _irq: PeripheralRef<'d, SUBGHZ_RADIO>,
50}
51
52impl<'d> SubGhzRadio<'d> {
53 /// Create a new instance of a SubGhz radio for LoRaWAN. 48 /// Create a new instance of a SubGhz radio for LoRaWAN.
54 /// 49 pub fn new(
55 /// # Safety 50 mut radio: SubGhz<'d, NoDma, NoDma>,
56 /// Do not leak self or futures 51 switch: RS,
57 pub unsafe fn new(
58 state: &'d mut SubGhzState<'d>,
59 radio: SubGhz<'d, NoDma, NoDma>,
60 switch: RadioSwitch<'d>,
61 irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd, 52 irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd,
62 ) -> Self { 53 config: SubGhzRadioConfig,
54 ) -> Result<Self, RadioError> {
63 into_ref!(irq); 55 into_ref!(irq);
64 56
65 let mut inner = StateInner { radio, switch }; 57 radio.reset();
66 inner.radio.reset();
67
68 let state_ptr = state.0.as_mut_ptr();
69 state_ptr.write(inner);
70 58
71 irq.disable(); 59 irq.disable();
72 irq.set_handler(|p| { 60 irq.set_handler(|_| {
73 // This is safe because we only get interrupts when configured for, so 61 IRQ_WAKER.wake();
74 // the radio will be awaiting on the signal at this point. If not, the ISR will 62 unsafe { SUBGHZ_RADIO::steal().disable() };
75 // anyway only adjust the state in the IRQ signal state.
76 let state = &mut *(p as *mut StateInner<'d>);
77 state.on_interrupt();
78 }); 63 });
79 irq.set_handler_context(state_ptr as *mut ());
80 irq.enable();
81 64
82 Self { 65 Self { radio, switch, irq }
83 state: state_ptr,
84 _irq: irq,
85 }
86 } 66 }
87}
88 67
89impl<'d> StateInner<'d> {
90 /// Configure radio settings in preparation for TX or RX 68 /// Configure radio settings in preparation for TX or RX
91 pub(crate) fn configure(&mut self) -> Result<(), RadioError> { 69 pub(crate) fn configure(&mut self) -> Result<(), RadioError> {
92 trace!("Configuring STM32WL SUBGHZ radio"); 70 trace!("Configuring STM32WL SUBGHZ radio");
@@ -151,8 +129,7 @@ impl<'d> StateInner<'d> {
151 self.radio.set_tx(Timeout::DISABLED)?; 129 self.radio.set_tx(Timeout::DISABLED)?;
152 130
153 loop { 131 loop {
154 let (_status, irq_status) = IRQ.wait().await; 132 let (_status, irq_status) = self.irq_wait().await;
155 IRQ.reset();
156 133
157 if irq_status & Irq::TxDone.mask() != 0 { 134 if irq_status & Irq::TxDone.mask() != 0 {
158 let stats = self.radio.lora_stats()?; 135 let stats = self.radio.lora_stats()?;
@@ -214,8 +191,8 @@ impl<'d> StateInner<'d> {
214 trace!("RX started"); 191 trace!("RX started");
215 192
216 loop { 193 loop {
217 let (status, irq_status) = IRQ.wait().await; 194 let (status, irq_status) = self.irq_wait().await;
218 IRQ.reset(); 195
219 trace!("RX IRQ {:?}, {:?}", status, irq_status); 196 trace!("RX IRQ {:?}, {:?}", status, irq_status);
220 if irq_status & Irq::RxDone.mask() != 0 { 197 if irq_status & Irq::RxDone.mask() != 0 {
221 let (status, len, ptr) = self.radio.rx_buffer_status()?; 198 let (status, len, ptr) = self.radio.rx_buffer_status()?;
@@ -238,17 +215,24 @@ impl<'d> StateInner<'d> {
238 } 215 }
239 } 216 }
240 217
241 /// Read interrupt status and store in global signal 218 async fn irq_wait(&mut self) -> (Status, u16) {
242 fn on_interrupt(&mut self) { 219 poll_fn(|cx| {
243 let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); 220 self.irq.unpend();
244 self.radio 221 self.irq.enable();
245 .clear_irq_status(irq_status) 222 IRQ_WAKER.register(cx.waker());
246 .expect("error clearing irq status"); 223
247 if irq_status & Irq::PreambleDetected.mask() != 0 { 224 let (status, irq_status) = self.radio.irq_status().expect("error getting irq status");
248 trace!("Preamble detected, ignoring"); 225 self.radio
249 } else { 226 .clear_irq_status(irq_status)
250 IRQ.signal((status, irq_status)); 227 .expect("error clearing irq status");
251 } 228 trace!("IRQ status: {=u16:b}", irq_status);
229 if irq_status == 0 {
230 Poll::Pending
231 } else {
232 Poll::Ready((status, irq_status))
233 }
234 })
235 .await
252 } 236 }
253} 237}
254 238
@@ -257,18 +241,12 @@ impl PhyRxTx for SubGhzRadio<'static> {
257 241
258 type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm; 242 type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm;
259 fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { 243 fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
260 async move { 244 async move { self.do_tx(config, buf).await }
261 let inner = unsafe { &mut *self.state };
262 inner.do_tx(config, buf).await
263 }
264 } 245 }
265 246
266 type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm; 247 type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm;
267 fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { 248 fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
268 async move { 249 async move { self.do_rx(config, buf).await }
269 let inner = unsafe { &mut *self.state };
270 inner.do_rx(config, buf).await
271 }
272 } 250 }
273} 251}
274 252
@@ -278,7 +256,7 @@ impl From<embassy_stm32::spi::Error> for RadioError {
278 } 256 }
279} 257}
280 258
281impl<'d> Timings for SubGhzRadio<'d> { 259impl<'d, RS> Timings for SubGhzRadio<'d, RS> {
282 fn get_rx_window_offset_ms(&self) -> i32 { 260 fn get_rx_window_offset_ms(&self) -> i32 {
283 -200 261 -200
284 } 262 }