diff --git a/dw1000/CHANGELOG.md b/dw1000/CHANGELOG.md index b217400..d05045f 100644 --- a/dw1000/CHANGELOG.md +++ b/dw1000/CHANGELOG.md @@ -1,3 +1,9 @@ +### Unreleased + +- Add TX continuation (into RX state) for fast tx-rx turnaround time +- Add auto ack functionality +- Add support for 'raw' messages (aka it's up to the user to encode them as valid ieee 802.15.4 frames) + ### v0.6.0 (2021-12-14) - Add support for double-buffering RX ([#134]) diff --git a/dw1000/src/configs.rs b/dw1000/src/configs.rs index f90e9cc..ae5e163 100644 --- a/dw1000/src/configs.rs +++ b/dw1000/src/configs.rs @@ -26,6 +26,8 @@ pub struct TxConfig { pub sfd_sequence: SfdSequence, /// When true, a CRC will be appended to the message pub append_crc: bool, + /// Setting for how the transmission should be continued + pub continuation: TxContinuation, } impl Default for TxConfig { @@ -38,10 +40,33 @@ impl Default for TxConfig { channel: Default::default(), sfd_sequence: Default::default(), append_crc: true, + continuation: TxContinuation::Ready, } } } +/// Setting for how the transmission should be continued +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +pub enum TxContinuation { + #[default] + /// After the transmission the radio should go back to ready + Ready, + /// After the transmission the radio should go to the receiving state + Rx { + /// Enable frame filtering. See [RxConfig::frame_filtering] + frame_filtering: bool, + /// Used as the [RxConfig::auto_ack] value + auto_ack: AutoAck, + }, + /// After the transmission the radio should go to the double buffered receiving state + RxDoubleBuffered { + /// Enable frame filtering. See [RxConfig::frame_filtering] + frame_filtering: bool, + /// Used as the [RxConfig::auto_ack] value + auto_ack: AutoAck, + }, +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] /// Receive configuration pub struct RxConfig { @@ -68,6 +93,35 @@ pub struct RxConfig { pub sfd_sequence: SfdSequence, /// When true, a CRC will be expected to be appended to the message pub append_crc: bool, + /// When enabled, the radio itself will send acks to messages with the ack bit enabled. + pub auto_ack: AutoAck, +} + +impl RxConfig { + /// Create an [RxConfig] by copying the properties from a [TxConfig] and adding missing properties + pub const fn from_tx_config(tx_config: TxConfig, frame_filtering: bool, auto_ack: AutoAck) -> Self { + let TxConfig { + bitrate, + ranging_enable: _, + pulse_repetition_frequency, + preamble_length, + channel, + sfd_sequence, + append_crc, + continuation: _, + } = tx_config; + + RxConfig { + bitrate, + frame_filtering, + pulse_repetition_frequency, + expected_preamble_length: preamble_length, + channel, + sfd_sequence, + append_crc, + auto_ack, + } + } } impl Default for RxConfig { @@ -80,10 +134,32 @@ impl Default for RxConfig { channel: Default::default(), sfd_sequence: Default::default(), append_crc: true, + auto_ack: Default::default(), } } } +/// The auto acknowledge behavior +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)] +pub enum AutoAck { + #[default] + /// No automatic acks are sent + Disabled, + /// An automatic Ack will be sent if: + /// - Frame filtering is on + /// - The received frame passes through the filter + /// - The frame has the ack bit set + Enabled { + /// The turnaround time in number of symbols. + /// + /// Recommended minimal value: + /// - 110 kbps: 0 + /// - 850 kbps: 2 + /// - 6.8 Mbps: 3 + turnaround_time: u8, + }, +} + #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, TryFromPrimitive)] #[repr(u8)] /// The bitrate at which a message is transmitted diff --git a/dw1000/src/hl/awake.rs b/dw1000/src/hl/awake.rs index 2fa2f1b..4f36471 100644 --- a/dw1000/src/hl/awake.rs +++ b/dw1000/src/hl/awake.rs @@ -50,6 +50,12 @@ where Ok(Instant::new(sys_time).unwrap()) } + /// Set the value of the pending bit used by the auto acks + pub fn set_pending_bit(&mut self, value: bool) -> Result<(), Error> { + self.ll.sys_cfg().modify(|_, w| w.aackpend(value as u8))?; + Ok(()) + } + /// Provides direct access to the register-level API /// /// Be aware that by using the register-level API, you can invalidate diff --git a/dw1000/src/hl/error.rs b/dw1000/src/hl/error.rs index ccc1dbd..9ac05fd 100644 --- a/dw1000/src/hl/error.rs +++ b/dw1000/src/hl/error.rs @@ -80,6 +80,12 @@ where /// There are issues with frame filtering in double buffer mode. /// So it's not supported now. RxConfigFrameFilteringUnsupported, + + /// The wrong continuation was called on the radio + WrongTxContinuation, + + /// The transmission has not yet finished + TxNotFinishedyet, } impl From> for Error @@ -131,6 +137,12 @@ where Error::RxConfigFrameFilteringUnsupported => { write!(f, "RxConfigFrameFilteringUnsupported") } + Error::WrongTxContinuation => { + write!(f, "WrongTxContinuation") + } + Error::TxNotFinishedyet => { + write!(f, "TxNotFinishedyet") + } } } } diff --git a/dw1000/src/hl/ready.rs b/dw1000/src/hl/ready.rs index 726557b..b9b2014 100644 --- a/dw1000/src/hl/ready.rs +++ b/dw1000/src/hl/ready.rs @@ -1,7 +1,8 @@ -use super::AutoDoubleBufferReceiving; +use super::{AutoDoubleBufferReceiving, Receiving}; use crate::{ - configs::SfdSequence, time::Instant, Error, Ready, RxConfig, Sending, SingleBufferReceiving, - Sleeping, TxConfig, DW1000, + configs::{AutoAck, BitRate, SfdSequence, TxContinuation}, + time::Instant, + Error, Ready, RxConfig, Sending, SingleBufferReceiving, Sleeping, TxConfig, DW1000, }; use byte::BytesExt as _; use core::num::Wrapping; @@ -216,20 +217,44 @@ where send_time: SendTime, config: TxConfig, ) -> Result, Error> { - // Clear event counters - self.ll.evc_ctrl().write(|w| w.evc_clr(0b1))?; - while self.ll.evc_ctrl().read()?.evc_clr() == 0b1 {} - - // (Re-)Enable event counters - self.ll.evc_ctrl().write(|w| w.evc_en(0b1))?; - while self.ll.evc_ctrl().read()?.evc_en() == 0b1 {} - - // Sometimes, for unknown reasons, the DW1000 gets stuck in RX mode. - // Starting the transmitter won't get it to enter TX mode, which means - // all subsequent send operations will fail. Let's disable the - // transceiver and force the chip into IDLE mode to make sure that - // doesn't happen. - self.force_idle(false)?; + match config.continuation { + TxContinuation::Ready => { + // Clear event counters + self.ll.evc_ctrl().write(|w| w.evc_clr(0b1))?; + while self.ll.evc_ctrl().read()?.evc_clr() == 0b1 {} + + // (Re-)Enable event counters + self.ll.evc_ctrl().write(|w| w.evc_en(0b1))?; + while self.ll.evc_ctrl().read()?.evc_en() == 0b1 {} + + // Sometimes, for unknown reasons, the DW1000 gets stuck in RX mode. + // Starting the transmitter won't get it to enter TX mode, which means + // all subsequent send operations will fail. Let's disable the + // transceiver and force the chip into IDLE mode to make sure that + // doesn't happen. + self.force_idle(false)?; + } + TxContinuation::Rx { + frame_filtering, + auto_ack, + } => { + self.config_receiving::(RxConfig::from_tx_config( + config, + frame_filtering, + auto_ack, + ))?; + } + TxContinuation::RxDoubleBuffered { + frame_filtering, + auto_ack, + } => { + self.config_receiving::(RxConfig::from_tx_config( + config, + frame_filtering, + auto_ack, + ))?; + } + } match send_time { SendTime::Delayed(time) => { @@ -329,6 +354,8 @@ where // Todo: Power control (register 0x1E) + self.ll.ack_resp_t().modify(|_, w| w.w4r_tim(0))?; + self.ll.sys_ctrl().modify(|_, w| { // Do we want to suppress crc generation? let w = w.sfcst(!config.append_crc as u8); @@ -341,6 +368,7 @@ where w } .txstrt(0b1) + .wait4resp(!matches!(config.continuation, TxContinuation::Ready) as u8) } else { w } @@ -349,10 +377,184 @@ where Ok(DW1000 { ll: self.ll, seq: self.seq, - state: Sending { finished: false }, + state: Sending { + finished: false, + continuation: config.continuation, + config, + }, }) } + pub(super) fn config_receiving( + &mut self, + config: RxConfig, + ) -> Result<(), Error> { + // For unknown reasons, the DW1000 gets stuck in RX mode without ever + // receiving anything, after receiving one good frame. Reset the + // receiver to make sure its in a valid state before attempting to + // receive anything. + self.ll.pmsc_ctrl0().modify( + |_, w| w.softreset(0b1110), // reset receiver + )?; + self.ll.pmsc_ctrl0().modify( + |_, w| w.softreset(0b1111), // clear reset + )?; + + // We're already resetting the receiver in the previous step, and that's + // good enough to make my example program that's both sending and + // receiving work very reliably over many hours (that's not to say it + // becomes unreliable after those hours, that's just when my test + // stopped). However, I've seen problems with an example program that + // only received, never sent, data. That got itself into some weird + // state where it couldn't receive anymore. + // I suspect that's because that example didn't have the following line + // of code, while the send/receive example had that line of code, being + // called from `send`. + // While I haven't, as of this writing, run any hours-long tests to + // confirm this does indeed fix the receive-only example, it seems + // (based on my eyeball-only measurements) that the RX/TX example is + // dropping fewer frames now. + self.force_idle(false)?; + + self.ll.sys_cfg().modify(|_, w| { + w.ffen(config.frame_filtering as u8) // enable or disable frame filtering + .ffab(0b1) // receive beacon frames + .ffad(0b1) // receive data frames + .ffaa(0b1) // receive acknowledgement frames + .ffam(0b1) // receive MAC command frames + // Set the double buffering and auto re-enable + .dis_drxb(!RECEIVING::DOUBLE_BUFFERED as u8) + .rxautr(RECEIVING::AUTO_RX_REENABLE as u8) + // Set whether the receiver should look for 110kbps or 850/6800kbps messages + .rxm110k((config.bitrate == BitRate::Kbps110) as u8) + })?; + + // Set PLLLDT bit in EC_CTRL. According to the documentation of the + // CLKPLL_LL bit in SYS_STATUS, this bit needs to be set to ensure the + // reliable operation of the CLKPLL_LL bit. Since I've seen that bit + // being set, I want to make sure I'm not just seeing crap. + self.ll.ec_ctrl().modify(|_, w| w.pllldt(0b1))?; + + // Now that PLLLDT is set, clear all bits in SYS_STATUS that depend on + // it for reliable operation. After that is done, these bits should work + // reliably. + self.ll + .sys_status() + .write(|w| w.cplock(0b1).clkpll_ll(0b1))?; + + // Apply the config + self.ll.chan_ctrl().modify(|_, w| { + w.tx_chan(config.channel as u8) + .rx_chan(config.channel as u8) + .dwsfd( + (config.sfd_sequence == SfdSequence::Decawave + || config.sfd_sequence == SfdSequence::DecawaveAlt) + as u8, + ) + .rxprf(config.pulse_repetition_frequency as u8) + .tnssfd( + (config.sfd_sequence == SfdSequence::User + || config.sfd_sequence == SfdSequence::DecawaveAlt) + as u8, + ) + .rnssfd( + (config.sfd_sequence == SfdSequence::User + || config.sfd_sequence == SfdSequence::DecawaveAlt) + as u8, + ) + .tx_pcode( + config + .channel + .get_recommended_preamble_code(config.pulse_repetition_frequency), + ) + .rx_pcode( + config + .channel + .get_recommended_preamble_code(config.pulse_repetition_frequency), + ) + })?; + + match config.sfd_sequence { + SfdSequence::IEEE => {} // IEEE has predefined sfd lengths and the register has no effect. + SfdSequence::Decawave => self.ll.sfd_length().write(|w| w.value(8))?, // This isn't entirely necessary as the Decawave8 settings in chan_ctrl already force it to 8 + SfdSequence::DecawaveAlt => self.ll.sfd_length().write(|w| w.value(16))?, // Set to 16 + SfdSequence::User => {} // Users are responsible for setting the lengths themselves + } + + // Set general tuning + self.ll.drx_tune0b().write(|w| { + w.value( + config + .bitrate + .get_recommended_drx_tune0b(config.sfd_sequence), + ) + })?; + self.ll.drx_tune1a().write(|w| { + w.value( + config + .pulse_repetition_frequency + .get_recommended_drx_tune1a(), + ) + })?; + let drx_tune1b = config + .expected_preamble_length + .get_recommended_drx_tune1b(config.bitrate)?; + self.ll.drx_tune1b().write(|w| w.value(drx_tune1b))?; + let drx_tune2 = config + .pulse_repetition_frequency + .get_recommended_drx_tune2( + config.expected_preamble_length.get_recommended_pac_size(), + )?; + self.ll.drx_tune2().write(|w| w.value(drx_tune2))?; + self.ll + .drx_tune4h() + .write(|w| w.value(config.expected_preamble_length.get_recommended_dxr_tune4h()))?; + + // Set channel tuning + self.ll + .rf_rxctrlh() + .write(|w| w.value(config.channel.get_recommended_rf_rxctrlh()))?; + self.ll + .fs_pllcfg() + .write(|w| w.value(config.channel.get_recommended_fs_pllcfg()))?; + self.ll + .fs_plltune() + .write(|w| w.value(config.channel.get_recommended_fs_plltune()))?; + + // Set the LDE registers + self.ll + .lde_cfg2() + .write(|w| w.value(config.pulse_repetition_frequency.get_recommended_lde_cfg2()))?; + self.ll.lde_repc().write(|w| { + w.value( + config.channel.get_recommended_lde_repc_value( + config.pulse_repetition_frequency, + config.bitrate, + ), + ) + })?; + + // Check if the rx buffer pointer is correct + let status = self.ll.sys_status().read()?; + if status.hsrbp() != status.icrbp() { + // The RX Buffer Pointer of the host and the ic side don't point to the same one. + // We need to switch over + self.ll.sys_ctrl().modify(|_, w| w.hrbpt(1))?; + } + + if let AutoAck::Enabled { turnaround_time } = config.auto_ack { + self.ll.sys_cfg().modify(|_, w| w.autoack(0b1))?; + self.ll + .ack_resp_t() + .modify(|_, w| w.ack_tim(turnaround_time))?; + + // Reload the SFD sequence as described by 5.3.1.2 + self.ll.sys_ctrl().modify(|_, w| w.txstrt(1).trxoff(1))?; + } + + Ok(()) + } + /// Attempt to receive a single IEEE 802.15.4 MAC frame /// /// Initializes the receiver. The method consumes this instance of `DW1000` @@ -364,9 +566,11 @@ where /// that are transmitted. The default works with the TxConfig's default and /// is a sane starting point. pub fn receive( - self, + mut self, config: RxConfig, ) -> Result, Error> { + self.config_receiving::(config)?; + let mut rx_radio = DW1000 { ll: self.ll, seq: self.seq, @@ -377,7 +581,7 @@ where }; // Start rx'ing - rx_radio.start_receiving(config)?; + rx_radio.start_receiving()?; // Return the double buffer state Ok(rx_radio) @@ -398,9 +602,11 @@ where /// that are transmitted. The default works with the TxConfig's default and /// is a sane starting point. pub fn receive_auto_double_buffered( - self, + mut self, config: RxConfig, ) -> Result, Error> { + self.config_receiving::(config)?; + let mut rx_radio = DW1000 { ll: self.ll, seq: self.seq, @@ -411,7 +617,7 @@ where }; // Start rx'ing - rx_radio.start_receiving(config)?; + rx_radio.start_receiving()?; // Return the double buffer state Ok(rx_radio) diff --git a/dw1000/src/hl/receiving.rs b/dw1000/src/hl/receiving.rs index d71b779..41ac341 100644 --- a/dw1000/src/hl/receiving.rs +++ b/dw1000/src/hl/receiving.rs @@ -1,9 +1,4 @@ -use crate::{ - configs::{BitRate, SfdSequence}, - mac, - time::Instant, - Error, Ready, RxConfig, DW1000, -}; +use crate::{mac, time::Instant, Error, Ready, DW1000}; use byte::BytesExt as _; use core::convert::TryInto; use embedded_hal::spi::SpiDevice; @@ -61,168 +56,7 @@ where SPI: SpiDevice, RECEIVING: Receiving, { - pub(super) fn start_receiving(&mut self, config: RxConfig) -> Result<(), Error> { - // Really weird thing about double buffering I can't find anything about. - // When a message is received in double buffer mode that should be filtered out, - // the radio gives a really short fake interrupt. - // This messes up all the logic, so unless a solution can be found we simply don't support it. - if RECEIVING::DOUBLE_BUFFERED && config.frame_filtering { - return Err(Error::RxConfigFrameFilteringUnsupported); - } - - // For unknown reasons, the DW1000 gets stuck in RX mode without ever - // receiving anything, after receiving one good frame. Reset the - // receiver to make sure its in a valid state before attempting to - // receive anything. - self.ll.pmsc_ctrl0().modify( - |_, w| w.softreset(0b1110), // reset receiver - )?; - self.ll.pmsc_ctrl0().modify( - |_, w| w.softreset(0b1111), // clear reset - )?; - - // We're already resetting the receiver in the previous step, and that's - // good enough to make my example program that's both sending and - // receiving work very reliably over many hours (that's not to say it - // becomes unreliable after those hours, that's just when my test - // stopped). However, I've seen problems with an example program that - // only received, never sent, data. That got itself into some weird - // state where it couldn't receive anymore. - // I suspect that's because that example didn't have the following line - // of code, while the send/receive example had that line of code, being - // called from `send`. - // While I haven't, as of this writing, run any hours-long tests to - // confirm this does indeed fix the receive-only example, it seems - // (based on my eyeball-only measurements) that the RX/TX example is - // dropping fewer frames now. - self.force_idle(false)?; - - self.ll.sys_cfg().modify(|_, w| { - w.ffen(config.frame_filtering as u8) // enable or disable frame filtering - .ffab(0b1) // receive beacon frames - .ffad(0b1) // receive data frames - .ffaa(0b1) // receive acknowledgement frames - .ffam(0b1) // receive MAC command frames - // Set the double buffering and auto re-enable - .dis_drxb(!RECEIVING::DOUBLE_BUFFERED as u8) - .rxautr(RECEIVING::AUTO_RX_REENABLE as u8) - // Set whether the receiver should look for 110kbps or 850/6800kbps messages - .rxm110k((config.bitrate == BitRate::Kbps110) as u8) - })?; - - // Set PLLLDT bit in EC_CTRL. According to the documentation of the - // CLKPLL_LL bit in SYS_STATUS, this bit needs to be set to ensure the - // reliable operation of the CLKPLL_LL bit. Since I've seen that bit - // being set, I want to make sure I'm not just seeing crap. - self.ll.ec_ctrl().modify(|_, w| w.pllldt(0b1))?; - - // Now that PLLLDT is set, clear all bits in SYS_STATUS that depend on - // it for reliable operation. After that is done, these bits should work - // reliably. - self.ll - .sys_status() - .write(|w| w.cplock(0b1).clkpll_ll(0b1))?; - - // Apply the config - self.ll.chan_ctrl().modify(|_, w| { - w.tx_chan(config.channel as u8) - .rx_chan(config.channel as u8) - .dwsfd( - (config.sfd_sequence == SfdSequence::Decawave - || config.sfd_sequence == SfdSequence::DecawaveAlt) - as u8, - ) - .rxprf(config.pulse_repetition_frequency as u8) - .tnssfd( - (config.sfd_sequence == SfdSequence::User - || config.sfd_sequence == SfdSequence::DecawaveAlt) - as u8, - ) - .rnssfd( - (config.sfd_sequence == SfdSequence::User - || config.sfd_sequence == SfdSequence::DecawaveAlt) - as u8, - ) - .tx_pcode( - config - .channel - .get_recommended_preamble_code(config.pulse_repetition_frequency), - ) - .rx_pcode( - config - .channel - .get_recommended_preamble_code(config.pulse_repetition_frequency), - ) - })?; - - match config.sfd_sequence { - SfdSequence::IEEE => {} // IEEE has predefined sfd lengths and the register has no effect. - SfdSequence::Decawave => self.ll.sfd_length().write(|w| w.value(8))?, // This isn't entirely necessary as the Decawave8 settings in chan_ctrl already force it to 8 - SfdSequence::DecawaveAlt => self.ll.sfd_length().write(|w| w.value(16))?, // Set to 16 - SfdSequence::User => {} // Users are responsible for setting the lengths themselves - } - - // Set general tuning - self.ll.drx_tune0b().write(|w| { - w.value( - config - .bitrate - .get_recommended_drx_tune0b(config.sfd_sequence), - ) - })?; - self.ll.drx_tune1a().write(|w| { - w.value( - config - .pulse_repetition_frequency - .get_recommended_drx_tune1a(), - ) - })?; - let drx_tune1b = config - .expected_preamble_length - .get_recommended_drx_tune1b(config.bitrate)?; - self.ll.drx_tune1b().write(|w| w.value(drx_tune1b))?; - let drx_tune2 = config - .pulse_repetition_frequency - .get_recommended_drx_tune2( - config.expected_preamble_length.get_recommended_pac_size(), - )?; - self.ll.drx_tune2().write(|w| w.value(drx_tune2))?; - self.ll - .drx_tune4h() - .write(|w| w.value(config.expected_preamble_length.get_recommended_dxr_tune4h()))?; - - // Set channel tuning - self.ll - .rf_rxctrlh() - .write(|w| w.value(config.channel.get_recommended_rf_rxctrlh()))?; - self.ll - .fs_pllcfg() - .write(|w| w.value(config.channel.get_recommended_fs_pllcfg()))?; - self.ll - .fs_plltune() - .write(|w| w.value(config.channel.get_recommended_fs_plltune()))?; - - // Set the LDE registers - self.ll - .lde_cfg2() - .write(|w| w.value(config.pulse_repetition_frequency.get_recommended_lde_cfg2()))?; - self.ll.lde_repc().write(|w| { - w.value( - config.channel.get_recommended_lde_repc_value( - config.pulse_repetition_frequency, - config.bitrate, - ), - ) - })?; - - // Check if the rx buffer pointer is correct - let status = self.ll.sys_status().read()?; - if status.hsrbp() != status.icrbp() { - // The RX Buffer Pointer of the host and the ic side don't point to the same one. - // We need to switch over - self.ll.sys_ctrl().modify(|_, w| w.hrbpt(1))?; - } - + pub(super) fn start_receiving(&mut self) -> Result<(), Error> { // Start receiving self.ll.sys_ctrl().modify(|_, w| w.rxenab(0b1))?; @@ -319,6 +153,24 @@ where // - LDEERR: Leading Edge Detection Processing Error // - RXPREJ: Receiver Preamble Rejection + if sys_status.txfrb() == 1 + || sys_status.txprs() == 1 + || sys_status.txphs() == 1 + || sys_status.txfrs() == 1 + { + // Old tx flags are still active. We don't need them anymore. + // This can happen from a tx continuation. + self.ll + .sys_status() + .write(|w| { + w.txfrb(0b1) // Transmit Frame Begins + .txprs(0b1) // Transmit Preamble Sent + .txphs(0b1) // Transmit PHY Header Sent + .txfrs(0b1) // Transmit Frame Sent + }) + .map_err(|error| nb::Error::Other(Error::Spi(error.0)))?; + } + // No errors detected. That must mean the frame is just not ready // yet. return Err(nb::Error::WouldBlock); diff --git a/dw1000/src/hl/sending.rs b/dw1000/src/hl/sending.rs index 7566190..5203e8d 100644 --- a/dw1000/src/hl/sending.rs +++ b/dw1000/src/hl/sending.rs @@ -1,7 +1,9 @@ -use crate::{time::Instant, Error, Ready, Sending, DW1000}; +use crate::{configs::TxContinuation, time::Instant, Error, Ready, RxConfig, Sending, DW1000}; use embedded_hal::spi::SpiDevice; use nb; +use super::{AutoDoubleBufferReceiving, SingleBufferReceiving}; + impl DW1000 where SPI: SpiDevice, @@ -77,12 +79,27 @@ where Ok(tx_timestamp) } - /// Finishes sending and returns to the `Ready` state + /// Finishes sending and returns to the `Ready` state. + /// + /// If the used tx continuation was not set to ready, this function returns an error. /// /// If the send operation has finished, as indicated by `wait`, this is a /// no-op. If the send operation is still ongoing, it will be aborted. #[allow(clippy::type_complexity)] - pub fn finish_sending(mut self) -> Result, (Self, Error)> { + pub fn finish_sending(self) -> Result, (Self, Error)> { + if self.state.continuation != TxContinuation::Ready { + return Err((self, Error::WrongTxContinuation)); + } + + self.abort_sending() + } + + /// Finishes sending and returns to the `Ready` state (no matter the tx continuation that was configured) + /// + /// If the send operation has finished, as indicated by `wait`, this is a + /// no-op. If the send operation is still ongoing, it will be aborted. + #[allow(clippy::type_complexity)] + pub fn abort_sending(mut self) -> Result, (Self, Error)> { if !self.state.finished { // Can't use `map_err` and `?` here, as the compiler will complain // about `self` moving into the closure. @@ -109,15 +126,106 @@ where }) } + /// Continue on in the receiving state. + /// + /// This can only be called when the tx config specified this should be the continuation. + /// This function will not abort the send operation, so make sure to call [Self::wait_transmit] first. + pub fn continue_receiving( + mut self, + ) -> Result, (Self, Error)> { + let TxContinuation::Rx { + frame_filtering, + auto_ack, + } = self.state.continuation + else { + return Err((self, Error::WrongTxContinuation)); + }; + + if !self.state.finished { + return Err((self, Error::TxNotFinishedyet)); + } + + match self.reset_flags() { + Ok(()) => (), + Err(error) => return Err((self, error)), + } + + // Turn off the external transmit synchronization + match self.ll.ec_ctrl().modify(|_, w| w.ostsm(0)) { + Ok(_) => {} + Err(e) => return Err((self, Error::Spi(e.0))), + } + + Ok(DW1000 { + ll: self.ll, + seq: self.seq, + state: SingleBufferReceiving { + finished: false, + config: RxConfig::from_tx_config(self.state.config, frame_filtering, auto_ack), + }, + }) + } + + /// Continue on in the double buffered receiving state. + /// + /// This can only be called when the tx config specified this should be the continuation. + /// This function will not abort the send operation, so make sure to call [Self::wait_transmit] first. + pub fn continue_receiving_double_buffered( + mut self, + ) -> Result, (Self, Error)> { + let TxContinuation::RxDoubleBuffered { + frame_filtering, + auto_ack, + } = self.state.continuation + else { + return Err((self, Error::WrongTxContinuation)); + }; + + if !self.state.finished { + return Err((self, Error::TxNotFinishedyet)); + } + + let status = match self.ll.sys_status().read() { + Ok(status) => status, + Err(e) => return Err((self, Error::Spi(e.0))), + }; + if status.hsrbp() != status.icrbp() { + // The RX Buffer Pointer of the host and the ic side don't point to the same one. + // We need to switch over + match self.ll.sys_ctrl().modify(|_, w| w.hrbpt(1)) { + Ok(()) => {} + Err(e) => return Err((self, Error::Spi(e.0))), + }; + } + + match self.reset_flags() { + Ok(()) => (), + Err(error) => return Err((self, error)), + } + + // Turn off the external transmit synchronization + match self.ll.ec_ctrl().modify(|_, w| w.ostsm(0)) { + Ok(_) => {} + Err(e) => return Err((self, Error::Spi(e.0))), + } + + Ok(DW1000 { + ll: self.ll, + seq: self.seq, + state: AutoDoubleBufferReceiving { + finished: false, + config: RxConfig::from_tx_config(self.state.config, frame_filtering, auto_ack), + }, + }) + } + fn reset_flags(&mut self) -> Result<(), Error> { - self.ll.sys_status().write( - |w| { - w.txfrb(0b1) // Transmit Frame Begins - .txprs(0b1) // Transmit Preamble Sent - .txphs(0b1) // Transmit PHY Header Sent - .txfrs(0b1) - }, // Transmit Frame Sent - )?; + self.ll.sys_status().write(|w| { + w.txfrb(0b1) // Transmit Frame Begins + .txprs(0b1) // Transmit Preamble Sent + .txphs(0b1) // Transmit PHY Header Sent + .txfrs(0b1) // Transmit Frame Sent + })?; Ok(()) } diff --git a/dw1000/src/hl/state_impls.rs b/dw1000/src/hl/state_impls.rs index b4f6f77..8c38bcb 100644 --- a/dw1000/src/hl/state_impls.rs +++ b/dw1000/src/hl/state_impls.rs @@ -1,4 +1,4 @@ -use crate::{time::Duration, RxConfig}; +use crate::{configs::TxContinuation, time::Duration, RxConfig, TxConfig}; /// Indicates that the `DW1000` instance is not initialized yet #[derive(Debug)] @@ -12,6 +12,8 @@ pub struct Ready; #[derive(Debug)] pub struct Sending { pub(super) finished: bool, + pub(super) continuation: TxContinuation, + pub(super) config: TxConfig, } /// Indicates that the `DW1000` instance is currently receiving in single buffer mode (default) diff --git a/dw1000/src/ll.rs b/dw1000/src/ll.rs index 10dcbd3..dae17fa 100644 --- a/dw1000/src/ll.rs +++ b/dw1000/src/ll.rs @@ -777,6 +777,10 @@ impl_register! { rx_state, 8, 12, u8; /// Current Receive State Machine value pmsc_state, 16, 23, u8; /// Current PMSC State Machine value } + 0x1A, 0x00, 4, RW, ACK_RESP_T(ack_resp_t) { /// Acknowledgement time and response time + w4r_tim, 0, 19, u32; /// Wait-for-Response turn-around Time + ack_tim, 24, 31, u8; /// Auto-Acknowledgement turn-around Time + } 0x1E, 0x00, 4, RW, TX_POWER(tx_power) { /// TX Power Control // The TX_POWER register has multiple sets of fields defined, depending // on the smart TX power control setting. I don't know how to model