diff --git a/nrf-hal-common/Cargo.toml b/nrf-hal-common/Cargo.toml index 7f9a07fe..3bbe3582 100644 --- a/nrf-hal-common/Cargo.toml +++ b/nrf-hal-common/Cargo.toml @@ -28,6 +28,8 @@ cfg-if = "1.0.0" embedded-dma = "0.1.1" embedded-storage = "0.1.0" +#log = "0.4.14" + [dependencies.void] default-features = false version = "1.0.2" diff --git a/nrf-hal-common/src/ieee802154.rs b/nrf-hal-common/src/ieee802154.rs index 0066d773..47a0c3ec 100644 --- a/nrf-hal-common/src/ieee802154.rs +++ b/nrf-hal-common/src/ieee802154.rs @@ -1,6 +1,7 @@ //! IEEE 802.15.4 radio use core::{ + fmt, marker::PhantomData, ops::{self, RangeFrom}, sync::atomic::{self, Ordering}, @@ -25,6 +26,15 @@ pub struct Radio<'c> { needs_enable: bool, // used to freeze `Clocks` _clocks: PhantomData<&'c ()>, + rxbuffer: Packet, + receiving: bool, +} + +impl fmt::Debug for Radio<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let channel = self.radio.frequency.read().frequency().bits() / 5 + 10; + write!(f, "Radio(ch={})", channel) + } } /// Default Clear Channel Assessment method = Carrier sense @@ -155,6 +165,8 @@ impl<'c> Radio<'c> { needs_enable: false, radio, _clocks: PhantomData, + rxbuffer: Packet::new(), + receiving: false, }; // shortcuts will be kept off by default and only be temporarily enabled within blocking @@ -326,6 +338,48 @@ impl<'c> Radio<'c> { } } + pub fn recv_async(&mut self) -> nb::Result { + if !self.receiving { + unsafe { + // TODO: Ensure the following assumption still holds true! + // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's + // allocated in RAM + let ptr = self.rxbuffer.buffer.as_mut_ptr() as u32; + self.start_recv_ptr(ptr); + self.receiving = true; + } + } + if self.radio.events_end.read().events_end().bit_is_set() { + self.wait_for_event(Event::End); + dma_end_fence(); + self.receiving = false; + + let crc = self.radio.rxcrc.read().rxcrc().bits() as u16; + let crcstatus = self.radio.crcstatus.read().crcstatus().bit_is_set(); + + let packet = self.rxbuffer.clone(); + /* + unsafe { + // Data is captured into a new Packet object, re-start rx for the next one + // start transfer + dma_start_fence(); + self.radio.tasks_start.write(|w| w.tasks_start().set_bit()); + self.receiving = true; + }*/ + + //log::trace!("Received crc: {}, crcstatus: {}", crc, crcstatus); + //log::trace!("Data: {}", str::from_utf8(&*packet).expect("msg is not valid UTF-8 data")); + + if crcstatus { + Ok(packet) + } else { + Err(nb::Error::Other(Error::AsyncCrc(packet, crc))) + } + } else { + Err(nb::Error::WouldBlock) + } + } + /// Listens for a packet for no longer than the specified amount of microseconds /// and copies its contents into the given `packet` buffer /// @@ -387,7 +441,8 @@ impl<'c> Radio<'c> { } } - unsafe fn start_recv(&mut self, packet: &mut Packet) { + unsafe fn start_recv_ptr(&mut self, ptr: u32) { + //log::trace!("start_recv_ptr({})", ptr); // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's // allocated in RAM @@ -401,13 +456,17 @@ impl<'c> Radio<'c> { // set up RX buffer self.radio .packetptr - .write(|w| w.packetptr().bits(packet.buffer.as_mut_ptr() as u32)); + .write(|w| w.packetptr().bits(ptr)); // start transfer dma_start_fence(); self.radio.tasks_start.write(|w| w.tasks_start().set_bit()); } + unsafe fn start_recv(&mut self, packet: &mut Packet) { + self.start_recv_ptr(packet.buffer.as_mut_ptr() as u32) + } + fn cancel_recv(&mut self) { self.radio.tasks_stop.write(|w| w.tasks_stop().set_bit()); self.wait_for_state_a(STATE_A::RXIDLE); @@ -615,6 +674,11 @@ impl<'c> Radio<'c> { /// Moves the radio to the RXIDLE state fn put_in_rx_mode(&mut self) { + if self.receiving { + self.cancel_recv(); + self.receiving = false; + } + let state = self.state(); let (disable, enable) = match state { @@ -622,6 +686,8 @@ impl<'c> Radio<'c> { State::RxIdle => (false, self.needs_enable), // NOTE to avoid errata 204 (see rev1 v1.4) we do TXIDLE -> DISABLED -> RXIDLE State::TxIdle => (true, true), + // cancel_recv above puts us in RxIdle + State::Rx => unreachable!(), }; if disable { @@ -640,6 +706,10 @@ impl<'c> Radio<'c> { /// Moves the radio to the TXIDLE state fn put_in_tx_mode(&mut self) { + if self.receiving { + self.cancel_recv(); + self.receiving = false; + } let state = self.state(); if state != State::TxIdle || self.needs_enable { @@ -655,6 +725,7 @@ impl<'c> Radio<'c> { STATE_A::DISABLED => State::Disabled, STATE_A::TXIDLE => State::TxIdle, STATE_A::RXIDLE => State::RxIdle, + STATE_A::RX => State::Rx, _ => unreachable!(), } } else { @@ -688,12 +759,14 @@ impl<'c> Radio<'c> { } /// Error -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Error { /// Incorrect CRC Crc(u16), /// Timeout Timeout, + /// Incorrect CRC + AsyncCrc(Packet, u16), } /// Driver state @@ -705,6 +778,7 @@ enum State { Disabled, RxIdle, TxIdle, + Rx, } /// NOTE must be followed by a volatile write operation @@ -733,6 +807,7 @@ enum Event { /// `copy_from_slice` methods. These methods will automatically update the PHR. /// /// See figure 119 in the Product Specification of the nRF52840 for more details +#[derive(Debug, PartialEq)] pub struct Packet { buffer: [u8; Self::SIZE], } @@ -785,6 +860,16 @@ impl Packet { self.buffer[Self::PHY_HDR] = len + Self::CRC; } + pub fn get_data_crc_included(&self) -> &[u8] { + let full_len = self.buffer[Self::PHY_HDR] as usize; + &self.buffer[Self::DATA][..full_len] + } + + pub fn get_data_crc_included_mut(&mut self) -> &mut [u8] { + let full_len = self.buffer[Self::PHY_HDR] as usize; + &mut self.buffer[Self::DATA][..full_len] + } + /// Returns the LQI (Link Quality Indicator) of the received packet /// /// Note that the LQI is stored in the `Packet`'s internal buffer by the hardware so the value @@ -799,6 +884,15 @@ impl Packet { } } +impl Clone for Packet { + fn clone(&self) -> Self { + let mut res = Self::new(); + res.buffer.clone_from_slice(&self.buffer); + //log::trace!("Packet.clone {} {}", self.len(), res.len()); + res + } +} + impl ops::Deref for Packet { type Target = [u8];