Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added non-blocking radio receive #485

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 80 additions & 49 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(&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,31 @@ 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
self.recv_non_blocking(packet, |recv| {
// 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<'a, T>(
&'a mut self,
packet: &'a mut Packet,
f: impl FnOnce(&Recv<'a, 'c>) -> T,
) -> T {
// Start the read
// NOTE(unsafe) We block until reception completes or errors
// NOTE(unsafe)
// The packet must live until the transfer is done. Receive is handled inside
// a closure to ensure this
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)
}
let recv = Recv::new(self);
f(&recv)
}

/// Listens for a packet for no longer than the specified amount of microseconds
Expand All @@ -347,41 +399,25 @@ 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;

loop {
if self.radio.events_end.read().bits() != 0 {
// transfer complete
dma_end_fence();
recv_completed = true;
break;
}

if timer.reset_if_finished() {
// timeout
break;
}
}
// Start non-blocking receive
self.recv_non_blocking(packet, |recv| {
// Check if either receive is done or timeout occured
loop {
match recv.is_done() {
Ok(crc) => break Ok(crc),
Err(err) => match err {
nb::Error::Other(crc) => break Err(Error::Crc(crc)),
nb::Error::WouldBlock => (),
},
}

if !recv_completed {
// Cancel the reception if it did not complete until now
self.cancel_recv();
Err(Error::Timeout)
} 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))
if timer.reset_if_finished() {
// Break loop in case of timeout. Receive is
// cancelled when `recv` is dropped.
break Err(Error::Timeout);
}
}
}
})
}

unsafe fn start_recv(&mut self, packet: &mut Packet) {
Expand Down Expand Up @@ -674,10 +710,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 +760,6 @@ fn dma_end_fence() {
}

enum Event {
End,
PhyEnd,
}

Expand Down
Loading