aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTil Blechschmidt <[email protected]>2022-02-23 22:51:59 +0100
committerTil Blechschmidt <[email protected]>2022-02-23 22:51:59 +0100
commite96dd3654a5b3919ad853a33a89927596f5ca1c2 (patch)
treefd5e6dc3b7bac95ea371cd6f3e20ce67c9abe91f
parented9fad8c7e71d7a868683957bbfdf64c0eca7ffe (diff)
Change SPIM methods to copy slice if required and add non-copying variants
-rw-r--r--embassy-nrf/src/spim.rs101
1 files changed, 97 insertions, 4 deletions
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs
index 6dbf67e3b..fedd4e480 100644
--- a/embassy-nrf/src/spim.rs
+++ b/embassy-nrf/src/spim.rs
@@ -28,6 +28,40 @@ pub enum Error {
28 DMABufferNotInDataMemory, 28 DMABufferNotInDataMemory,
29} 29}
30 30
31/// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload.
32///
33/// ## Data locality requirements
34///
35/// On nRF chips, EasyDMA requires the buffers to reside in RAM. However, Rust
36/// slices will not always do so. Take the following example:
37///
38/// ```no_run
39/// // As we pass a slice to the function whose contents will not ever change,
40/// // the compiler writes it into the flash and thus the pointer to it will
41/// // reference static memory. Since EasyDMA requires slices to reside in RAM,
42/// // this function call will fail.
43/// let result = spim.write_from_ram(&[1, 2, 3]);
44/// assert_eq!(result, Error::DMABufferNotInDataMemory);
45///
46/// // The data is still static and located in flash. However, since we are assigning
47/// // it to a variable, the compiler will load it into memory. Passing a reference to the
48/// // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy.
49/// // This function call succeeds.
50/// let data = [1, 2, 3];
51/// let result = spim.write_from_ram(&data);
52/// assert!(result.is_ok());
53/// ```
54///
55/// Each function in this struct has a `_from_ram` variant and one without this suffix.
56/// - Functions with the suffix (e.g. [`write_from_ram`](Spim::write_from_ram), [`transfer_from_ram`](Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM.
57/// - Functions without the suffix (e.g. [`write`](Spim::write), [`transfer`](Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission.
58///
59/// Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will
60/// fail and notify you, or the more convenient versions without the suffix which are potentially a little bit
61/// more inefficient.
62///
63/// Note that the [`read`](Spim::read) and [`transfer_in_place`](Spim::transfer_in_place) methods do not have the corresponding `_from_ram` variants as
64/// mutable slices always reside in RAM.
31pub struct Spim<'d, T: Instance> { 65pub struct Spim<'d, T: Instance> {
32 phantom: PhantomData<&'d mut T>, 66 phantom: PhantomData<&'d mut T>,
33} 67}
@@ -223,7 +257,7 @@ impl<'d, T: Instance> Spim<'d, T> {
223 Ok(()) 257 Ok(())
224 } 258 }
225 259
226 fn blocking_inner(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 260 fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
227 self.prepare(rx, tx)?; 261 self.prepare(rx, tx)?;
228 262
229 // Wait for 'end' event. 263 // Wait for 'end' event.
@@ -234,7 +268,18 @@ impl<'d, T: Instance> Spim<'d, T> {
234 Ok(()) 268 Ok(())
235 } 269 }
236 270
237 async fn async_inner(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 271 fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
272 match self.blocking_inner_from_ram(rx, tx) {
273 Ok(_) => Ok(()),
274 Err(Error::DMABufferNotInDataMemory) => {
275 let tx_copied = tx.clone();
276 self.blocking_inner_from_ram(rx, tx_copied)
277 }
278 Err(error) => Err(error),
279 }
280 }
281
282 async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
238 self.prepare(rx, tx)?; 283 self.prepare(rx, tx)?;
239 284
240 // Wait for 'end' event. 285 // Wait for 'end' event.
@@ -253,37 +298,85 @@ impl<'d, T: Instance> Spim<'d, T> {
253 Ok(()) 298 Ok(())
254 } 299 }
255 300
301 async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
302 match self.async_inner_from_ram(rx, tx).await {
303 Ok(_) => Ok(()),
304 Err(Error::DMABufferNotInDataMemory) => {
305 let tx_copied = tx.clone();
306 self.async_inner_from_ram(rx, tx_copied).await
307 }
308 Err(error) => Err(error),
309 }
310 }
311
312 /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled.
256 pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { 313 pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {
257 self.blocking_inner(data, &[]) 314 self.blocking_inner(data, &[])
258 } 315 }
259 316
317 /// Simultaneously sends and receives data. Blocks until the transmission is completed.
318 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
260 pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { 319 pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
261 self.blocking_inner(read, write) 320 self.blocking_inner(read, write)
262 } 321 }
263 322
323 /// Same as [`blocking_transfer`](Spim::blocking_transfer) but will fail instead of copying data into RAM.
324 pub fn blocking_transfer_from_ram(
325 &mut self,
326 read: &mut [u8],
327 write: &[u8],
328 ) -> Result<(), Error> {
329 self.blocking_inner(read, write)
330 }
331
332 /// Simultaneously sends and receives data.
333 /// Places the received data into the same buffer and blocks until the transmission is completed.
264 pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { 334 pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {
265 self.blocking_inner(data, data) 335 self.blocking_inner_from_ram(data, data)
266 } 336 }
267 337
338 /// Sends data, discarding any received data. Blocks until the transmission is completed.
339 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
268 pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { 340 pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {
269 self.blocking_inner(&mut [], data) 341 self.blocking_inner(&mut [], data)
270 } 342 }
271 343
344 /// Same as [`blocking_write`](Spim::blocking_write) but will fail instead of copying data into RAM.
345 pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> {
346 self.blocking_inner(&mut [], data)
347 }
348
349 /// Reads data from the SPI bus without sending anything.
272 pub async fn read(&mut self, data: &mut [u8]) -> Result<(), Error> { 350 pub async fn read(&mut self, data: &mut [u8]) -> Result<(), Error> {
273 self.async_inner(data, &[]).await 351 self.async_inner(data, &[]).await
274 } 352 }
275 353
354 /// Simultaneously sends and receives data.
355 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
276 pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { 356 pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
277 self.async_inner(read, write).await 357 self.async_inner(read, write).await
278 } 358 }
279 359
360 /// Same as [`transfer`](Spim::transfer) but will fail instead of copying data into RAM.
361 pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
362 self.async_inner_from_ram(read, write).await
363 }
364
365 /// Simultaneously sends and receives data. Places the received data into the same buffer.
280 pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { 366 pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {
281 self.async_inner(data, data).await 367 self.async_inner_from_ram(data, data).await
282 } 368 }
283 369
370 /// Sends data, discarding any received data.
371 /// If necessary, the write buffer will be copied into RAM (see struct description for detail).
284 pub async fn write(&mut self, data: &[u8]) -> Result<(), Error> { 372 pub async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
285 self.async_inner(&mut [], data).await 373 self.async_inner(&mut [], data).await
286 } 374 }
375
376 /// Same as [`write`](Spim::write) but will fail instead of copying data into RAM.
377 pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> {
378 self.async_inner_from_ram(&mut [], data).await
379 }
287} 380}
288 381
289impl<'d, T: Instance> Drop for Spim<'d, T> { 382impl<'d, T: Instance> Drop for Spim<'d, T> {