Skip to content

Commit 5f0196c

Browse files
authored
Merge pull request #4113 from alexmoon/twim-ram-buffer
Make the nrf Twim RAM buffer a instance variable instead of stack allocated
2 parents 1d2f0ad + fb7504c commit 5f0196c

File tree

3 files changed

+34
-143
lines changed

3 files changed

+34
-143
lines changed

embassy-nrf/src/twim.rs

+29-142
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use core::future::{poll_fn, Future};
66
use core::marker::PhantomData;
7-
use core::mem::MaybeUninit;
87
use core::sync::atomic::compiler_fence;
98
use core::sync::atomic::Ordering::SeqCst;
109
use core::task::Poll;
@@ -17,7 +16,7 @@ use embassy_time::{Duration, Instant};
1716
use embedded_hal_1::i2c::Operation;
1817
pub use pac::twim::vals::Frequency;
1918

20-
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
19+
use crate::chip::EASY_DMA_SIZE;
2120
use crate::gpio::Pin as GpioPin;
2221
use crate::interrupt::typelevel::Interrupt;
2322
use crate::pac::gpio::vals as gpiovals;
@@ -75,8 +74,8 @@ pub enum Error {
7574
Transmit,
7675
/// Data reception failed.
7776
Receive,
78-
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
79-
BufferNotInRAM,
77+
/// 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.
78+
RAMBufferTooSmall,
8079
/// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly.
8180
AddressNack,
8281
/// Didn't receive an ACK bit after a data byte.
@@ -115,16 +114,24 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
115114
/// TWI driver.
116115
pub struct Twim<'d, T: Instance> {
117116
_p: Peri<'d, T>,
117+
tx_ram_buffer: &'d mut [u8],
118118
}
119119

