diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 0ca18d88..b5e9afbc 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -196,8 +196,9 @@ impl<'d, T: Instance> FullDuplex for Spim<'d, T> { fn read_write<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::WriteReadFuture<'a> { async move { - slice_in_ram_or(rx, Error::DMABufferNotInDataMemory)?; slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; + // NOTE: RAM slice check for rx is not necessary, as a mutable + // slice can only be built from data located in RAM. // Conservative compiler fence to prevent optimizations that do not // take in to account actions by DMA. The fence has been placed here, diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index ea3ac755..160868d7 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -6,11 +6,16 @@ //! //! - nRF52832: Section 33 //! - nRF52840: Section 6.31 +use core::future::Future; use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering::SeqCst}; +use core::task::Poll; use embassy::interrupt::{Interrupt, InterruptExt}; +use embassy::traits; use embassy::util::{AtomicWaker, Unborrow}; use embassy_extras::unborrow; +use futures::future::poll_fn; +use traits::i2c::I2c; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; @@ -418,6 +423,26 @@ impl<'d, T: Instance> Twim<'d, T> { self.write_then_read(address, wr_ram_buffer, rd_buffer) } + + fn wait_for_stopped_event(cx: &mut core::task::Context) -> Poll<()> { + let r = T::regs(); + let s = T::state(); + + s.end_waker.register(cx.waker()); + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + + return Poll::Ready(()); + } + + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + } + + Poll::Pending + } } impl<'a, T: Instance> Drop for Twim<'a, T> { @@ -437,6 +462,191 @@ impl<'a, T: Instance> Drop for Twim<'a, T> { } } +impl<'d, T> I2c for Twim<'d, T> +where + T: Instance, +{ + type Error = Error; + + #[rustfmt::skip] + type WriteFuture<'a> where Self: 'a = impl Future> + 'a; + #[rustfmt::skip] + type ReadFuture<'a> where Self: 'a = impl Future> + 'a; + #[rustfmt::skip] + type WriteReadFuture<'a> where Self: 'a = impl Future> + 'a; + + fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + async move { + // NOTE: RAM slice check for buffer is not necessary, as a mutable + // slice can only be built from data located in RAM. + + let r = T::regs(); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started. + compiler_fence(SeqCst); + + r.address.write(|w| unsafe { w.address().bits(address) }); + + // Set up the DMA read. + unsafe { self.set_rx_buffer(buffer)? }; + + // Reset events + r.events_stopped.reset(); + r.events_error.reset(); + self.clear_errorsrc(); + + // Enable events + r.intenset.write(|w| w.stopped().set().error().set()); + + // Start read operation. + r.shorts.write(|w| w.lastrx_stop().enabled()); + r.tasks_startrx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed. + compiler_fence(SeqCst); + + // Wait for 'stopped' event. + poll_fn(Self::wait_for_stopped_event).await; + + self.read_errorsrc()?; + + if r.rxd.amount.read().bits() != buffer.len() as u32 { + return Err(Error::Receive); + } + + Ok(()) + } + } + + fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { + async move { + slice_in_ram_or(bytes, Error::DMABufferNotInDataMemory)?; + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started. + compiler_fence(SeqCst); + + let r = T::regs(); + + // Set up current address we're trying to talk to + r.address.write(|w| unsafe { w.address().bits(address) }); + + // Set up DMA write. + unsafe { + self.set_tx_buffer(bytes)?; + } + + // Reset events + r.events_stopped.reset(); + r.events_error.reset(); + r.events_lasttx.reset(); + self.clear_errorsrc(); + + // Enable events + r.intenset.write(|w| w.stopped().set().error().set()); + + // Start write operation. + r.shorts.write(|w| w.lasttx_stop().enabled()); + r.tasks_starttx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed. + compiler_fence(SeqCst); + + // Wait for 'stopped' event. + poll_fn(Self::wait_for_stopped_event).await; + + self.read_errorsrc()?; + + if r.txd.amount.read().bits() != bytes.len() as u32 { + return Err(Error::Transmit); + } + + Ok(()) + } + } + + fn write_read<'a>( + &'a mut self, + address: u8, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a> { + async move { + slice_in_ram_or(bytes, Error::DMABufferNotInDataMemory)?; + // NOTE: RAM slice check for buffer is not necessary, as a mutable + // slice can only be built from data located in RAM. + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started. + compiler_fence(SeqCst); + + let r = T::regs(); + + // Set up current address we're trying to talk to + r.address.write(|w| unsafe { w.address().bits(address) }); + + // Set up DMA buffers. + unsafe { + self.set_tx_buffer(bytes)?; + self.set_rx_buffer(buffer)?; + } + + // Reset events + r.events_stopped.reset(); + r.events_error.reset(); + r.events_lasttx.reset(); + self.clear_errorsrc(); + + // Enable events + r.intenset.write(|w| w.stopped().set().error().set()); + + // Start write+read operation. + r.shorts.write(|w| { + w.lasttx_startrx().enabled(); + w.lastrx_stop().enabled(); + w + }); + // `1` is a valid value to write to task registers. + r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // after all possible DMA actions have completed. + compiler_fence(SeqCst); + + // Wait for 'stopped' event. + poll_fn(Self::wait_for_stopped_event).await; + + self.read_errorsrc()?; + + let bad_write = r.txd.amount.read().bits() != bytes.len() as u32; + let bad_read = r.rxd.amount.read().bits() != buffer.len() as u32; + + if bad_write { + return Err(Error::Transmit); + } + + if bad_read { + return Err(Error::Receive); + } + + Ok(()) + } + } +} + impl<'a, T: Instance> embedded_hal::blocking::i2c::Write for Twim<'a, T> { type Error = Error; diff --git a/embassy-traits/src/i2c.rs b/embassy-traits/src/i2c.rs index abe932d9..e426a00b 100644 --- a/embassy-traits/src/i2c.rs +++ b/embassy-traits/src/i2c.rs @@ -94,9 +94,15 @@ pub trait I2c { /// Error type type Error; - type ReadFuture<'a>: Future> + 'a; - type WriteFuture<'a>: Future> + 'a; - type WriteReadFuture<'a>: Future> + 'a; + type WriteFuture<'a>: Future> + 'a + where + Self: 'a; + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + type WriteReadFuture<'a>: Future> + 'a + where + Self: 'a; /// Reads enough bytes from slave with `address` to fill `buffer` /// @@ -116,7 +122,7 @@ pub trait I2c { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn read<'a>(&'a mut self, address: A, buffer: &mut [u8]) -> Self::ReadFuture<'a>; + fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a>; /// Sends bytes to slave with address `address` /// @@ -134,7 +140,7 @@ pub trait I2c { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - fn write<'a>(&'a mut self, address: A, bytes: &[u8]) -> Self::WriteFuture<'a>; + fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Self::WriteFuture<'a>; /// Sends bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a /// single transaction* @@ -161,7 +167,7 @@ pub trait I2c { fn write_read<'a>( &'a mut self, address: A, - bytes: &[u8], - buffer: &mut [u8], + bytes: &'a [u8], + buffer: &'a mut [u8], ) -> Self::WriteReadFuture<'a>; }