From cd1bf31fedbd33170507245eef1f7ae576aa3557 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 22 May 2023 16:48:31 +0200 Subject: [PATCH] Add YieldingAsync adapter --- embassy-embedded-hal/Cargo.toml | 4 +- .../{adapter.rs => adapter/blocking_async.rs} | 79 +----- embassy-embedded-hal/src/adapter/mod.rs | 5 + .../src/adapter/yielding_async.rs | 232 ++++++++++++++++++ 4 files changed, 241 insertions(+), 79 deletions(-) rename embassy-embedded-hal/src/{adapter.rs => adapter/blocking_async.rs} (70%) create mode 100644 embassy-embedded-hal/src/adapter/mod.rs create mode 100644 embassy-embedded-hal/src/adapter/yielding_async.rs diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 81cece68..ad2f1456 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -14,10 +14,10 @@ target = "x86_64-unknown-linux-gnu" [features] std = [] # Enable nightly-only features -nightly = ["embedded-hal-async", "embedded-storage-async"] +nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"] [dependencies] -embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs similarity index 70% rename from embassy-embedded-hal/src/adapter.rs rename to embassy-embedded-hal/src/adapter/blocking_async.rs index 169aad5e..171ff6c9 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -1,6 +1,5 @@ //! Adapters between embedded-hal traits. -use embassy_futures::yield_now; use embedded_hal_02::{blocking, serial}; /// Wrapper that implements async traits using blocking implementations. @@ -151,18 +150,11 @@ where const ERASE_SIZE: usize = ::ERASE_SIZE; async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { - self.wrapped.write(offset, data)?; - yield_now().await; - Ok(()) + self.wrapped.write(offset, data) } async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - for from in (from..to).step_by(T::ERASE_SIZE) { - let to = core::cmp::min(from + T::ERASE_SIZE as u32, to); - self.wrapped.erase(from, to)?; - yield_now().await; - } - Ok(()) + self.wrapped.erase(from, to) } } @@ -179,70 +171,3 @@ where self.wrapped.capacity() } } - -#[cfg(test)] -mod tests { - use super::*; - - extern crate std; - - #[derive(Default)] - struct FakeFlash(Vec<(u32, u32)>); - - impl embedded_storage::nor_flash::ErrorType for FakeFlash { - type Error = std::convert::Infallible; - } - - impl embedded_storage::nor_flash::ReadNorFlash for FakeFlash { - const READ_SIZE: usize = 1; - - fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - fn capacity(&self) -> usize { - unimplemented!() - } - } - - impl embedded_storage::nor_flash::NorFlash for FakeFlash { - const WRITE_SIZE: usize = 4; - const ERASE_SIZE: usize = 128; - - fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.0.push((from, to)); - Ok(()) - } - } - - #[futures_test::test] - async fn can_erase() { - let fake = FakeFlash::default(); - let mut yielding = BlockingAsync::new(fake); - - yielding.erase(0, 256).await.unwrap(); - - let fake = yielding.wrapped; - assert_eq!(2, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - } - - #[futures_test::test] - async fn can_erase_wrong_erase_size() { - let fake = FakeFlash::default(); - let mut yielding = BlockingAsync::new(fake); - - yielding.erase(0, 257).await.unwrap(); - - let fake = yielding.wrapped; - assert_eq!(3, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - assert_eq!((256, 257), fake.0[2]); - } -} diff --git a/embassy-embedded-hal/src/adapter/mod.rs b/embassy-embedded-hal/src/adapter/mod.rs new file mode 100644 index 00000000..787ac297 --- /dev/null +++ b/embassy-embedded-hal/src/adapter/mod.rs @@ -0,0 +1,5 @@ +mod blocking_async; +mod yielding_async; + +pub use blocking_async::BlockingAsync; +pub use yielding_async::YieldingAsync; diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs new file mode 100644 index 00000000..96d5cca8 --- /dev/null +++ b/embassy-embedded-hal/src/adapter/yielding_async.rs @@ -0,0 +1,232 @@ +use embassy_futures::yield_now; + +/// Wrapper that yields for each operation to the wrapped instance +/// +/// This can be used in combination with BlockingAsync to enforce yields +/// between long running blocking operations. +pub struct YieldingAsync { + wrapped: T, +} + +impl YieldingAsync { + /// Create a new instance of a wrapper that yields after each operation. + pub fn new(wrapped: T) -> Self { + Self { wrapped } + } +} + +// +// I2C implementations +// +impl embedded_hal_1::i2c::ErrorType for YieldingAsync +where + T: embedded_hal_1::i2c::ErrorType, +{ + type Error = T::Error; +} + +impl embedded_hal_async::i2c::I2c for YieldingAsync +where + T: embedded_hal_async::i2c::I2c, +{ + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, read).await?; + yield_now().await; + Ok(()) + } + + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(address, write).await?; + yield_now().await; + Ok(()) + } + + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.write_read(address, write, read).await?; + yield_now().await; + Ok(()) + } + + async fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.wrapped.transaction(address, operations).await?; + yield_now().await; + Ok(()) + } +} + +// +// SPI implementations +// + +impl embedded_hal_async::spi::ErrorType for YieldingAsync +where + T: embedded_hal_async::spi::ErrorType, +{ + type Error = T::Error; +} + +impl embedded_hal_async::spi::SpiBus for YieldingAsync +where + T: embedded_hal_async::spi::SpiBus, +{ + async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer(read, write).await?; + yield_now().await; + Ok(()) + } + + async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer_in_place(words).await?; + yield_now().await; + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusFlush for YieldingAsync +where + T: embedded_hal_async::spi::SpiBusFlush, +{ + async fn flush(&mut self) -> Result<(), Self::Error> { + self.wrapped.flush().await?; + yield_now().await; + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusWrite for YieldingAsync +where + T: embedded_hal_async::spi::SpiBusWrite, +{ + async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(data).await?; + yield_now().await; + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusRead for YieldingAsync +where + T: embedded_hal_async::spi::SpiBusRead, +{ + async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(data).await?; + yield_now().await; + Ok(()) + } +} + +/// +/// NOR flash implementations +/// +impl embedded_storage::nor_flash::ErrorType for YieldingAsync { + type Error = T::Error; +} + +impl embedded_storage_async::nor_flash::ReadNorFlash + for YieldingAsync +{ + const READ_SIZE: usize = T::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(offset, bytes).await?; + Ok(()) + } + + fn capacity(&self) -> usize { + self.wrapped.capacity() + } +} + +impl embedded_storage_async::nor_flash::NorFlash for YieldingAsync { + const WRITE_SIZE: usize = T::WRITE_SIZE; + const ERASE_SIZE: usize = T::ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(offset, bytes).await?; + yield_now().await; + Ok(()) + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + // Yield between each actual erase + for from in (from..to).step_by(T::ERASE_SIZE) { + let to = core::cmp::min(from + T::ERASE_SIZE as u32, to); + self.wrapped.erase(from, to).await?; + yield_now().await; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use embedded_storage_async::nor_flash::NorFlash; + + use super::*; + + extern crate std; + + #[derive(Default)] + struct FakeFlash(Vec<(u32, u32)>); + + impl embedded_storage::nor_flash::ErrorType for FakeFlash { + type Error = std::convert::Infallible; + } + + impl embedded_storage_async::nor_flash::ReadNorFlash for FakeFlash { + const READ_SIZE: usize = 1; + + async fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> { + unimplemented!() + } + + fn capacity(&self) -> usize { + unimplemented!() + } + } + + impl embedded_storage_async::nor_flash::NorFlash for FakeFlash { + const WRITE_SIZE: usize = 4; + const ERASE_SIZE: usize = 128; + + async fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> { + unimplemented!() + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.0.push((from, to)); + Ok(()) + } + } + + #[futures_test::test] + async fn can_erase() { + let fake = FakeFlash::default(); + let mut yielding = YieldingAsync::new(fake); + + yielding.erase(0, 256).await.unwrap(); + + let fake = yielding.wrapped; + assert_eq!(2, fake.0.len()); + assert_eq!((0, 128), fake.0[0]); + assert_eq!((128, 256), fake.0[1]); + } + + #[futures_test::test] + async fn can_erase_wrong_erase_size() { + let fake = FakeFlash::default(); + let mut yielding = YieldingAsync::new(fake); + + yielding.erase(0, 257).await.unwrap(); + + let fake = yielding.wrapped; + assert_eq!(3, fake.0.len()); + assert_eq!((0, 128), fake.0[0]); + assert_eq!((128, 256), fake.0[1]); + assert_eq!((256, 257), fake.0[2]); + } +}