Make the nrf Twim RAM buffer a instance variable instead of stack allocated

This commit is contained in:
Alex Moon 2025-04-18 15:08:18 -04:00
parent ca40dc7ff7
commit 77d355e0ec
No known key found for this signature in database
GPG Key ID: A6D388B98A7DB071
3 changed files with 28 additions and 143 deletions

View File

@ -4,7 +4,6 @@
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;
@ -17,7 +16,7 @@ use embassy_time::{Duration, Instant};
use embedded_hal_1::i2c::Operation; use embedded_hal_1::i2c::Operation;
pub use pac::twim::vals::Frequency; pub use pac::twim::vals::Frequency;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::chip::EASY_DMA_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::pac::gpio::vals as gpiovals; use crate::pac::gpio::vals as gpiovals;
@ -75,8 +74,8 @@ pub enum Error {
Transmit, Transmit,
/// Data reception failed. /// Data reception failed.
Receive, Receive,
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. /// The buffer is not in data RAM and is larger than the RAM buffer. It's most likely in flash, and nRF's DMA cannot access flash.
BufferNotInRAM, RAMBufferTooSmall,
/// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly. /// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly.
AddressNack, AddressNack,
/// Didn't receive an ACK bit after a data byte. /// Didn't receive an ACK bit after a data byte.
@ -115,6 +114,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
/// TWI driver. /// TWI driver.
pub struct Twim<'d, T: Instance> { pub struct Twim<'d, T: Instance> {
_p: Peri<'d, T>, _p: Peri<'d, T>,
tx_ram_buffer: &'d mut [u8],
} }
impl<'d, T: Instance> Twim<'d, T> { impl<'d, T: Instance> Twim<'d, T> {
@ -125,6 +125,7 @@ impl<'d, T: Instance> Twim<'d, T> {
sda: Peri<'d, impl GpioPin>, sda: Peri<'d, impl GpioPin>,
scl: Peri<'d, impl GpioPin>, scl: Peri<'d, impl GpioPin>,
config: Config, config: Config,
tx_ram_buffer: &'d mut [u8],
) -> Self { ) -> Self {
let r = T::regs(); let r = T::regs();
@ -159,7 +160,10 @@ impl<'d, T: Instance> Twim<'d, T> {
// Enable TWIM instance. // Enable TWIM instance.
r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));
let mut twim = Self { _p: twim }; let mut twim = Self {
_p: twim,
tx_ram_buffer,
};
// Apply runtime peripheral configuration // Apply runtime peripheral configuration
Self::set_config(&mut twim, &config).unwrap(); Self::set_config(&mut twim, &config).unwrap();
@ -174,21 +178,17 @@ 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( unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
&mut self,
buffer: &[u8],
ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
) -> Result<(), Error> {
let buffer = if slice_in_ram(buffer) { let buffer = if slice_in_ram(buffer) {
buffer buffer
} else { } else {
let ram_buffer = ram_buffer.ok_or(Error::BufferNotInRAM)?; if buffer.len() > self.tx_ram_buffer.len() {
return Err(Error::RAMBufferTooSmall);
}
trace!("Copying TWIM tx buffer into RAM for DMA"); trace!("Copying TWIM tx buffer into RAM for DMA");
let ram_buffer = &mut ram_buffer[..buffer.len()]; let ram_buffer = &mut self.tx_ram_buffer[..buffer.len()];
// Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer) ram_buffer.copy_from_slice(buffer);
let uninit_src: &[MaybeUninit<u8>] = unsafe { core::mem::transmute(buffer) }; &*ram_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 {
@ -358,7 +358,6 @@ impl<'d, T: Instance> Twim<'d, T> {
&mut self, &mut self,
address: u8, address: u8,
operations: &mut [Operation<'_>], operations: &mut [Operation<'_>],
tx_ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
last_op: Option<&Operation<'_>>, last_op: Option<&Operation<'_>>,
inten: bool, inten: bool,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
@ -397,7 +396,7 @@ impl<'d, T: Instance> Twim<'d, T> {
// Set up DMA buffers. // Set up DMA buffers.
unsafe { unsafe {
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; self.set_tx_buffer(wr_buffer)?;
self.set_rx_buffer(rd_buffer)?; self.set_rx_buffer(rd_buffer)?;
} }
@ -450,7 +449,7 @@ impl<'d, T: Instance> Twim<'d, T> {
{ {
// Set up DMA buffers. // Set up DMA buffers.
unsafe { unsafe {
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; self.set_tx_buffer(wr_buffer)?;
self.set_rx_buffer(rd_buffer)?; self.set_rx_buffer(rd_buffer)?;
} }
@ -472,7 +471,7 @@ impl<'d, T: Instance> Twim<'d, T> {
// Set up DMA buffers. // Set up DMA buffers.
unsafe { unsafe {
self.set_tx_buffer(buffer, tx_ram_buffer)?; self.set_tx_buffer(buffer)?;
} }
// Start write operation. // Start write operation.
@ -539,28 +538,9 @@ impl<'d, T: Instance> Twim<'d, T> {
/// An `Operation::Write` following an `Operation::Read` must have a /// An `Operation::Write` following an `Operation::Read` must have a
/// non-empty buffer. /// non-empty buffer.
pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
let mut last_op = None; let mut last_op = None;
while !operations.is_empty() { while !operations.is_empty() {
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; let ops = self.setup_operations(address, operations, last_op, false)?;
let (in_progress, rest) = operations.split_at_mut(ops);
self.blocking_wait();
self.check_operations(in_progress)?;
last_op = in_progress.last();
operations = rest;
}
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<'_>],
) -> Result<(), Error> {
let mut last_op = None;
while !operations.is_empty() {
let ops = self.setup_operations(address, operations, None, last_op, false)?;
let (in_progress, rest) = operations.split_at_mut(ops); let (in_progress, rest) = operations.split_at_mut(ops);
self.blocking_wait(); self.blocking_wait();
self.check_operations(in_progress)?; self.check_operations(in_progress)?;
@ -580,30 +560,9 @@ impl<'d, T: Instance> Twim<'d, T> {
mut operations: &mut [Operation<'_>], mut operations: &mut [Operation<'_>],
timeout: Duration, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
let mut last_op = None; let mut last_op = None;
while !operations.is_empty() { while !operations.is_empty() {
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; let ops = self.setup_operations(address, operations, last_op, false)?;
let (in_progress, rest) = operations.split_at_mut(ops);
self.blocking_wait_timeout(timeout)?;
self.check_operations(in_progress)?;
last_op = in_progress.last();
operations = rest;
}
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<'_>],
timeout: Duration,
) -> Result<(), Error> {
let mut last_op = None;
while !operations.is_empty() {
let ops = self.setup_operations(address, operations, None, last_op, false)?;
let (in_progress, rest) = operations.split_at_mut(ops); let (in_progress, rest) = operations.split_at_mut(ops);
self.blocking_wait_timeout(timeout)?; self.blocking_wait_timeout(timeout)?;
self.check_operations(in_progress)?; self.check_operations(in_progress)?;
@ -624,28 +583,9 @@ impl<'d, T: Instance> Twim<'d, T> {
/// An `Operation::Write` following an `Operation::Read` must have a /// An `Operation::Write` following an `Operation::Read` must have a
/// non-empty buffer. /// non-empty buffer.
pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
let mut last_op = None; let mut last_op = None;
while !operations.is_empty() { while !operations.is_empty() {
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, true)?; let ops = self.setup_operations(address, operations, last_op, true)?;
let (in_progress, rest) = operations.split_at_mut(ops);
self.async_wait().await?;
self.check_operations(in_progress)?;
last_op = in_progress.last();
operations = rest;
}
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<'_>],
) -> Result<(), Error> {
let mut last_op = None;
while !operations.is_empty() {
let ops = self.setup_operations(address, operations, None, last_op, true)?;
let (in_progress, rest) = operations.split_at_mut(ops); let (in_progress, rest) = operations.split_at_mut(ops);
self.async_wait().await?; self.async_wait().await?;
self.check_operations(in_progress)?; self.check_operations(in_progress)?;
@ -665,11 +605,6 @@ impl<'d, T: Instance> Twim<'d, T> {
self.blocking_transaction(address, &mut [Operation::Write(buffer)]) self.blocking_transaction(address, &mut [Operation::Write(buffer)])
} }
/// 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> {
self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)])
}
/// Read from an I2C slave. /// Read from 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
@ -687,16 +622,6 @@ impl<'d, T: Instance> Twim<'d, T> {
self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
} }
/// 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.
pub fn blocking_write_read_from_ram(
&mut self,
address: u8,
wr_buffer: &[u8],
rd_buffer: &mut [u8],
) -> Result<(), Error> {
self.blocking_transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
}
// =========================================== // ===========================================
/// Write to an I2C slave with timeout. /// Write to an I2C slave with timeout.
@ -707,17 +632,6 @@ impl<'d, T: Instance> Twim<'d, T> {
self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout) self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout)
} }
/// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
#[cfg(feature = "time")]
pub fn blocking_write_from_ram_timeout(
&mut self,
address: u8,
buffer: &[u8],
timeout: Duration,
) -> Result<(), Error> {
self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], timeout)
}
/// Read from an I2C slave. /// Read from 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
@ -747,22 +661,6 @@ impl<'d, T: Instance> Twim<'d, T> {
) )
} }
/// 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.
#[cfg(feature = "time")]
pub fn blocking_write_read_from_ram_timeout(
&mut self,
address: u8,
wr_buffer: &[u8],
rd_buffer: &mut [u8],
timeout: Duration,
) -> Result<(), Error> {
self.blocking_transaction_from_ram_timeout(
address,
&mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
timeout,
)
}
// =========================================== // ===========================================
/// Read from an I2C slave. /// Read from an I2C slave.
@ -781,12 +679,6 @@ impl<'d, T: Instance> Twim<'d, T> {
self.transaction(address, &mut [Operation::Write(buffer)]).await self.transaction(address, &mut [Operation::Write(buffer)]).await
} }
/// 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> {
self.transaction_from_ram(address, &mut [Operation::Write(buffer)])
.await
}
/// 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
/// triggering a stop condition between the two. /// triggering a stop condition between the two.
/// ///
@ -796,17 +688,6 @@ impl<'d, T: Instance> Twim<'d, T> {
self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
.await .await
} }
/// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub async fn write_read_from_ram(
&mut self,
address: u8,
wr_buffer: &[u8],
rd_buffer: &mut [u8],
) -> Result<(), Error> {
self.transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
.await
}
} }
impl<'a, T: Instance> Drop for Twim<'a, T> { impl<'a, T: Instance> Drop for Twim<'a, T> {
@ -904,7 +785,7 @@ impl embedded_hal_1::i2c::Error for Error {
Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other, Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,
Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other,
Self::Receive => embedded_hal_1::i2c::ErrorKind::Other, Self::Receive => embedded_hal_1::i2c::ErrorKind::Other,
Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other, Self::RAMBufferTooSmall => embedded_hal_1::i2c::ErrorKind::Other,
Self::AddressNack => { Self::AddressNack => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
} }

View File

@ -9,6 +9,7 @@ use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_nrf::twim::{self, Twim}; use embassy_nrf::twim::{self, Twim};
use embassy_nrf::{bind_interrupts, peripherals}; use embassy_nrf::{bind_interrupts, peripherals};
use static_cell::ConstStaticCell;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
const ADDRESS: u8 = 0x50; const ADDRESS: u8 = 0x50;
@ -22,7 +23,8 @@ async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default()); let p = embassy_nrf::init(Default::default());
info!("Initializing TWI..."); info!("Initializing TWI...");
let config = twim::Config::default(); let config = twim::Config::default();
let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]);
let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config, RAM_BUFFER.take());
info!("Reading..."); info!("Reading...");

View File

@ -30,6 +30,7 @@ async fn main(_p: Spawner) {
loop { loop {
info!("Initializing TWI..."); info!("Initializing TWI...");
let config = twim::Config::default(); let config = twim::Config::default();
let mut ram_buffer = [0u8; 16];
// Create the TWIM instance with borrowed singletons, so they're not consumed. // Create the TWIM instance with borrowed singletons, so they're not consumed.
let mut twi = Twim::new( let mut twi = Twim::new(
@ -38,6 +39,7 @@ async fn main(_p: Spawner) {
p.P0_03.reborrow(), p.P0_03.reborrow(),
p.P0_04.reborrow(), p.P0_04.reborrow(),
config, config,
&mut ram_buffer,
); );
info!("Reading..."); info!("Reading...");