From cd1bf31fedbd33170507245eef1f7ae576aa3557 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 22 May 2023 16:48:31 +0200 Subject: Add YieldingAsync adapter --- embassy-embedded-hal/src/adapter.rs | 248 --------------------- embassy-embedded-hal/src/adapter/blocking_async.rs | 173 ++++++++++++++ embassy-embedded-hal/src/adapter/mod.rs | 5 + embassy-embedded-hal/src/adapter/yielding_async.rs | 232 +++++++++++++++++++ 4 files changed, 410 insertions(+), 248 deletions(-) delete mode 100644 embassy-embedded-hal/src/adapter.rs create mode 100644 embassy-embedded-hal/src/adapter/blocking_async.rs create mode 100644 embassy-embedded-hal/src/adapter/mod.rs create mode 100644 embassy-embedded-hal/src/adapter/yielding_async.rs (limited to 'embassy-embedded-hal/src') diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs deleted file mode 100644 index 169aad5e3..000000000 --- a/embassy-embedded-hal/src/adapter.rs +++ /dev/null @@ -1,248 +0,0 @@ -//! Adapters between embedded-hal traits. - -use embassy_futures::yield_now; -use embedded_hal_02::{blocking, serial}; - -/// Wrapper that implements async traits using blocking implementations. -/// -/// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations. -/// -/// BlockingAsync will implement any async trait that maps to embedded-hal traits implemented for the wrapped driver. -/// -/// Driver users are then free to choose which implementation that is available to them. -pub struct BlockingAsync { - wrapped: T, -} - -impl BlockingAsync { - /// Create a new instance of a wrapper for a given peripheral. - pub fn new(wrapped: T) -> Self { - Self { wrapped } - } -} - -// -// I2C implementations -// -impl embedded_hal_1::i2c::ErrorType for BlockingAsync -where - E: embedded_hal_1::i2c::Error + 'static, - T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, -{ - type Error = E; -} - -impl embedded_hal_async::i2c::I2c for BlockingAsync -where - E: embedded_hal_1::i2c::Error + 'static, - T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, -{ - async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.wrapped.read(address, read) - } - - async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.wrapped.write(address, write) - } - - async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.wrapped.write_read(address, write, read) - } - - async fn transaction( - &mut self, - address: u8, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() - } -} - -// -// SPI implementatinos -// - -impl embedded_hal_async::spi::ErrorType for BlockingAsync -where - E: embedded_hal_1::spi::Error, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - type Error = E; -} - -impl embedded_hal_async::spi::SpiBus for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { - // Ensure we write the expected bytes - for i in 0..core::cmp::min(read.len(), write.len()) { - read[i] = write[i].clone(); - } - self.wrapped.transfer(read)?; - Ok(()) - } - - async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> { - todo!() - } -} - -impl embedded_hal_async::spi::SpiBusFlush for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusWrite for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { - self.wrapped.write(data)?; - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusRead for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { - self.wrapped.transfer(data)?; - Ok(()) - } -} - -// Uart implementatinos -impl embedded_hal_1::serial::ErrorType for BlockingAsync -where - T: serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type Error = E; -} - -/// NOR flash wrapper -use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; -use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; - -impl ErrorType for BlockingAsync -where - T: ErrorType, -{ - type Error = T::Error; -} - -impl AsyncNorFlash for BlockingAsync -where - T: NorFlash, -{ - const WRITE_SIZE: usize = ::WRITE_SIZE; - 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(()) - } - - 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(()) - } -} - -impl AsyncReadNorFlash for BlockingAsync -where - T: ReadNorFlash, -{ - const READ_SIZE: usize = ::READ_SIZE; - async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> { - self.wrapped.read(address, data) - } - - fn capacity(&self) -> usize { - 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/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs new file mode 100644 index 000000000..171ff6c9f --- /dev/null +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -0,0 +1,173 @@ +//! Adapters between embedded-hal traits. + +use embedded_hal_02::{blocking, serial}; + +/// Wrapper that implements async traits using blocking implementations. +/// +/// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations. +/// +/// BlockingAsync will implement any async trait that maps to embedded-hal traits implemented for the wrapped driver. +/// +/// Driver users are then free to choose which implementation that is available to them. +pub struct BlockingAsync { + wrapped: T, +} + +impl BlockingAsync { + /// Create a new instance of a wrapper for a given peripheral. + pub fn new(wrapped: T) -> Self { + Self { wrapped } + } +} + +// +// I2C implementations +// +impl embedded_hal_1::i2c::ErrorType for BlockingAsync +where + E: embedded_hal_1::i2c::Error + 'static, + T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, +{ + type Error = E; +} + +impl embedded_hal_async::i2c::I2c for BlockingAsync +where + E: embedded_hal_1::i2c::Error + 'static, + T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, +{ + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, read) + } + + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(address, write) + } + + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.write_read(address, write, read) + } + + async fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + let _ = address; + let _ = operations; + todo!() + } +} + +// +// SPI implementatinos +// + +impl embedded_hal_async::spi::ErrorType for BlockingAsync +where + E: embedded_hal_1::spi::Error, + T: blocking::spi::Transfer + blocking::spi::Write, +{ + type Error = E; +} + +impl embedded_hal_async::spi::SpiBus for BlockingAsync +where + E: embedded_hal_1::spi::Error + 'static, + T: blocking::spi::Transfer + blocking::spi::Write, +{ + async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + // Ensure we write the expected bytes + for i in 0..core::cmp::min(read.len(), write.len()) { + read[i] = write[i].clone(); + } + self.wrapped.transfer(read)?; + Ok(()) + } + + async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> { + todo!() + } +} + +impl embedded_hal_async::spi::SpiBusFlush for BlockingAsync +where + E: embedded_hal_1::spi::Error + 'static, + T: blocking::spi::Transfer + blocking::spi::Write, +{ + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusWrite for BlockingAsync +where + E: embedded_hal_1::spi::Error + 'static, + T: blocking::spi::Transfer + blocking::spi::Write, +{ + async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(data)?; + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusRead for BlockingAsync +where + E: embedded_hal_1::spi::Error + 'static, + T: blocking::spi::Transfer + blocking::spi::Write, +{ + async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer(data)?; + Ok(()) + } +} + +// Uart implementatinos +impl embedded_hal_1::serial::ErrorType for BlockingAsync +where + T: serial::Read, + E: embedded_hal_1::serial::Error + 'static, +{ + type Error = E; +} + +/// NOR flash wrapper +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +impl ErrorType for BlockingAsync +where + T: ErrorType, +{ + type Error = T::Error; +} + +impl AsyncNorFlash for BlockingAsync +where + T: NorFlash, +{ + const WRITE_SIZE: usize = ::WRITE_SIZE; + const ERASE_SIZE: usize = ::ERASE_SIZE; + + async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(offset, data) + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.wrapped.erase(from, to) + } +} + +impl AsyncReadNorFlash for BlockingAsync +where + T: ReadNorFlash, +{ + const READ_SIZE: usize = ::READ_SIZE; + async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, data) + } + + fn capacity(&self) -> usize { + self.wrapped.capacity() + } +} diff --git a/embassy-embedded-hal/src/adapter/mod.rs b/embassy-embedded-hal/src/adapter/mod.rs new file mode 100644 index 000000000..787ac2979 --- /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 000000000..96d5cca8e --- /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]); + } +} -- cgit