From 857ac3386bf13af1eeb9bf28b45b47b5169310e5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 4 Jun 2021 17:31:35 +0200 Subject: [PATCH] nrf async twim --- embassy-nrf/src/twim.rs | 113 ++++++++++++++++++++++++++++++++++++++ embassy-traits/src/i2c.rs | 20 ++++--- 2 files changed, 126 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index ea3ac7553..6a7149044 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; @@ -437,6 +442,114 @@ 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> { + self.write_read(address, &[], buffer) + } + + fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { + self.write_read(address, bytes, &mut []) + } + + 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(); + let s = T::state(); + + // 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 and enable the events + r.events_stopped.reset(); + r.events_error.reset(); + r.events_lasttx.reset(); + self.clear_errorsrc(); + + r.intenset.write(|w| w.stopped().set()); + r.intenset.write(|w| w.error().set()); + r.intenset.write(|w| w.lasttx().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(|cx| { + 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 + }) + .await; + + 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::TxBufferTooLong); + } + + if bad_read { + return Err(Error::RxBufferTooLong); + } + + 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 abe932d9c..e426a00b0 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>; }