Add support for transactions to Twim in embassy-nrf

This commit is contained in:
Alex Moon 2024-08-14 17:07:57 -04:00
parent 8803128707
commit 528a3e4355
No known key found for this signature in database
GPG Key ID: A6D388B98A7DB071

View File

@ -4,6 +4,7 @@
use core::future::{poll_fn, Future}; use core::future::{poll_fn, Future};
use core::marker::PhantomData; use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::sync::atomic::compiler_fence; use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst; use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll; use core::task::Poll;
@ -13,11 +14,12 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")] #[cfg(feature = "time")]
use embassy_time::{Duration, Instant}; use embassy_time::{Duration, Instant};
use embedded_hal_1::i2c::Operation;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin; use crate::gpio::Pin as GpioPin;
use crate::interrupt::typelevel::Interrupt; use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::util::slice_in_ram;
use crate::{gpio, interrupt, pac, Peripheral}; use crate::{gpio, interrupt, pac, Peripheral};
/// TWI frequency /// TWI frequency
@ -103,6 +105,18 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
let r = T::regs(); let r = T::regs();
let s = T::state(); let s = T::state();
// Workaround for lack of LASTRX_SUSPEND short in some nRF chips
// Do this first to minimize latency
#[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))]
if r.events_lastrx.read().bits() != 0 {
r.tasks_suspend.write(|w| unsafe { w.bits(1) });
r.events_lastrx.reset();
}
if r.events_suspended.read().bits() != 0 {
s.end_waker.wake();
r.intenclr.write(|w| w.suspended().clear());
}
if r.events_stopped.read().bits() != 0 { if r.events_stopped.read().bits() != 0 {
s.end_waker.wake(); s.end_waker.wake();
r.intenclr.write(|w| w.stopped().clear()); r.intenclr.write(|w| w.stopped().clear());
@ -182,8 +196,22 @@ impl<'d, T: Instance> Twim<'d, T> {
} }
/// Set TX buffer, checking that it is in RAM and has suitable length. /// Set TX buffer, checking that it is in RAM and has suitable length.
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { unsafe fn set_tx_buffer(
slice_in_ram_or(buffer, Error::BufferNotInRAM)?; &mut self,
buffer: &[u8],
ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
) -> Result<(), Error> {
let buffer = if slice_in_ram(buffer) {
buffer
} else {
let ram_buffer = ram_buffer.ok_or(Error::BufferNotInRAM)?;
trace!("Copying TWIM tx buffer into RAM for DMA");
let ram_buffer = &mut ram_buffer[..buffer.len()];
// Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer)
let uninit_src: &[MaybeUninit<u8>] = unsafe { core::mem::transmute(buffer) };
ram_buffer.copy_from_slice(uninit_src);
unsafe { &*(ram_buffer as *const [MaybeUninit<u8>] as *const [u8]) }
};
if buffer.len() > EASY_DMA_SIZE { if buffer.len() > EASY_DMA_SIZE {
return Err(Error::TxBufferTooLong); return Err(Error::TxBufferTooLong);
@ -290,7 +318,7 @@ impl<'d, T: Instance> Twim<'d, T> {
fn blocking_wait(&mut self) { fn blocking_wait(&mut self) {
let r = T::regs(); let r = T::regs();
loop { loop {
if r.events_stopped.read().bits() != 0 { if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 {
r.events_stopped.reset(); r.events_stopped.reset();
break; break;
} }
@ -307,7 +335,7 @@ impl<'d, T: Instance> Twim<'d, T> {
let r = T::regs(); let r = T::regs();
let deadline = Instant::now() + timeout; let deadline = Instant::now() + timeout;
loop { loop {
if r.events_stopped.read().bits() != 0 { if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 {
r.events_stopped.reset(); r.events_stopped.reset();
break; break;
} }
@ -331,7 +359,7 @@ impl<'d, T: Instance> Twim<'d, T> {
let s = T::state(); let s = T::state();
s.end_waker.register(cx.waker()); s.end_waker.register(cx.waker());
if r.events_stopped.read().bits() != 0 { if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 {
r.events_stopped.reset(); r.events_stopped.reset();
return Poll::Ready(()); return Poll::Ready(());
@ -347,75 +375,13 @@ impl<'d, T: Instance> Twim<'d, T> {
}) })
} }
fn setup_write_from_ram(&mut self, address: u8, buffer: &[u8], inten: bool) -> Result<(), Error> { fn setup_operations(
let r = T::regs();
compiler_fence(SeqCst);
r.address.write(|w| unsafe { w.address().bits(address) });
// Set up the DMA write.
unsafe { self.set_tx_buffer(buffer)? };
// Clear events
r.events_stopped.reset();
r.events_error.reset();
r.events_lasttx.reset();
self.clear_errorsrc();
if inten {
r.intenset.write(|w| w.stopped().set().error().set());
} else {
r.intenclr.write(|w| w.stopped().clear().error().clear());
}
// Start write operation.
r.shorts.write(|w| w.lasttx_stop().enabled());
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
if buffer.is_empty() {
// With a zero-length buffer, LASTTX doesn't fire (because there's no last byte!), so do the STOP ourselves.
r.tasks_stop.write(|w| unsafe { w.bits(1) });
}
Ok(())
}
fn setup_read(&mut self, address: u8, buffer: &mut [u8], inten: bool) -> Result<(), Error> {
let r = T::regs();
compiler_fence(SeqCst);
r.address.write(|w| unsafe { w.address().bits(address) });
// Set up the DMA read.
unsafe { self.set_rx_buffer(buffer)? };
// Clear events
r.events_stopped.reset();
r.events_error.reset();
self.clear_errorsrc();
if inten {
r.intenset.write(|w| w.stopped().set().error().set());
} else {
r.intenclr.write(|w| w.stopped().clear().error().clear());
}
// Start read operation.
r.shorts.write(|w| w.lastrx_stop().enabled());
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
if buffer.is_empty() {
// With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves.
r.tasks_stop.write(|w| unsafe { w.bits(1) });
}
Ok(())
}
fn setup_write_read_from_ram(
&mut self, &mut self,
address: u8, address: u8,
wr_buffer: &[u8], operations: &mut [Operation<'_>],
rd_buffer: &mut [u8], tx_ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
inten: bool, inten: bool,
stop: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
let r = T::regs(); let r = T::regs();
@ -423,92 +389,343 @@ impl<'d, T: Instance> Twim<'d, T> {
r.address.write(|w| unsafe { w.address().bits(address) }); r.address.write(|w| unsafe { w.address().bits(address) });
// Set up DMA buffers. let was_suspended = r.events_suspended.read().bits() != 0;
unsafe { r.events_suspended.reset();
self.set_tx_buffer(wr_buffer)?;
self.set_rx_buffer(rd_buffer)?;
}
// Clear events
r.events_stopped.reset(); r.events_stopped.reset();
r.events_error.reset(); r.events_error.reset();
self.clear_errorsrc(); self.clear_errorsrc();
if inten { if inten {
r.intenset.write(|w| w.stopped().set().error().set()); r.intenset.write(|w| w.suspended().set().stopped().set().error().set());
} else { } else {
r.intenclr.write(|w| w.stopped().clear().error().clear()); r.intenclr
.write(|w| w.suspended().clear().stopped().clear().error().clear());
}
#[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))]
r.intenclr.write(|w| w.lastrx().clear());
match operations {
[Operation::Read(rd_buffer), Operation::Write(wr_buffer), rest @ ..]
if !rd_buffer.is_empty() && !wr_buffer.is_empty() =>
{
let stop = stop && rest.is_empty();
// Set up DMA buffers.
unsafe {
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?;
self.set_rx_buffer(rd_buffer)?;
}
r.shorts.write(|w| {
w.lastrx_starttx().enabled();
if stop {
w.lasttx_stop().enabled();
} else {
w.lasttx_suspend().enabled();
}
w
});
// Start read+write operation.
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
if was_suspended {
r.tasks_resume.write(|w| unsafe { w.bits(1) });
}
}
[Operation::Write(wr_buffer), Operation::Read(rd_buffer), rest @ ..]
if !wr_buffer.is_empty() && !rd_buffer.is_empty() =>
{
let stop = stop && rest.is_empty();
// Set up DMA buffers.
unsafe {
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?;
self.set_rx_buffer(rd_buffer)?;
} }
// Start write+read operation. // Start write+read operation.
r.shorts.write(|w| { r.shorts.write(|w| {
w.lasttx_startrx().enabled(); w.lasttx_startrx().enabled();
if stop {
w.lastrx_stop().enabled(); w.lastrx_stop().enabled();
} else {
#[cfg(not(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120")))]
w.lastrx_suspend().enabled();
}
w w
}); });
#[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))]
if !stop {
r.intenset.write(|w| w.lastrx().set());
}
r.tasks_starttx.write(|w| unsafe { w.bits(1) }); r.tasks_starttx.write(|w| unsafe { w.bits(1) });
if wr_buffer.is_empty() && rd_buffer.is_empty() {
// With a zero-length buffer, LASTRX/LASTTX doesn't fire (because there's no last byte!), so do the STOP ourselves. if was_suspended {
// TODO handle when only one of the buffers is zero length r.tasks_resume.write(|w| unsafe { w.bits(1) });
}
}
[Operation::Read(buffer), rest @ ..] => {
let stop = stop && rest.is_empty();
// Set up DMA buffers.
unsafe {
self.set_rx_buffer(buffer)?;
}
// Start read operation.
r.shorts.write(|w| {
if stop {
w.lastrx_stop().enabled();
} else {
#[cfg(not(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120")))]
w.lastrx_suspend().enabled();
}
w
});
#[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))]
if !stop {
r.intenset.write(|w| w.lastrx().set());
}
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
if was_suspended {
r.tasks_resume.write(|w| unsafe { w.bits(1) });
}
if buffer.is_empty() {
// With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP/SUSPEND ourselves.
if stop {
r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.tasks_stop.write(|w| unsafe { w.bits(1) });
} else {
r.tasks_suspend.write(|w| unsafe { w.bits(1) });
}
}
}
[Operation::Write(buffer), rest @ ..] => {
let stop = stop && rest.is_empty();
// Set up DMA buffers.
unsafe {
self.set_tx_buffer(buffer, tx_ram_buffer)?;
}
// Start write operation.
r.shorts.write(|w| {
if stop {
w.lasttx_stop().enabled();
} else {
w.lasttx_suspend().enabled();
}
w
});
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
if was_suspended {
r.tasks_resume.write(|w| unsafe { w.bits(1) });
}
if buffer.is_empty() {
// With a zero-length buffer, LASTTX doesn't fire (because there's no last byte!), so do the STOP/SUSPEND ourselves.
if stop {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
} else {
r.tasks_suspend.write(|w| unsafe { w.bits(1) });
}
}
}
[] => {
if stop {
if was_suspended {
r.tasks_resume.write(|w| unsafe { w.bits(1) });
}
r.tasks_stop.write(|w| unsafe { w.bits(1) });
}
}
} }
Ok(()) Ok(())
} }
fn setup_write_read( fn check_operations(&mut self, operations: &mut [Operation<'_>]) -> Result<usize, Error> {
&mut self, compiler_fence(SeqCst);
address: u8, self.check_errorsrc()?;
wr_buffer: &[u8],
rd_buffer: &mut [u8], match operations {
inten: bool, [Operation::Read(rd_buffer), Operation::Write(wr_buffer), ..]
) -> Result<(), Error> { | [Operation::Write(wr_buffer), Operation::Read(rd_buffer), ..]
match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) { if !rd_buffer.is_empty() && !wr_buffer.is_empty() =>
Ok(_) => Ok(()), {
Err(Error::BufferNotInRAM) => { self.check_tx(wr_buffer.len())?;
trace!("Copying TWIM tx buffer into RAM for DMA"); self.check_rx(rd_buffer.len())?;
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; Ok(2)
tx_ram_buf.copy_from_slice(wr_buffer);
self.setup_write_read_from_ram(address, tx_ram_buf, rd_buffer, inten)
} }
Err(error) => Err(error), [Operation::Read(buffer), ..] => {
self.check_rx(buffer.len())?;
Ok(1)
}
[Operation::Write(buffer), ..] => {
self.check_tx(buffer.len())?;
Ok(1)
}
[] => Ok(0),
} }
} }
fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { // ===========================================
match self.setup_write_from_ram(address, wr_buffer, inten) {
Ok(_) => Ok(()), /// Execute the provided operations on the I2C bus.
Err(Error::BufferNotInRAM) => { ///
trace!("Copying TWIM tx buffer into RAM for DMA"); /// Each buffer must have a length of at most 255 bytes on the nRF52832
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; /// and at most 65535 bytes on the nRF52840.
tx_ram_buf.copy_from_slice(wr_buffer); ///
self.setup_write_from_ram(address, tx_ram_buf, inten) /// If `stop` is set, the transaction will be terminated with a STOP
/// condition and the Twim will be stopped. Otherwise, the bus will be
/// left busy via clock stretching and Twim will be suspended.
///
/// The nrf52832, nrf5340, and nrf9120 do not have hardware support for
/// suspending following a read operation therefore it is emulated by the
/// interrupt handler. If the latency of servicing that interrupt is
/// longer than a byte worth of clocks on the bus, the SCL clock will
/// continue to run for one or more additional bytes. This applies to
/// consecutive read operations, certain write-read-write sequences, or
/// any sequence of operations ending in a read when `stop == false`.
pub fn blocking_transaction(
&mut self,
address: u8,
mut operations: &mut [Operation<'_>],
stop: bool,
) -> Result<(), Error> {
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
while !operations.is_empty() {
self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false, stop)?;
self.blocking_wait();
let consumed = self.check_operations(operations)?;
operations = &mut operations[consumed..];
} }
Err(error) => Err(error), Ok(())
} }
/// Same as [`blocking_transaction`](Twim::blocking_transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub fn blocking_transaction_from_ram(
&mut self,
address: u8,
mut operations: &mut [Operation<'_>],
stop: bool,
) -> Result<(), Error> {
while !operations.is_empty() {
self.setup_operations(address, operations, None, false, stop)?;
self.blocking_wait();
let consumed = self.check_operations(operations)?;
operations = &mut operations[consumed..];
} }
Ok(())
}
/// Execute the provided operations on the I2C bus with timeout.
///
/// See [`blocking_transaction`].
#[cfg(feature = "time")]
pub fn blocking_transaction_timeout(
&mut self,
address: u8,
mut operations: &mut [Operation<'_>],
stop: bool,
timeout: Duration,
) -> Result<(), Error> {
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
while !operations.is_empty() {
self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false, stop)?;
self.blocking_wait_timeout(timeout)?;
let consumed = self.check_operations(operations)?;
operations = &mut operations[consumed..];
}
Ok(())
}
/// Same as [`blocking_transaction_timeout`](Twim::blocking_transaction_timeout) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
#[cfg(feature = "time")]
pub fn blocking_transaction_from_ram_timeout(
&mut self,
address: u8,
mut operations: &mut [Operation<'_>],
stop: bool,
timeout: Duration,
) -> Result<(), Error> {
while !operations.is_empty() {
self.setup_operations(address, operations, None, false, stop)?;
self.blocking_wait_timeout(timeout)?;
let consumed = self.check_operations(operations)?;
operations = &mut operations[consumed..];
}
Ok(())
}
/// Execute the provided operations on the I2C bus.
///
/// Each buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
///
/// If `stop` is set, the transaction will be terminated with a STOP
/// condition and the Twim will be stopped. Otherwise, the bus will be
/// left busy via clock stretching and Twim will be suspended.
///
/// The nrf52832, nrf5340, and nrf9120 do not have hardware support for
/// suspending following a read operation therefore it is emulated by the
/// interrupt handler. If the latency of servicing that interrupt is
/// longer than a byte worth of clocks on the bus, the SCL clock will
/// continue to run for one or more additional bytes. This applies to
/// consecutive read operations, certain write-read-write sequences, or
/// any sequence of operations ending in a read when `stop == false`.
pub async fn transaction(
&mut self,
address: u8,
mut operations: &mut [Operation<'_>],
stop: bool,
) -> Result<(), Error> {
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
while !operations.is_empty() {
self.setup_operations(address, operations, Some(&mut tx_ram_buffer), true, stop)?;
self.async_wait().await;
let consumed = self.check_operations(operations)?;
operations = &mut operations[consumed..];
}
Ok(())
}
/// Same as [`transaction`](Twim::transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub async fn transaction_from_ram(
&mut self,
address: u8,
mut operations: &mut [Operation<'_>],
stop: bool,
) -> Result<(), Error> {
while !operations.is_empty() {
self.setup_operations(address, operations, None, true, stop)?;
self.async_wait().await;
let consumed = self.check_operations(operations)?;
operations = &mut operations[consumed..];
}
Ok(())
}
// ===========================================
/// Write to an I2C slave. /// Write to an I2C slave.
/// ///
/// The buffer must have a length of at most 255 bytes on the nRF52832 /// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840. /// and at most 65535 bytes on the nRF52840.
pub fn blocking_write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { pub fn blocking_write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
self.setup_write(address, buffer, false)?; self.blocking_transaction(address, &mut [Operation::Write(buffer)], true)
self.blocking_wait();
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_tx(buffer.len())?;
Ok(())
} }
/// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
self.setup_write_from_ram(address, buffer, false)?; self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)], true)
self.blocking_wait();
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_tx(buffer.len())?;
Ok(())
} }
/// Read from an I2C slave. /// Read from an I2C slave.
@ -516,12 +733,7 @@ impl<'d, T: Instance> Twim<'d, T> {
/// The buffer must have a length of at most 255 bytes on the nRF52832 /// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840. /// and at most 65535 bytes on the nRF52840.
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.setup_read(address, buffer, false)?; self.blocking_transaction(address, &mut [Operation::Read(buffer)], true)
self.blocking_wait();
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_rx(buffer.len())?;
Ok(())
} }
/// Write data to an I2C slave, then read data from the slave without /// Write data to an I2C slave, then read data from the slave without
@ -530,13 +742,11 @@ impl<'d, T: Instance> Twim<'d, T> {
/// The buffers must have a length of at most 255 bytes on the nRF52832 /// The buffers must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840. /// and at most 65535 bytes on the nRF52840.
pub fn blocking_write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { pub fn blocking_write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> {
self.setup_write_read(address, wr_buffer, rd_buffer, false)?; self.blocking_transaction(
self.blocking_wait(); address,
compiler_fence(SeqCst); &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
self.check_errorsrc()?; true,
self.check_tx(wr_buffer.len())?; )
self.check_rx(rd_buffer.len())?;
Ok(())
} }
/// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
@ -546,13 +756,11 @@ impl<'d, T: Instance> Twim<'d, T> {
wr_buffer: &[u8], wr_buffer: &[u8],
rd_buffer: &mut [u8], rd_buffer: &mut [u8],
) -> Result<(), Error> { ) -> Result<(), Error> {
self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, false)?; self.blocking_transaction_from_ram(
self.blocking_wait(); address,
compiler_fence(SeqCst); &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
self.check_errorsrc()?; true,
self.check_tx(wr_buffer.len())?; )
self.check_rx(rd_buffer.len())?;
Ok(())
} }
// =========================================== // ===========================================
@ -562,12 +770,7 @@ impl<'d, T: Instance> Twim<'d, T> {
/// See [`blocking_write`]. /// See [`blocking_write`].
#[cfg(feature = "time")] #[cfg(feature = "time")]
pub fn blocking_write_timeout(&mut self, address: u8, buffer: &[u8], timeout: Duration) -> Result<(), Error> { pub fn blocking_write_timeout(&mut self, address: u8, buffer: &[u8], timeout: Duration) -> Result<(), Error> {
self.setup_write(address, buffer, false)?; self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], true, timeout)
self.blocking_wait_timeout(timeout)?;
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_tx(buffer.len())?;
Ok(())
} }
/// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
@ -578,12 +781,7 @@ impl<'d, T: Instance> Twim<'d, T> {
buffer: &[u8], buffer: &[u8],
timeout: Duration, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.setup_write_from_ram(address, buffer, false)?; self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], true, timeout)
self.blocking_wait_timeout(timeout)?;
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_tx(buffer.len())?;
Ok(())
} }
/// Read from an I2C slave. /// Read from an I2C slave.
@ -592,12 +790,7 @@ impl<'d, T: Instance> Twim<'d, T> {
/// and at most 65535 bytes on the nRF52840. /// and at most 65535 bytes on the nRF52840.
#[cfg(feature = "time")] #[cfg(feature = "time")]
pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> {
self.setup_read(address, buffer, false)?; self.blocking_transaction_timeout(address, &mut [Operation::Read(buffer)], true, timeout)
self.blocking_wait_timeout(timeout)?;
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_rx(buffer.len())?;
Ok(())
} }
/// Write data to an I2C slave, then read data from the slave without /// Write data to an I2C slave, then read data from the slave without
@ -613,13 +806,12 @@ impl<'d, T: Instance> Twim<'d, T> {
rd_buffer: &mut [u8], rd_buffer: &mut [u8],
timeout: Duration, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.setup_write_read(address, wr_buffer, rd_buffer, false)?; self.blocking_transaction_timeout(
self.blocking_wait_timeout(timeout)?; address,
compiler_fence(SeqCst); &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
self.check_errorsrc()?; true,
self.check_tx(wr_buffer.len())?; timeout,
self.check_rx(rd_buffer.len())?; )
Ok(())
} }
/// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
@ -631,13 +823,12 @@ impl<'d, T: Instance> Twim<'d, T> {
rd_buffer: &mut [u8], rd_buffer: &mut [u8],
timeout: Duration, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, false)?; self.blocking_transaction_from_ram_timeout(
self.blocking_wait_timeout(timeout)?; address,
compiler_fence(SeqCst); &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
self.check_errorsrc()?; true,
self.check_tx(wr_buffer.len())?; timeout,
self.check_rx(rd_buffer.len())?; )
Ok(())
} }
// =========================================== // ===========================================
@ -647,12 +838,7 @@ impl<'d, T: Instance> Twim<'d, T> {
/// The buffer must have a length of at most 255 bytes on the nRF52832 /// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840. /// and at most 65535 bytes on the nRF52840.
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.setup_read(address, buffer, true)?; self.transaction(address, &mut [Operation::Read(buffer)], true).await
self.async_wait().await;
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_rx(buffer.len())?;
Ok(())
} }
/// Write to an I2C slave. /// Write to an I2C slave.
@ -660,22 +846,13 @@ impl<'d, T: Instance> Twim<'d, T> {
/// The buffer must have a length of at most 255 bytes on the nRF52832 /// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840. /// and at most 65535 bytes on the nRF52840.
pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
self.setup_write(address, buffer, true)?; self.transaction(address, &mut [Operation::Write(buffer)], true).await
self.async_wait().await;
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_tx(buffer.len())?;
Ok(())
} }
/// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
self.setup_write_from_ram(address, buffer, true)?; self.transaction_from_ram(address, &mut [Operation::Write(buffer)], true)
self.async_wait().await; .await
compiler_fence(SeqCst);
self.check_errorsrc()?;
self.check_tx(buffer.len())?;
Ok(())
} }
/// Write data to an I2C slave, then read data from the slave without /// Write data to an I2C slave, then read data from the slave without
@ -684,13 +861,12 @@ impl<'d, T: Instance> Twim<'d, T> {
/// The buffers must have a length of at most 255 bytes on the nRF52832 /// The buffers must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840. /// and at most 65535 bytes on the nRF52840.
pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> {
self.setup_write_read(address, wr_buffer, rd_buffer, true)?; self.transaction(
self.async_wait().await; address,
compiler_fence(SeqCst); &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
self.check_errorsrc()?; true,
self.check_tx(wr_buffer.len())?; )
self.check_rx(rd_buffer.len())?; .await
Ok(())
} }
/// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. /// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
@ -700,13 +876,12 @@ impl<'d, T: Instance> Twim<'d, T> {
wr_buffer: &[u8], wr_buffer: &[u8],
rd_buffer: &mut [u8], rd_buffer: &mut [u8],
) -> Result<(), Error> { ) -> Result<(), Error> {
self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, true)?; self.transaction_from_ram(
self.async_wait().await; address,
compiler_fence(SeqCst); &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
self.check_errorsrc()?; true,
self.check_tx(wr_buffer.len())?; )
self.check_rx(rd_buffer.len())?; .await
Ok(())
} }
} }
@ -777,16 +952,7 @@ mod eh02 {
type Error = Error; type Error = Error;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
if slice_in_ram(bytes) {
self.blocking_write(addr, bytes) self.blocking_write(addr, bytes)
} else {
let buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..];
for chunk in bytes.chunks(FORCE_COPY_BUFFER_SIZE) {
buf[..chunk.len()].copy_from_slice(chunk);
self.blocking_write(addr, &buf[..chunk.len()])?;
}
Ok(())
}
} }
} }
@ -832,47 +998,14 @@ impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for Twim<'d, T> {
} }
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> { impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer) self.blocking_transaction(address, operations, true)
}
fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, buffer)
}
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, wr_buffer, rd_buffer)
}
fn transaction(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
todo!();
} }
} }
impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
self.read(address, read).await self.transaction(address, operations, true).await
}
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.write(address, write).await
}
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.write_read(address, write, read).await
}
async fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
let _ = address;
let _ = operations;
todo!()
} }
} }