esb_ng/
irq.rs

1use crate::{
2    app::{Addresses, FramedConsumer, FramedProducer},
3    payload::{EsbHeader, PayloadR, PayloadW},
4    peripherals::{EsbRadio, EsbTimer, Interrupt, RxPayloadState},
5    Config, Error, RAMP_UP_TIME,
6};
7use core::{
8    marker::PhantomData,
9    sync::atomic::{AtomicBool, Ordering},
10};
11use cortex_m::peripheral::NVIC;
12
13/// Type to represent the driver in the disabled mode
14pub struct Disabled;
15
16/// The current state of the radio when in PTX mode
17#[derive(PartialEq, Copy, Clone, Debug)]
18pub enum StatePTX {
19    /// The radio is idle in PTX mode
20    IdleTx,
21    /// ESB on PTX state transmitting.
22    TransmitterTx,
23    TransmitterTxNoAck,
24    /// ESB on PTX state waiting for ack.
25    TransmitterWaitAck,
26    /// ESB on PTX state waiting for the retransmit timeout.
27    TransmitterWaitRetransmit,
28}
29
30/// The current state of the radio when in PRX mode
31#[derive(PartialEq, Copy, Clone, Debug)]
32pub enum StatePRX {
33    /// The radio is idle in PRX mode
34    IdleRx,
35    /// ESB in the PRX state listening for packets
36    Receiver,
37    /// Transmitting the acknowledgement
38    TransmittingAck,
39    /// Transmitting the acknowledgement for a repeated packet
40    TransmittingRepeatedAck,
41}
42
43pub struct IrqTimer<T: EsbTimer> {
44    /// Flag to determine if the timer caused the interrupt
45    pub(crate) timer_flag: &'static AtomicBool,
46    pub(crate) _timer: PhantomData<T>,
47}
48
49impl<T: EsbTimer> IrqTimer<T> {
50    /// Must be called inside the timer interrupt handler.
51    pub fn timer_interrupt(&mut self) {
52        // Check which event triggered the timer
53        let retransmit_event = T::is_retransmit_pending();
54        let ack_timeout_event = T::is_ack_pending();
55
56        if retransmit_event {
57            T::clear_interrupt_retransmit();
58        }
59        // Retransmit might have been triggered just after ack, check and clear ack too
60        if ack_timeout_event {
61            T::clear_interrupt_ack();
62        }
63        self.timer_flag.store(true, Ordering::Release);
64        NVIC::pend(Interrupt::RADIO);
65    }
66}
67
68/// This is the primary RADIO-interrupt-side interface.
69///
70/// It is intended to be used inside of the `RADIO` interrupt,
71/// and allows for sending or receiving frames from the Application
72/// hardware.
73pub struct EsbIrq<const OUT: usize, const IN: usize, Timer, STATE>
74where
75    Timer: EsbTimer,
76{
77    /// Producer to send incoming frames FROM the radio, TO the application
78    pub(crate) prod_to_app: FramedProducer<IN>,
79
80    /// Consumer to receive outgoing frames TO the radio, FROM the application
81    pub(crate) cons_from_app: FramedConsumer<OUT>,
82
83    /// Peripheral timer, use for ACK and other timeouts
84    pub(crate) timer: Timer,
85
86    /// Wrapping structure of the nRF RADIO peripheral
87    pub(crate) radio: EsbRadio<OUT, IN>,
88
89    /// Current state of the Radio/IRQ task
90    pub(crate) state: STATE,
91
92    /// The assigned addresses of the ESB radio an pipes
93    pub(crate) addresses: Addresses,
94
95    /// The number of attempts to send the current packet
96    pub(crate) attempts: u8,
97
98    /// Flag to determine if the timer caused the interrupt
99    pub(crate) timer_flag: &'static AtomicBool,
100
101    /// Protocol configuration
102    pub(crate) config: Config,
103}
104
105struct Events {
106    disabled: bool,
107    timer: bool,
108}
109
110impl<const OUT: usize, const IN: usize, Timer, STATE> EsbIrq<OUT, IN, Timer, STATE>
111where
112    Timer: EsbTimer,
113{
114    /// Puts the driver in the disabled state
115    pub fn into_disabled(mut self) -> EsbIrq<OUT, IN, Timer, Disabled> {
116        // Put the radio in a known state
117        self.radio.stop(true);
118        Timer::clear_interrupt_retransmit();
119        Timer::clear_interrupt_ack();
120        let _ = self.check_and_clear_flags();
121
122        EsbIrq {
123            prod_to_app: self.prod_to_app,
124            cons_from_app: self.cons_from_app,
125            timer: self.timer,
126            radio: self.radio,
127            state: Disabled,
128            addresses: self.addresses,
129            attempts: 0,
130            timer_flag: self.timer_flag,
131            config: self.config,
132        }
133    }
134
135    fn check_and_clear_flags(&mut self) -> Events {
136        let evts = Events {
137            disabled: self.radio.check_disabled_event(),
138            timer: self.timer_flag.load(Ordering::Acquire),
139        };
140
141        if evts.disabled {
142            self.radio.clear_disabled_event();
143        }
144        if evts.timer {
145            self.timer_flag.store(false, Ordering::Release);
146        }
147
148        // TODO: Try to remove this, probably not necessary
149        NVIC::unpend(Interrupt::RADIO);
150
151        evts
152    }
153}
154
155impl<const OUT: usize, const IN: usize, Timer> EsbIrq<OUT, IN, Timer, Disabled>
156where
157    Timer: EsbTimer,
158{
159    /// Puts the driver in the PTX mode
160    pub fn into_ptx(self) -> EsbIrq<OUT, IN, Timer, StatePTX> {
161        EsbIrq {
162            prod_to_app: self.prod_to_app,
163            cons_from_app: self.cons_from_app,
164            timer: self.timer,
165            radio: self.radio,
166            state: StatePTX::IdleTx,
167            addresses: self.addresses,
168            attempts: 0,
169            timer_flag: self.timer_flag,
170            config: self.config,
171        }
172    }
173
174    /// Puts the driver in the PRX mode in a idle state, the user must call
175    /// [start_receiving](struct.EsbIrq.html#method.start_receiving) to enable the radio for receiving
176    pub fn into_prx(self) -> EsbIrq<OUT, IN, Timer, StatePRX> {
177        EsbIrq {
178            prod_to_app: self.prod_to_app,
179            cons_from_app: self.cons_from_app,
180            timer: self.timer,
181            radio: self.radio,
182            state: StatePRX::IdleRx,
183            addresses: self.addresses,
184            attempts: 0,
185            timer_flag: self.timer_flag,
186            config: self.config,
187        }
188    }
189}
190
191impl<const OUT: usize, const IN: usize, Timer> EsbIrq<OUT, IN, Timer, StatePTX>
192where
193    Timer: EsbTimer,
194{
195    /// Must be called inside the radio interrupt handler
196    pub fn radio_interrupt(&mut self) -> Result<StatePTX, Error> {
197        let Events { disabled, timer } = self.check_and_clear_flags();
198
199        // We only trigger the interrupt in these three events, if we didn't trigger it then the
200        // user did.
201        let user_event = !disabled && !timer;
202
203        if user_event && self.state != StatePTX::IdleTx {
204            return Ok(self.state);
205        }
206
207        match self.state {
208            StatePTX::IdleTx => {
209                // Interrupt received means that the user pushed a packet to the queue.
210                debug_assert!(user_event, "TransmitterTx de: {}, te: {}", disabled, timer);
211                if user_event {
212                    self.send_packet();
213                }
214            }
215            StatePTX::TransmitterTxNoAck => {
216                // Transmission ended
217                self.radio.finish_tx_no_ack();
218                self.send_packet();
219            }
220            StatePTX::TransmitterTx => {
221                debug_assert!(disabled, "TransmitterTx de: {}, te: {}", disabled, timer);
222
223                let packet = self
224                    .prod_to_app
225                    .grant(u16::from(self.config.maximum_payload_size) + EsbHeader::header_size())
226                    .map(PayloadW::new_from_radio);
227                if let Ok(packet) = packet {
228                    self.radio.prepare_for_ack(packet);
229                    self.state = StatePTX::TransmitterWaitAck;
230                } else {
231                    self.radio.stop(true);
232                    self.state = StatePTX::IdleTx;
233                    return Err(Error::IncomingQueueFull);
234                }
235
236                // The radio will be disabled if we retransmit, because of that we need to take into
237                // account the ramp-up time for TX
238                self.timer
239                    .set_interrupt_retransmit(self.config.retransmit_delay - RAMP_UP_TIME);
240
241                // Takes into account the RX ramp-up time
242                self.timer
243                    .set_interrupt_ack(self.config.wait_for_ack_timeout + RAMP_UP_TIME);
244            }
245            StatePTX::TransmitterWaitAck => {
246                let mut retransmit = false;
247                if disabled {
248                    // We got an ack, check it
249                    Timer::clear_interrupt_ack();
250                    if self.radio.check_ack()? {
251                        // Everything went fine, `clear_interrupt_retransmit` also resets and stops
252                        // the timer
253                        Timer::clear_interrupt_retransmit();
254                        self.attempts = 0;
255                        self.send_packet();
256                    } else {
257                        // CRC mismatch, wait for retransmission
258                        retransmit = true;
259                    }
260                } else {
261                    debug_assert!(timer, "TransmitterWaitAck de: {}, te: {}", disabled, timer);
262                    // Ack timeout
263                    retransmit = true;
264                }
265                if retransmit {
266                    self.radio.stop(true);
267                    self.attempts += 1;
268                    self.state = StatePTX::TransmitterWaitRetransmit;
269                }
270                if self.attempts > self.config.maximum_transmit_attempts {
271                    Timer::clear_interrupt_retransmit();
272
273                    // We reached the maximum number of attempts, `radio.stop()` dropped the radio
274                    // grants and we will release the last packet and try the next one
275                    if let Ok(old_packet) = self.cons_from_app.read() {
276                        old_packet.release();
277                    }
278                    self.attempts = 0;
279                    self.send_packet();
280                    return Err(Error::MaximumAttempts);
281                }
282            }
283            StatePTX::TransmitterWaitRetransmit => {
284                debug_assert!(
285                    timer,
286                    "TransmitterWaitRetransmit de: {}, te: {}",
287                    disabled, timer
288                );
289                // The timer interrupt cleared and stopped the timer by now
290                self.send_packet();
291            }
292        }
293        Ok(self.state)
294    }
295
296    fn send_packet(&mut self) {
297        if let Ok(packet) = self.cons_from_app.read().map(PayloadR::new) {
298            let ack = !packet.no_ack();
299            self.radio.transmit(packet, ack);
300            if ack {
301                self.state = StatePTX::TransmitterTx;
302            } else {
303                self.state = StatePTX::TransmitterTxNoAck;
304            }
305        } else {
306            self.radio.disable_disabled_interrupt();
307            self.state = StatePTX::IdleTx;
308        }
309    }
310}
311
312impl<const OUT: usize, const IN: usize, Timer> EsbIrq<OUT, IN, Timer, StatePRX>
313where
314    Timer: EsbTimer,
315{
316    /// Must be called inside the radio interrupt handler
317    pub fn radio_interrupt(&mut self) -> Result<StatePRX, Error> {
318        let Events { disabled, timer } = self.check_and_clear_flags();
319
320        // We only trigger the interrupt in these three events, if we didn't trigger it then the
321        // user did.
322        let user_event = !disabled && !timer;
323
324        if user_event && self.state != StatePRX::IdleRx {
325            return Ok(self.state);
326        }
327
328        match self.state {
329            StatePRX::Receiver => {
330                debug_assert!(disabled, "Receiver de: {}, te: {}", disabled, timer);
331                // We got a packet, check it
332                match self.radio.check_packet(&mut self.cons_from_app)? {
333                    // Do nothing, the radio will return to rx
334                    RxPayloadState::BadCRC => {}
335                    RxPayloadState::NoAck => {
336                        self.prepare_receiver(|this, grant| {
337                            this.radio.complete_rx_no_ack(Some(grant));
338                            Ok(())
339                        })?;
340                    }
341                    RxPayloadState::RepeatedNoAck => {
342                        // this goes back to rx
343                        self.radio.complete_rx_no_ack(None);
344                    }
345                    RxPayloadState::Ack => {
346                        self.state = StatePRX::TransmittingAck;
347                    }
348                    RxPayloadState::RepeatedAck => {
349                        self.state = StatePRX::TransmittingRepeatedAck;
350                    }
351                }
352            }
353            StatePRX::TransmittingAck => {
354                debug_assert!(disabled, "TransmittingAck de: {}, te: {}", disabled, timer);
355                // We finished transmitting the acknowledgement, get ready for the next packet
356                self.prepare_receiver(|this, grant| {
357                    // This goes back to rx
358                    this.radio.complete_rx_ack(Some(grant))?;
359                    this.state = StatePRX::Receiver;
360                    Ok(())
361                })?;
362            }
363            StatePRX::TransmittingRepeatedAck => {
364                debug_assert!(
365                    disabled,
366                    "TransmittingRepeatedAck de: {}, te: {}",
367                    disabled, timer
368                );
369                // This goes back to rx
370                self.radio.complete_rx_ack(None)?;
371                self.state = StatePRX::Receiver;
372            }
373            StatePRX::IdleRx => {
374                debug_assert!(
375                    user_event,
376                    "TransmittingRepeatedAck de: {}, te: {}",
377                    disabled, timer
378                );
379                self.start_receiving()?;
380            }
381        }
382        Ok(self.state)
383    }
384
385    /// Changes esb to the receiving state
386    pub fn start_receiving(&mut self) -> Result<(), Error> {
387        if self.state == StatePRX::IdleRx {
388            self.prepare_receiver(|this, grant| {
389                this.radio.start_receiving(grant, this.config.enabled_pipes);
390                this.state = StatePRX::Receiver;
391                Ok(())
392            })?;
393        }
394        Ok(())
395    }
396
397    /// Stops the receiving
398    pub fn stop_receiving(&mut self) {
399        // Put the radio in a known state
400        self.radio.stop(true);
401        Timer::clear_interrupt_retransmit();
402        Timer::clear_interrupt_ack();
403        let _ = self.check_and_clear_flags();
404
405        self.state = StatePRX::IdleRx;
406    }
407
408    fn prepare_receiver<F>(&mut self, f: F) -> Result<(), Error>
409    where
410        F: FnOnce(&mut Self, PayloadW<IN>) -> Result<(), Error>,
411    {
412        if let Ok(grant) = self
413            .prod_to_app
414            .grant(u16::from(self.config.maximum_payload_size) + EsbHeader::header_size())
415            .map(PayloadW::new_from_radio)
416        {
417            f(self, grant)?;
418            Ok(())
419        } else {
420            self.radio.stop(true);
421            self.state = StatePRX::IdleRx;
422            Err(Error::IncomingQueueFull)
423        }
424    }
425}