Skip to content

Commit

Permalink
Added non-blocking radio receive
Browse files Browse the repository at this point in the history
  • Loading branch information
eivindbergem committed Aug 18, 2024
1 parent 04cd0c8 commit 8798adc
Showing 1 changed file with 76 additions and 44 deletions.
120 changes: 76 additions & 44 deletions nrf-hal-common/src/ieee802154.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use core::{
sync::atomic::{self, Ordering},
};

use nb::block;

use crate::{
clocks::{Clocks, ExternalOscillator},
pac::{
Expand All @@ -15,6 +17,47 @@ use crate::{
timer::{self, Timer},
};

/// Non-blocking receive
pub struct Recv<'a, 'c> {
radio: &'a mut Radio<'c>,
}

impl<'a, 'c> Recv<'a, 'c> {
fn new(radio: &'a mut Radio<'c>) -> Self {
Self { radio }
}

/// Check if receive is done
///
/// This methods returns the `Ok` variant if the CRC included the
/// packet was successfully validated by the hardware. It returns
/// `Err(nb::Error::WouldBlock)` if a packet hasn't been received
/// yet, and `Err(nb::Error::Other)` if the CRC check failed.
pub fn is_done(&mut self) -> nb::Result<u16, u16> {
if self.radio.radio.events_end.read().events_end().bit_is_set() {
self.radio.radio.events_end.reset();

dma_end_fence();

let crc = self.radio.radio.rxcrc.read().rxcrc().bits() as u16;

if self.radio.radio.crcstatus.read().crcstatus().bit_is_set() {
Ok(crc)
} else {
Err(nb::Error::Other(crc))
}
} else {
Err(nb::Error::WouldBlock)
}
}
}

impl<'a, 'c> Drop for Recv<'a, 'c> {
fn drop(&mut self) {
self.radio.cancel_recv();
}
}

/// IEEE 802.15.4 radio
pub struct Radio<'c> {
radio: RADIO,
Expand Down Expand Up @@ -305,22 +348,24 @@ impl<'c> Radio<'c> {
/// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet`
/// will be updated with the received packet's data
pub fn recv(&mut self, packet: &mut Packet) -> Result<u16, u16> {
// Start non-blocking receive
let mut recv = self.recv_non_blocking(packet);

// Block untill receive is done
block!(recv.is_done())
}

/// Receives one radio packet and copies its contents into the given `packet` buffer
///
/// This method is non-blocking
pub fn recv_non_blocking(&mut self, packet: &mut Packet) -> Recv<'_, 'c> {
// Start the read
// NOTE(unsafe) We block until reception completes or errors
unsafe {
self.start_recv(packet);
}

// wait until we have received something
self.wait_for_event(Event::End);
dma_end_fence();

let crc = self.radio.rxcrc.read().rxcrc().bits() as u16;
if self.radio.crcstatus.read().crcstatus().bit_is_set() {
Ok(crc)
} else {
Err(crc)
}
Recv::new(self)
}

/// Listens for a packet for no longer than the specified amount of microseconds
Expand All @@ -347,40 +392,32 @@ impl<'c> Radio<'c> {
// Start the timeout timer
timer.start(microseconds);

// Start the read
// NOTE(unsafe) We block until reception completes or errors
unsafe {
self.start_recv(packet);
}

// Wait for transmission to end
let mut recv_completed = false;
let result = {
// Start non-blocking receive
let mut recv = self.recv_non_blocking(packet);

loop {
if self.radio.events_end.read().bits() != 0 {
// transfer complete
dma_end_fence();
recv_completed = true;
break;
}
// Check if either receive is done, or timeout occurs
loop {
match recv.is_done() {
Ok(crc) => break Some(Ok(crc)),
Err(err) => match err {
nb::Error::Other(crc) => break Some(Err(Error::Crc(crc))),
nb::Error::WouldBlock => (),
},
}

if timer.reset_if_finished() {
// timeout
break;
if timer.reset_if_finished() {
// Break loop in case of timeout. Receive is
// cancelled when `recv` is dropped.
break None;
}
}
}
};

if !recv_completed {
// Cancel the reception if it did not complete until now
self.cancel_recv();
Err(Error::Timeout)
if let Some(result) = result {
result
} else {
let crc = self.radio.rxcrc.read().rxcrc().bits() as u16;
if self.radio.crcstatus.read().crcstatus().bit_is_set() {
Ok(crc)
} else {
Err(Error::Crc(crc))
}
Err(Error::Timeout)
}
}

Expand Down Expand Up @@ -674,10 +711,6 @@ impl<'c> Radio<'c> {

fn wait_for_event(&self, event: Event) {
match event {
Event::End => {
while self.radio.events_end.read().events_end().bit_is_clear() {}
self.radio.events_end.reset();
}
Event::PhyEnd => {
while self
.radio
Expand Down Expand Up @@ -728,7 +761,6 @@ fn dma_end_fence() {
}

enum Event {
End,
PhyEnd,
}

Expand Down

0 comments on commit 8798adc

Please sign in to comment.