diff options
| author | Til Blechschmidt <[email protected]> | 2022-02-23 22:51:59 +0100 |
|---|---|---|
| committer | Til Blechschmidt <[email protected]> | 2022-02-23 22:51:59 +0100 |
| commit | e96dd3654a5b3919ad853a33a89927596f5ca1c2 (patch) | |
| tree | fd5e6dc3b7bac95ea371cd6f3e20ce67c9abe91f | |
| parent | ed9fad8c7e71d7a868683957bbfdf64c0eca7ffe (diff) | |
Change SPIM methods to copy slice if required and add non-copying variants
| -rw-r--r-- | embassy-nrf/src/spim.rs | 101 |
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. | ||
| 31 | pub struct Spim<'d, T: Instance> { | 65 | pub 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 | ||
| 289 | impl<'d, T: Instance> Drop for Spim<'d, T> { | 382 | impl<'d, T: Instance> Drop for Spim<'d, T> { |