120120
impl<'d, T: Instance> Twim<'d, T> {
121121
/// Create a new TWI driver.
122+
///
123+
/// `tx_ram_buffer` is required if any write operations will be performed with data that is not in RAM.
124+
/// Usually this is static data that the compiler locates in flash instead of RAM. The `tx_ram_buffer`
125+
/// needs to be at least as large as the largest write operation that will be executed with a buffer
126+
/// that is not in RAM. If all write operations will be performed from RAM, an empty buffer (`&[]`) may
127+
/// be used.
122128
pub fn new(
123129
twim: Peri<'d, T>,
124130
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
125131
sda: Peri<'d, impl GpioPin>,
126132
scl: Peri<'d, impl GpioPin>,
127133
config: Config,
134+
tx_ram_buffer: &'d mut [u8],
128135
) -> Self {
129136
let r = T::regs();
130137

@@ -159,7 +166,10 @@ impl<'d, T: Instance> Twim<'d, T> {
159166
// Enable TWIM instance.
160167
r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));
161168

162-
let mut twim = Self { _p: twim };
169+
let mut twim = Self {
170+
_p: twim,
171+
tx_ram_buffer,
172+
};
163173

164174
// Apply runtime peripheral configuration
165175
Self::set_config(&mut twim, &config).unwrap();
@@ -174,21 +184,17 @@ impl<'d, T: Instance> Twim<'d, T> {
174184
}
175185

176186
/// Set TX buffer, checking that it is in RAM and has suitable length.
177-
unsafe fn set_tx_buffer(
178-
&mut self,
179-
buffer: &[u8],
180-
ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
181-
) -> Result<(), Error> {
187+
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
182188
let buffer = if slice_in_ram(buffer) {
183189
buffer
184190
} else {
185-
let ram_buffer = ram_buffer.ok_or(Error::BufferNotInRAM)?;
191+
if buffer.len() > self.tx_ram_buffer.len() {
192+
return Err(Error::RAMBufferTooSmall);
193+
}
186194
trace!("Copying TWIM tx buffer into RAM for DMA");
187-
let ram_buffer = &mut ram_buffer[..buffer.len()];
188-
// Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer)
189-
let uninit_src: &[MaybeUninit<u8>] = unsafe { core::mem::transmute(buffer) };
190-
ram_buffer.copy_from_slice(uninit_src);
191-
unsafe { &*(ram_buffer as *const [MaybeUninit<u8>] as *const [u8]) }
195+
let ram_buffer = &mut self.tx_ram_buffer[..buffer.len()];
196+
ram_buffer.copy_from_slice(buffer);
197+
&*ram_buffer
192198
};
193199

194200
if buffer.len() > EASY_DMA_SIZE {
@@ -358,7 +364,6 @@ impl<'d, T: Instance> Twim<'d, T> {
358364
&mut self,
359365
address: u8,
360366
operations: &mut [Operation<'_>],
361-
tx_ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
362367
last_op: Option<&Operation<'_>>,
363368
inten: bool,
364369
) -> Result<usize, Error> {
@@ -397,7 +402,7 @@ impl<'d, T: Instance> Twim<'d, T> {
397402

398403
// Set up DMA buffers.
399404
unsafe {
400-
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?;
405+
self.set_tx_buffer(wr_buffer)?;
401406
self.set_rx_buffer(rd_buffer)?;
402407
}
403408

@@ -450,7 +455,7 @@ impl<'d, T: Instance> Twim<'d, T> {
450455
{
451456
// Set up DMA buffers.
452457
unsafe {
453-
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?;
458+
self.set_tx_buffer(wr_buffer)?;
454459
self.set_rx_buffer(rd_buffer)?;
455460
}
456461

@@ -472,7 +477,7 @@ impl<'d, T: Instance> Twim<'d, T> {
472477

473478
// Set up DMA buffers.
474479
unsafe {
475-
self.set_tx_buffer(buffer, tx_ram_buffer)?;
480+
self.set_tx_buffer(buffer)?;
476481
}
477482

478483
// Start write operation.
@@ -539,28 +544,9 @@ impl<'d, T: Instance> Twim<'d, T> {
539544
/// An `Operation::Write` following an `Operation::Read` must have a
540545
/// non-empty buffer.
541546
pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {
542-
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
543547
let mut last_op = None;
544548
while !operations.is_empty() {
545-
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?;
546-
let (in_progress, rest) = operations.split_at_mut(ops);
547-
self.blocking_wait();
548-
self.check_operations(in_progress)?;
549-
last_op = in_progress.last();
550-
operations = rest;
551-
}
552-
Ok(())
553-
}
554-
555-
/// Same as [`blocking_transaction`](Twim::blocking_transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
556-
pub fn blocking_transaction_from_ram(
557-
&mut self,
558-
address: u8,
559-
mut operations: &mut [Operation<'_>],
560-
) -> Result<(), Error> {
561-
let mut last_op = None;
562-
while !operations.is_empty() {
563-
let ops = self.setup_operations(address, operations, None, last_op, false)?;
549+
let ops = self.setup_operations(address, operations, last_op, false)?;
564550
let (in_progress, rest) = operations.split_at_mut(ops);
565551
self.blocking_wait();
566552
self.check_operations(in_progress)?;
@@ -579,31 +565,10 @@ impl<'d, T: Instance> Twim<'d, T> {
579565
address: u8,
580566
mut operations: &mut [Operation<'_>],
581567
timeout: Duration,
582-
) -> Result<(), Error> {
583-
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
584-
let mut last_op = None;
585-
while !operations.is_empty() {
586-
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?;
587-
let (in_progress, rest) = operations.split_at_mut(ops);
588-
self.blocking_wait_timeout(timeout)?;
589-
self.check_operations(in_progress)?;
590-
last_op = in_progress.last();
591-
operations = rest;
592-
}
593-
Ok(())
594-
}
595-
596-
/// 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.
597-
#[cfg(feature = "time")]
598-
pub fn blocking_transaction_from_ram_timeout(
599-
&mut self,
600-
address: u8,
601-
mut operations: &mut [Operation<'_>],
602-
timeout: Duration,
603568
) -> Result<(), Error> {
604569
let mut last_op = None;
605570
while !operations.is_empty() {
606-
let ops = self.setup_operations(address, operations, None, last_op, false)?;
571+
let ops = self.setup_operations(address, operations, last_op, false)?;
607572
let (in_progress, rest) = operations.split_at_mut(ops);
608573
self.blocking_wait_timeout(timeout)?;
609574
self.check_operations(in_progress)?;
@@ -624,28 +589,9 @@ impl<'d, T: Instance> Twim<'d, T> {
624589
/// An `Operation::Write` following an `Operation::Read` must have a
625590
/// non-empty buffer.
626591
pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {
627-
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
628-
let mut last_op = None;
629-
while !operations.is_empty() {
630-
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, true)?;
631-
let (in_progress, rest) = operations.split_at_mut(ops);
632-
self.async_wait().await?;
633-
self.check_operations(in_progress)?;
634-
last_op = in_progress.last();
635-
operations = rest;
636-
}
637-
Ok(())
638-
}
639-
640-
/// Same as [`transaction`](Twim::transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
641-
pub async fn transaction_from_ram(
642-
&mut self,
643-
address: u8,
644-
mut operations: &mut [Operation<'_>],
645-
) -> Result<(), Error> {
646592
let mut last_op = None;
647593
while !operations.is_empty() {
648-
let ops = self.setup_operations(address, operations, None, last_op, true)?;
594+
let ops = self.setup_operations(address, operations, last_op, true)?;
649595
let (in_progress, rest) = operations.split_at_mut(ops);
650596
self.async_wait().await?;
651597
self.check_operations(in_progress)?;
@@ -665,11 +611,6 @@ impl<'d, T: Instance> Twim<'d, T> {
665611
self.blocking_transaction(address, &mut [Operation::Write(buffer)])
666612
}
667613

668-
/// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
669-
pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
670-
self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)])
671-
}
672-
673614
/// Read from an I2C slave.
674615
///
675616
/// The buffer must have a length of at most 255 bytes on the nRF52832
@@ -687,16 +628,6 @@ impl<'d, T: Instance> Twim<'d, T> {
687628
self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
688629
}
689630

690-
/// 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.
691-
pub fn blocking_write_read_from_ram(
692-
&mut self,
693-
address: u8,
694-
wr_buffer: &[u8],
695-
rd_buffer: &mut [u8],
696-
) -> Result<(), Error> {
697-
self.blocking_transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
698-
}
699-
700631
// ===========================================
701632

702633
/// Write to an I2C slave with timeout.
@@ -707,17 +638,6 @@ impl<'d, T: Instance> Twim<'d, T> {
707638
self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout)
708639
}
709640

710-
/// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
711-
#[cfg(feature = "time")]
712-
pub fn blocking_write_from_ram_timeout(
713-
&mut self,
714-
address: u8,
715-
buffer: &[u8],
716-
timeout: Duration,
717-
) -> Result<(), Error> {
718-
self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], timeout)
719-
}
720-
721641
/// Read from an I2C slave.
722642
///
723643
/// The buffer must have a length of at most 255 bytes on the nRF52832
@@ -747,22 +667,6 @@ impl<'d, T: Instance> Twim<'d, T> {
747667
)
748668
}
749669

750-
/// 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.
751-
#[cfg(feature = "time")]
752-
pub fn blocking_write_read_from_ram_timeout(
753-
&mut self,
754-
address: u8,
755-
wr_buffer: &[u8],
756-
rd_buffer: &mut [u8],
757-
timeout: Duration,
758-
) -> Result<(), Error> {
759-
self.blocking_transaction_from_ram_timeout(
760-
address,
761-
&mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
762-
timeout,
763-
)
764-
}
765-
766670
// ===========================================
767671

768672
/// Read from an I2C slave.
@@ -781,12 +685,6 @@ impl<'d, T: Instance> Twim<'d, T> {
781685
self.transaction(address, &mut [Operation::Write(buffer)]).await
782686
}
783687

784-
/// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
785-
pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
786-
self.transaction_from_ram(address, &mut [Operation::Write(buffer)])
787-
.await
788-
}
789-
790688
/// Write data to an I2C slave, then read data from the slave without
791689
/// triggering a stop condition between the two.
792690
///
@@ -796,17 +694,6 @@ impl<'d, T: Instance> Twim<'d, T> {
796694
self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
797695
.await
798696
}
799-
800-
/// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
801-
pub async fn write_read_from_ram(
802-
&mut self,
803-
address: u8,
804-
wr_buffer: &[u8],
805-
rd_buffer: &mut [u8],
806-
) -> Result<(), Error> {
807-
self.transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
808-
.await
809-
}
810697
}
811698

812699
impl<'a, T: Instance> Drop for Twim<'a, T> {
@@ -904,7 +791,7 @@ impl embedded_hal_1::i2c::Error for Error {
904791
Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,
905792
Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other,
906793
Self::Receive => embedded_hal_1::i2c::ErrorKind::Other,
907-
Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other,
794+
Self::RAMBufferTooSmall => embedded_hal_1::i2c::ErrorKind::Other,
908795
Self::AddressNack => {
909796
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
910797
}

examples/nrf52840/src/bin/twim.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use defmt::*;
99
use embassy_executor::Spawner;
1010
use embassy_nrf::twim::{self, Twim};
1111
use embassy_nrf::{bind_interrupts, peripherals};
12+
use static_cell::ConstStaticCell;
1213
use {defmt_rtt as _, panic_probe as _};
1314

1415
const ADDRESS: u8 = 0x50;
@@ -22,7 +23,8 @@ async fn main(_spawner: Spawner) {
2223
let p = embassy_nrf::init(Default::default());
2324
info!("Initializing TWI...");
2425
let config = twim::Config::default();
25-
let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config);
26+
static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]);
27+
let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config, RAM_BUFFER.take());
2628

2729
info!("Reading...");
2830

examples/nrf52840/src/bin/twim_lowpower.rs

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ async fn main(_p: Spawner) {
3030
loop {
3131
info!("Initializing TWI...");
3232
let config = twim::Config::default();
33+
let mut ram_buffer = [0u8; 16];
3334

3435
// Create the TWIM instance with borrowed singletons, so they're not consumed.
3536
let mut twi = Twim::new(
@@ -38,6 +39,7 @@ async fn main(_p: Spawner) {
3839
p.P0_03.reborrow(),
3940
p.P0_04.reborrow(),
4041
config,
42+
&mut ram_buffer,
4143
);
4244

4345
info!("Reading...");

0 commit comments

Comments
 (0)