diff --git a/Cargo.toml b/Cargo.toml index 66cef8cb4..68c01af2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,12 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.3" [dependencies] +futures = { version = "0.3.4", optional = true } nb = { version = "0.1.1", features = ["unstable"] } [dev-dependencies] stm32f3 = { version = "0.8", features = ["stm32f303", "rt"] } futures = "0.1.17" +[features] +asynchronous = ["futures"] diff --git a/src/asynchronous/gpio.rs b/src/asynchronous/gpio.rs new file mode 100644 index 000000000..266f259b2 --- /dev/null +++ b/src/asynchronous/gpio.rs @@ -0,0 +1,201 @@ +//! General input/output pins. +//! +//! The [`InputPin`] and [`OutputPin`] traits define pins that can be read and written digitally +//! (i.e. either in a low or high state). +//! +//! There are additionally various `Into*` traits that allow users to re-configure pins to switch +//! between different modes of operation, e.g. [`IntoFloatingInputPin`] turns a pin into an +//! [`InputPin`] that does not employ any pull-up or pull-down resistors. +use core::pin; +use core::task; + +pub mod get; +pub mod set; + +/// A generic pin that can't be interacted with. +pub trait Pin { + /// The common error type for all pin operations. + /// + /// A single error type for all operations is enforced for simplicity. + type Error; +} + +/// A pin that can be read from. +pub trait InputPin: Pin { + /// Polls a read operation of this pin to completion. + fn poll_get( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll>; +} + +/// Extension functions for instances of [`InputPin`]. +pub trait InputPinExt: InputPin { + /// Gets the current high or low state of this pin. + fn get(&mut self) -> get::Get + where + Self: Unpin, + { + get::get(self) + } +} + +impl InputPinExt for A where A: InputPin {} + +/// A pin that can be written to. +pub trait OutputPin: Pin { + /// Polls a write operation of this pin to completion. + fn poll_set( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + high: bool, + ) -> task::Poll>; +} + +/// Extension functions for instances of [`OutputPin`]. +pub trait OutputPinExt: OutputPin { + /// Sets the current high or low state of this pin. + fn set(&mut self, high: bool) -> set::Set + where + Self: Unpin, + { + set::set(self, high) + } +} + +impl OutputPinExt for A where A: OutputPin {} + +/// A pin that can be turned into an [`InputPin`] that does not employ any pull-up or pull-down +/// resistors. +pub trait IntoFloatingInputPin: Pin { + /// The type of an [`InputPin`] that does not employ any pull-up or pull-down resistors. + type FloatingInputPin: InputPin + Unpin; + + /// Attempts to re-configure this pin into the new mode. + fn into_floating_input_pin(self) -> Result; +} + +/// A pin that can be turned into an [`InputPin`] that has a pull-up resistor attached. +pub trait IntoPullUpInputPin: Pin { + /// The type of an [`InputPin`] that has a pull-up resistor attached. + type PullUpInputPin: InputPin + Unpin; + + /// Attempts to re-configure this pin into the new mode. + fn into_pull_up_input_pin(self) -> Result; +} + +/// A pin that can be turned into an [`InputPin`] that has a pull-down resistor attached. +pub trait IntoPullDownInputPin: Pin { + /// The type of an [`InputPin`] that has a pull-down resistor attached. + type PullDownInputPin: InputPin + Unpin; + + /// Attempts to re-configure this pin into the new mode. + fn into_pull_down_input_pin(self) -> Result; +} + +/// A pin that can be turned into an [`OutputPin`] that is in open drain mode. +pub trait IntoOpenDrainOutputPin: Pin { + /// The type of an [`OutputPin`] that is in open drain mode. + type OpenDrainOutputPin: OutputPin + Unpin; + + /// Attempts to re-configure this pin into the new mode. + fn into_open_drain_output_pin( + self, + initial_high: bool, + ) -> Result; +} + +/// A pin that can be turned into an [`OutputPin`] that is in push-pull mode. +pub trait IntoPushPullOutputPin: Pin { + /// The type of an [`OutputPin`] that is in push-pull mode. + type PushPullOutputPin: OutputPin + Unpin; + + /// Attempts to re-configure this pin into the new mode. + fn into_push_pull_output_pin( + self, + initial_high: bool, + ) -> Result; +} + +/// A virtual pin that is not actually connected to a physical pin. +/// +/// The pin will always read a fixed value, can be configured to be in any mode, and will always +/// have writes result in no-ops. +#[derive(Clone, Copy, Debug)] +pub struct NoConnect(bool); + +impl NoConnect { + /// Creates a new [`NoConnect`] that will always read the specified high/low value. + pub fn new(value: bool) -> Self { + NoConnect(value) + } +} + +impl Pin for NoConnect { + type Error = futures::never::Never; +} + +impl InputPin for NoConnect { + fn poll_get( + self: pin::Pin<&mut Self>, + _cx: &mut task::Context<'_>, + ) -> task::Poll> { + task::Poll::Ready(Ok(false)) + } +} + +impl OutputPin for NoConnect { + fn poll_set( + self: pin::Pin<&mut Self>, + _cx: &mut task::Context<'_>, + _high: bool, + ) -> task::Poll> { + task::Poll::Ready(Ok(())) + } +} + +impl IntoFloatingInputPin for NoConnect { + type FloatingInputPin = Self; + + fn into_floating_input_pin(self) -> Result { + Ok(self) + } +} + +impl IntoPullUpInputPin for NoConnect { + type PullUpInputPin = Self; + + fn into_pull_up_input_pin(self) -> Result { + Ok(self) + } +} + +impl IntoPullDownInputPin for NoConnect { + type PullDownInputPin = Self; + + fn into_pull_down_input_pin(self) -> Result { + Ok(self) + } +} + +impl IntoOpenDrainOutputPin for NoConnect { + type OpenDrainOutputPin = Self; + + fn into_open_drain_output_pin( + self, + _initial_high: bool, + ) -> Result { + Ok(self) + } +} + +impl IntoPushPullOutputPin for NoConnect { + type PushPullOutputPin = Self; + + fn into_push_pull_output_pin( + self, + _initial_high: bool, + ) -> Result { + Ok(self) + } +} diff --git a/src/asynchronous/gpio/get.rs b/src/asynchronous/gpio/get.rs new file mode 100644 index 000000000..c0d3d84f3 --- /dev/null +++ b/src/asynchronous/gpio/get.rs @@ -0,0 +1,34 @@ +//! Defines futures for getting the value off of a GPIO pin. +use core::future; +use core::pin; +use core::task; + +/// A future which reads the value off a GPIO pin. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Get<'a, A> +where + A: super::InputPin + Unpin + ?Sized, +{ + pin: &'a mut A, +} + +/// Creates a new [`Get`] for the provided GPIO pin. +pub fn get(pin: &mut A) -> Get +where + A: super::InputPin + Unpin + ?Sized, +{ + Get { pin } +} + +impl future::Future for Get<'_, A> +where + A: super::InputPin + Unpin + ?Sized, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.pin).poll_get(cx) + } +} diff --git a/src/asynchronous/gpio/set.rs b/src/asynchronous/gpio/set.rs new file mode 100644 index 000000000..d910e2e48 --- /dev/null +++ b/src/asynchronous/gpio/set.rs @@ -0,0 +1,36 @@ +//! Defines futures for setting the value of a GPIO pin. +use core::future; +use core::pin; +use core::task; + +/// A future which sets the value of a GPIO pin. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Set<'a, A> +where + A: super::OutputPin + Unpin + ?Sized, +{ + pin: &'a mut A, + high: bool, +} + +/// Creates a new [`Set`] for the provided GPIO pin, that, when polled, will drive it to the +/// specified high or low value. +pub fn set(pin: &mut A, high: bool) -> Set +where + A: super::OutputPin + Unpin + ?Sized, +{ + Set { pin, high } +} + +impl future::Future for Set<'_, A> +where + A: super::OutputPin + Unpin + ?Sized, +{ + type Output = Result<(), A::Error>; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.pin).poll_set(cx, this.high) + } +} diff --git a/src/asynchronous/i2c.rs b/src/asynchronous/i2c.rs new file mode 100644 index 000000000..c634f4a1b --- /dev/null +++ b/src/asynchronous/i2c.rs @@ -0,0 +1,182 @@ +//! Definitions for I²C peripherals. +use crate::asynchronous::io; +use core::fmt; +use core::pin; +use core::task; + +pub mod begin_read; +pub mod begin_write; +pub mod initialize; + +/// A peripheral that can perform I²C read operations. +// TODO: this should maybe capture the lifetime of self and let it flow into Self::Read +pub trait I2cRead: fmt::Debug { + /// The common error type for I²C read operations. + /// + /// A single error type for all operations is enforced for simplicity. + type Error: io::ReadError; + /// An object that can be used to complete the read operation. + type Read: io::Read + Unpin; + + /// Polls the start of a read operation to completion. + fn poll_begin_read( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + addr: u8, + ) -> task::Poll>; +} + +/// Extension functions for instances of [`I2cRead`]. +// TODO: this should maybe capture the lifetime of self and let it flow into Self::Read +pub trait I2cReadExt: I2cRead { + /// Initiates a read operation on the specified address. + /// + /// The returned object can be used to read the actual data from the address. The user must + /// read the data until completion, or else it might leave this I²C peripheral in an incomplete + /// state. + fn begin_read(&mut self, address: u8) -> begin_read::BeginRead + where + Self: Unpin, + { + begin_read::begin_read(self, address) + } +} + +impl<'r, A> I2cReadExt for A where A: I2cRead {} + +/// Reads from the specified address into the specified buffer. +/// +/// Returns the number of bytes read. +pub async fn read(i2c: &mut R, address: u8, dest: &mut [u8]) -> Result +where + R: I2cRead + Unpin, +{ + use crate::asynchronous::io::ReadExt; + + let mut reader = i2c.begin_read(address).await?; + let size = reader.read(dest).await?; + + Ok(size) +} + +/// Reads from the specified address into the specified buffer, waiting for the exact amount of +/// bytes to arrive. +pub async fn read_exact(i2c: &mut R, address: u8, dest: &mut [u8]) -> Result<(), R::Error> +where + R: I2cRead + Unpin, +{ + use crate::asynchronous::io::ReadExt; + + let mut reader = i2c.begin_read(address).await?; + reader.read_exact(dest).await?; + + Ok(()) +} + +/// A peripheral that can perform I²C write operations. +pub trait I2cWrite: fmt::Debug { + /// The common error type for I²C write operations. + /// + /// A single error type for all operations is enforced for simplicity. + type Error: io::WriteError; + /// An object that can be used to complete the write operation. + type Write: io::Write + Unpin; + + /// Polls the start of a write operation to completion. + fn poll_begin_write( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + addr: u8, + ) -> task::Poll>; +} + +/// Extension functions for instances of [`I2cWrite`]. +pub trait I2cWriteExt: I2cWrite { + /// Initiates a write operation on the specified address. + /// + /// The returned object can be used to write the actual data to the address. The user must call + /// `shutdown` when done writing, or else it might leave this I²C peripheral in an incomplete + /// state. For example, the I²C peripheral might decide to flush remaining data in the [`Drop`] + /// implementation, which will be blocking. + fn begin_write(&mut self, address: u8) -> begin_write::BeginWrite + where + Self: Unpin, + { + begin_write::begin_write(self, address) + } +} + +impl I2cWriteExt for A where A: I2cWrite {} + +/// Writes from the specified buffer to the specified address. +/// +/// Returns the number of bytes that were written. +pub async fn write(i2c: &mut W, address: u8, data: &[u8]) -> Result +where + W: I2cWrite + Unpin, +{ + use crate::asynchronous::io::WriteExt; + + let mut writer = i2c.begin_write(address).await?; + let size = writer.write(data).await?; + writer.shutdown().await?; + + Ok(size) +} + +/// Writes all of the bytes from the specified buffer to the specified address. +pub async fn write_all(i2c: &mut W, address: u8, data: &[u8]) -> Result<(), W::Error> +where + W: I2cWrite + Unpin, +{ + use crate::asynchronous::io::WriteExt; + + let mut writer = i2c.begin_write(address).await?; + writer.write_all(data).await?; + writer.shutdown().await?; + + Ok(()) +} + +/// Defines a mapping for two GPIO pins that can be used to create an I²C bus. +pub trait I2cBusMapping { + /// The common error type for I²C operations. + /// + /// A single error type for all operations is enforced for simplicity. + type Error: io::ReadError + io::WriteError; + /// The I²C bus that will be produced once initialization based off of this mapping succeeds. + type Bus: I2cRead + I2cWrite; + + /// Polls the initialization operation to completion. + fn poll_initialize( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + sda: &mut SDA, + scl: &mut SCL, + ) -> task::Poll> + where + Self: Sized; +} + +/// Extension functions for instances of [`I2cBusMapping`]. +pub trait I2cBusMappingExt: I2cBusMapping +where + SDA: Unpin, + SCL: Unpin, +{ + /// Initializes a new I²C bus based off of the two provided SDA (data) and SCL (clock) pins. + fn initialize(self, sda: SDA, scl: SCL) -> initialize::Initialize + where + Self: Sized + Unpin, + { + initialize::initialize(self, sda, scl) + } +} + +impl I2cBusMappingExt for A +where + A: I2cBusMapping, + SDA: Unpin, + SCL: Unpin, +{ +} diff --git a/src/asynchronous/i2c/begin_read.rs b/src/asynchronous/i2c/begin_read.rs new file mode 100644 index 000000000..def4387bc --- /dev/null +++ b/src/asynchronous/i2c/begin_read.rs @@ -0,0 +1,37 @@ +//! Defines futures for initiating reads from an I²C peripheral. +use core::future; +use core::pin; +use core::task; + +/// A future which initializes reads from an I²C peripheral. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct BeginRead<'a, A> +where + A: super::I2cRead + Unpin + ?Sized, +{ + reader: &'a mut A, + address: u8, +} + +/// Creates a new [`BeginRead`] for the provided I²C peripheral. +/// +/// The read will access the specified address. +pub fn begin_read(reader: &mut A, address: u8) -> BeginRead +where + A: super::I2cRead + Unpin + ?Sized, +{ + BeginRead { reader, address } +} + +impl future::Future for BeginRead<'_, A> +where + A: super::I2cRead + Unpin + ?Sized, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.reader).poll_begin_read(cx, this.address) + } +} diff --git a/src/asynchronous/i2c/begin_write.rs b/src/asynchronous/i2c/begin_write.rs new file mode 100644 index 000000000..baaf4d259 --- /dev/null +++ b/src/asynchronous/i2c/begin_write.rs @@ -0,0 +1,37 @@ +//! Defines futures for initiating writes to an I²C peripheral. +use core::future; +use core::pin; +use core::task; + +/// A future which initializes writes to an I²C peripheral. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct BeginWrite<'a, A> +where + A: super::I2cWrite + Unpin + ?Sized, +{ + writer: &'a mut A, + address: u8, +} + +/// Creates a new [`BeginWrite`] for the provided I²C peripheral. +/// +/// The write will access the specified address. +pub fn begin_write(writer: &mut A, address: u8) -> BeginWrite +where + A: super::I2cWrite + Unpin + ?Sized, +{ + BeginWrite { writer, address } +} + +impl future::Future for BeginWrite<'_, A> +where + A: super::I2cWrite + Unpin + ?Sized, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.writer).poll_begin_write(cx, this.address) + } +} diff --git a/src/asynchronous/i2c/initialize.rs b/src/asynchronous/i2c/initialize.rs new file mode 100644 index 000000000..fa8fbc755 --- /dev/null +++ b/src/asynchronous/i2c/initialize.rs @@ -0,0 +1,42 @@ +//! Defines futures for initializing an I²C peripheral based off of GPIO pins. +use core::future; +use core::pin; +use core::task; + +/// A future which initializes an I²C peripheral based off of GPIO pins. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Initialize +where + A: super::I2cBusMapping + Unpin, + SDA: Unpin, + SCL: Unpin, +{ + mapping: A, + sda: SDA, + scl: SCL, +} + +/// Creates a new [`Initialize`] based off of a I²C bus pin mapping, as well as an SDA and SCL pin. +pub fn initialize(mapping: A, sda: SDA, scl: SCL) -> Initialize +where + A: super::I2cBusMapping + Unpin, + SDA: Unpin, + SCL: Unpin, +{ + Initialize { mapping, sda, scl } +} + +impl future::Future for Initialize +where + A: super::I2cBusMapping + Unpin, + SDA: Unpin, + SCL: Unpin, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut this.mapping).poll_initialize(cx, &mut this.sda, &mut this.scl) + } +} diff --git a/src/asynchronous/io.rs b/src/asynchronous/io.rs new file mode 100644 index 000000000..f3f3f287b --- /dev/null +++ b/src/asynchronous/io.rs @@ -0,0 +1,155 @@ +//! Common IO primitives. +//! +//! These primitives are closely mirroring the definitions in +//! [`tokio-io`](https://docs.rs/tokio-io). A big difference is that these definitions are not tied +//! to `std::io::Error`, but instead allow for custom error types, and also don't require +//! allocation. + +use core::fmt; +use core::pin; +use core::task; + +pub mod shutdown; +pub mod flush; +pub mod read; +pub mod read_exact; +pub mod write; +pub mod write_all; + +/// Reads bytes from a source. +pub trait Read: fmt::Debug { + /// The type of error that can occur during a read operation. + type Error: ReadError; + + /// Attempts to read into the provided buffer. + /// + /// On success, returns the number of bytes read. + fn poll_read( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + buffer: &mut [u8], + ) -> task::Poll>; +} + +/// An error that might arise from read operations. +pub trait ReadError: fmt::Debug { + /// Creates an error that indicates an EOF (end-of-file) condition. + /// + /// This condition is to be used when there is not enough data available to satisfy a read + /// operation. + fn eof() -> Self; +} + +/// Writes bytes to a source. +pub trait Write: fmt::Debug { + /// The type of error that can occur during a write operation. + type Error: WriteError; + + /// Attempts to write the contents of the provided buffer. + /// + /// On success, returns the number of bytes written. + fn poll_write( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + bytes: &[u8], + ) -> task::Poll>; + + /// Attempts to flush the object, ensuring that any buffered data reach their destination. + fn poll_flush( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll>; + + /// Initiates or attempts to shut down this writer, returning success when the I/O connection + /// has completely shut down. + fn poll_shutdown( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll>; +} + +impl Write for &mut A { + type Error = A::Error; + + fn poll_write( + mut self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + bytes: &[u8], + ) -> task::Poll> { + pin::Pin::new(&mut **self).poll_write(cx, bytes) + } + + fn poll_flush( + mut self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll> { + pin::Pin::new(&mut **self).poll_flush(cx) + } + + fn poll_shutdown( + mut self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll> { + pin::Pin::new(&mut **self).poll_shutdown(cx) + } +} + +/// An error that might arise from write operations. +pub trait WriteError: fmt::Debug { + /// Creates an error that indicates a zero-write condition. + /// + /// This condition is to be used when a write operation requires more bytes to be written, but + /// an attempt to write returned zero bytes, indicating that there's no more room for bytes + /// to be written. + fn write_zero() -> Self; +} + +/// Utility methods for types implementing [`Read`]. +pub trait ReadExt: Read { + /// Reads data into the specified buffer, returning the number of bytes written. + fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> read::Read<'a, Self> + where + Self: Unpin, + { + read::read(self, buffer) + } + + /// Reads data into the specified buffer, until the buffer is filled. + fn read_exact<'a>(&'a mut self, buffer: &'a mut [u8]) -> read_exact::ReadExact<'a, Self> + where + Self: Unpin, + { + read_exact::read_exact(self, buffer) + } +} + +impl ReadExt for A where A: Read {} + +/// Utility methods for types implementing [`Write`]. +pub trait WriteExt: Write { + /// Writes data from the specified buffer, returning the number of bytes written. + fn write<'a>(&'a mut self, bytes: &'a [u8]) -> write::Write<'a, Self> + where + Self: Unpin, + { + write::write(self, bytes) + } + + /// Writes data from the specified buffer until all bytes are written. + fn write_all<'a>(&'a mut self, bytes: &'a [u8]) -> write_all::WriteAll<'a, Self> + where + Self: Unpin, + { + write_all::write_all(self, bytes) + } + + /// Shuts down this writer. + fn shutdown(&mut self) -> shutdown::Shutdown + where + Self: Unpin, + { + shutdown::shutdown(self) + } +} + +impl WriteExt for A where A: Write {} diff --git a/src/asynchronous/io/flush.rs b/src/asynchronous/io/flush.rs new file mode 100644 index 000000000..866ccd9e3 --- /dev/null +++ b/src/asynchronous/io/flush.rs @@ -0,0 +1,31 @@ +//! Defines futures for flushing write operations. +use core::future; +use core::pin; +use core::task; + +/// A future that ensures that a write operation is flushed. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Flush<'a, A: ?Sized> { + writer: &'a mut A, +} + +/// Creates a new [`Flush`] for the provided writer. +pub fn flush(writer: &mut A) -> Flush +where + A: super::Write + Unpin + ?Sized, +{ + Flush { writer } +} + +impl future::Future for Flush<'_, A> +where + A: super::Write + Unpin + ?Sized, +{ + type Output = Result<(), A::Error>; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.writer).poll_flush(cx) + } +} diff --git a/src/asynchronous/io/read.rs b/src/asynchronous/io/read.rs new file mode 100644 index 000000000..a407cb2ac --- /dev/null +++ b/src/asynchronous/io/read.rs @@ -0,0 +1,41 @@ +//! Defines futures for read operations. +use core::future; +use core::pin; +use core::task; + +/// A future that ensures that a read operation is performed. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Read<'a, A> +where + A: super::Read + Unpin + ?Sized, +{ + reader: &'a mut A, + buffer: &'a mut [u8], + position: usize, +} + +/// Creates a new [`Read`] for the provided reader. +pub fn read<'a, A>(reader: &'a mut A, buffer: &'a mut [u8]) -> Read<'a, A> +where + A: super::Read + Unpin + ?Sized, +{ + let position = 0; + Read { + reader, + buffer, + position, + } +} + +impl future::Future for Read<'_, A> +where + A: super::Read + Unpin + ?Sized, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.reader).poll_read(cx, this.buffer) + } +} diff --git a/src/asynchronous/io/read_exact.rs b/src/asynchronous/io/read_exact.rs new file mode 100644 index 000000000..0c3bb089b --- /dev/null +++ b/src/asynchronous/io/read_exact.rs @@ -0,0 +1,57 @@ +//! Defines futures for exact read operations. +use core::future; +use core::pin; +use core::task; + +/// A future that ensures that an exact read operation is performed. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct ReadExact<'a, A> +where + A: super::Read + Unpin + ?Sized, +{ + reader: &'a mut A, + buffer: &'a mut [u8], + position: usize, +} + +/// Creates a new [`ReadExact`] for the provided reader. +pub fn read_exact<'a, A>(reader: &'a mut A, buffer: &'a mut [u8]) -> ReadExact<'a, A> +where + A: super::Read + Unpin + ?Sized, +{ + let position = 0; + ReadExact { + reader, + buffer, + position, + } +} + +impl future::Future for ReadExact<'_, A> +where + A: super::Read + Unpin + ?Sized, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + use super::ReadError; + + loop { + // if our buffer is empty, then we need to read some data to continue. + if self.position < self.buffer.len() { + let this = &mut *self; + let n = futures::ready!(pin::Pin::new(&mut *this.reader) + .poll_read(cx, &mut this.buffer[this.position..]))?; + this.position += n; + if n == 0 { + return Err(A::Error::eof()).into(); + } + } + + if self.position >= self.buffer.len() { + return task::Poll::Ready(Ok(self.position)); + } + } + } +} diff --git a/src/asynchronous/io/shutdown.rs b/src/asynchronous/io/shutdown.rs new file mode 100644 index 000000000..728002bc8 --- /dev/null +++ b/src/asynchronous/io/shutdown.rs @@ -0,0 +1,31 @@ +//! Defines futures for shutting down write operations. +use core::future; +use core::pin; +use core::task; + +/// A future that ensures that a write operation is shut down. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Shutdown<'a, A: ?Sized> { + writer: &'a mut A, +} + +/// Creates a new [`Shutdown`] for the provided writer. +pub fn shutdown(writer: &mut A) -> Shutdown +where + A: super::Write + Unpin + ?Sized, +{ + Shutdown { writer } +} + +impl future::Future for Shutdown<'_, A> +where + A: super::Write + Unpin + ?Sized, +{ + type Output = Result<(), A::Error>; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.writer).poll_shutdown(cx) + } +} diff --git a/src/asynchronous/io/write.rs b/src/asynchronous/io/write.rs new file mode 100644 index 000000000..c5155022e --- /dev/null +++ b/src/asynchronous/io/write.rs @@ -0,0 +1,32 @@ +//! Defines futures for write operations. +use core::future; +use core::pin; +use core::task; + +/// A future that ensures that a write operation is performed. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Write<'a, A: ?Sized> { + writer: &'a mut A, + buf: &'a [u8], +} + +/// Creates a new [`Write`] for the provided writer. +pub fn write<'a, A>(writer: &'a mut A, buf: &'a [u8]) -> Write<'a, A> +where + A: super::Write + Unpin + ?Sized, +{ + Write { writer, buf } +} + +impl future::Future for Write<'_, A> +where + A: super::Write + Unpin + ?Sized, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.writer).poll_write(cx, this.buf) + } +} diff --git a/src/asynchronous/io/write_all.rs b/src/asynchronous/io/write_all.rs new file mode 100644 index 000000000..c23783b09 --- /dev/null +++ b/src/asynchronous/io/write_all.rs @@ -0,0 +1,47 @@ +//! Defines futures for complete write operations. +use core::future; +use core::mem; +use core::pin; +use core::task; + +/// A future that ensures that a complete write operation is performed. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct WriteAll<'a, A: ?Sized> { + writer: &'a mut A, + buffer: &'a [u8], +} + +/// Creates a new [`WriteAll`] for the provided writer. +pub fn write_all<'a, A>(writer: &'a mut A, buffer: &'a [u8]) -> WriteAll<'a, A> +where + A: super::Write + Unpin + ?Sized, +{ + WriteAll { writer, buffer } +} + +impl future::Future for WriteAll<'_, A> +where + A: super::Write + Unpin + ?Sized, +{ + type Output = Result<(), A::Error>; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + use super::Write; + use super::WriteError; + + let this = &mut *self; + while !this.buffer.is_empty() { + let n = futures::ready!(pin::Pin::new(&mut this.writer).poll_write(cx, this.buffer))?; + { + let (_, rest) = mem::replace(&mut this.buffer, &[]).split_at(n); + this.buffer = rest; + } + if n == 0 { + return task::Poll::Ready(Err(A::Error::write_zero())); + } + } + + task::Poll::Ready(Ok(())) + } +} diff --git a/src/asynchronous/mod.rs b/src/asynchronous/mod.rs new file mode 100644 index 000000000..19816eadb --- /dev/null +++ b/src/asynchronous/mod.rs @@ -0,0 +1,12 @@ +//! Asynchronous versions of HAL support. +//! +//! This module uses the built-in Rust language support for asynchronous programming. +//! +//! This module is unfortunately not called `async`, because that's a reserved keyword. +pub mod gpio; +pub mod i2c; +pub mod io; +pub mod prelude; +pub mod serial; +pub mod spi; +pub mod timer; diff --git a/src/asynchronous/prelude.rs b/src/asynchronous/prelude.rs new file mode 100644 index 000000000..315616655 --- /dev/null +++ b/src/asynchronous/prelude.rs @@ -0,0 +1,16 @@ +//! Re-exports of all of the async extension traits. +pub use crate::asynchronous::gpio::InputPinExt as _; +pub use crate::asynchronous::gpio::IntoFloatingInputPin as _; +pub use crate::asynchronous::gpio::IntoOpenDrainOutputPin as _; +pub use crate::asynchronous::gpio::IntoPullDownInputPin as _; +pub use crate::asynchronous::gpio::IntoPullUpInputPin as _; +pub use crate::asynchronous::gpio::IntoPushPullOutputPin as _; +pub use crate::asynchronous::gpio::OutputPinExt as _; +pub use crate::asynchronous::i2c::I2cBusMappingExt as _; +pub use crate::asynchronous::i2c::I2cReadExt as _; +pub use crate::asynchronous::i2c::I2cWriteExt as _; +pub use crate::asynchronous::io::ReadExt as _; +pub use crate::asynchronous::io::WriteExt as _; +pub use crate::asynchronous::timer::IntoOneshotTimer as _; +pub use crate::asynchronous::timer::IntoPeriodicTimer as _; +pub use crate::asynchronous::timer::TimerExt as _; diff --git a/src/asynchronous/serial.rs b/src/asynchronous/serial.rs new file mode 100644 index 000000000..0ec097dee --- /dev/null +++ b/src/asynchronous/serial.rs @@ -0,0 +1,8 @@ +//! Serial data transfer support. +use crate::asynchronous::io; + +/// A peripheral that can perform serial read operations. +pub trait SerialRead: io::Read {} + +/// A peripheral that can perform serial write operations. +pub trait SerialWrite: io::Write {} diff --git a/src/asynchronous/spi.rs b/src/asynchronous/spi.rs new file mode 100644 index 000000000..69237936c --- /dev/null +++ b/src/asynchronous/spi.rs @@ -0,0 +1,85 @@ +//! SPI bus support. +use core::fmt; +use core::pin; +use core::task; + +pub mod begin_transaction; +pub mod complete; + +/// A peripheral that supports performing SPI operations. +pub trait Spi: fmt::Debug { + /// The type of error that can occur while performing SPI operations. + type Error; + /// The associated SPI transaction type. + type Transaction: SpiTransaction; + + /// Initiates a SPI transaction. + /// + /// This usually involves pulling down a chip select pin when operating in master mode, or + /// waiting for a chip select pin to be pulled low when operating in slave mode. + fn poll_begin_transaction( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll>; +} + +/// Utility methods for types implementing [`Spi`]. +pub trait SpiExt: Spi { + /// Initiates a SPI transaction. + /// + /// This usually involves pulling down a chip select pin when operating in master mode, or + /// waiting for a chip select pin to be pulled low when operating in slave mode. + fn begin_transaction(&mut self) -> begin_transaction::BeginTransaction + where + Self: Unpin, + { + begin_transaction::begin_transaction(self) + } +} + +impl SpiExt for A where A: Spi {} + +/// A SPI transaction, that allows for transferring data until the transaction is dropped. +pub trait SpiTransaction: fmt::Debug { + /// The type of error that can occur while performing SPI operations. + type Error; + /// The type of transfer used for the `transfer` method. + type Transfer: SpiTransfer; + /// The type of transfer used for the `transfer_split` method. + type TransferSplit: SpiTransfer; + + /// Initiates a new transfer of data where the same buffer is used both for sending and + /// receiving data. + fn transfer(&mut self, buffer: &mut [u8]) -> Result; + + /// Initiates a new transfer with a separate sending and receiving buffer. + fn transfer_split( + &mut self, + tx_buffer: &[u8], + rx_buffer: &mut [u8], + ) -> Result; +} + +/// A SPI transfer that is in progress. +pub trait SpiTransfer { + /// The type of error that can occur while performing SPI operations. + type Error; + /// Complete this transfer, meaning that the transfer is completely done. + fn poll_complete( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll>; +} + +/// Utility methods for types implementing [`SpiTransfer`]. +pub trait SpiTransferExt: SpiTransfer { + /// Complete this transfer, meaning that the transfer is completely done. + fn complete(&mut self) -> complete::Complete + where + Self: Unpin, + { + complete::complete(self) + } +} + +impl SpiTransferExt for T where T: SpiTransfer {} diff --git a/src/asynchronous/spi/begin_transaction.rs b/src/asynchronous/spi/begin_transaction.rs new file mode 100644 index 000000000..d2135a0db --- /dev/null +++ b/src/asynchronous/spi/begin_transaction.rs @@ -0,0 +1,34 @@ +//! Defines futures for initiating a SPI transaction. +use core::future; +use core::pin; +use core::task; + +/// A future which begins a SPI transaction. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct BeginTransaction<'a, A> +where + A: super::Spi + Unpin + ?Sized, +{ + spi: &'a mut A, +} + +/// Creates a new [`BeginTransaction`] for the provided SPI instance. +pub fn begin_transaction(spi: &mut A) -> BeginTransaction +where + A: super::Spi + Unpin + ?Sized, +{ + BeginTransaction { spi } +} + +impl future::Future for BeginTransaction<'_, A> +where + A: super::Spi + Unpin + ?Sized, +{ + type Output = Result; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.spi).poll_begin_transaction(cx) + } +} diff --git a/src/asynchronous/spi/complete.rs b/src/asynchronous/spi/complete.rs new file mode 100644 index 000000000..de751a2f7 --- /dev/null +++ b/src/asynchronous/spi/complete.rs @@ -0,0 +1,31 @@ +//! Defines futures for completing a SPI transfer. +use core::future; +use core::pin; +use core::task; + +/// A future which completes a SPI transfer. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Complete<'a, A: ?Sized> { + transfer: &'a mut A, +} + +/// Creates a new [`Complete`] for the provided SPI transfer. +pub fn complete(transfer: &mut A) -> Complete +where + A: super::SpiTransfer + Unpin + ?Sized, +{ + Complete { transfer } +} + +impl<'a, A> future::Future for Complete<'a, A> +where + A: super::SpiTransfer + Unpin + ?Sized, +{ + type Output = Result<(), A::Error>; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.transfer).poll_complete(cx) + } +} diff --git a/src/asynchronous/timer.rs b/src/asynchronous/timer.rs new file mode 100644 index 000000000..3db820ae9 --- /dev/null +++ b/src/asynchronous/timer.rs @@ -0,0 +1,78 @@ +//! Timer support. +use core::fmt; +use core::pin; +use core::task; + +pub mod start; +pub mod tick; +pub mod ticks; + +/// A timer peripheral. +pub trait Timer: fmt::Debug { + /// The type of error that can emerge from timer operations. + type Error; + + /// Starts the timer, meaning that ticks will start being produced. + fn poll_start( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll>; + + /// Awaits the next tick of the timer. + fn poll_tick( + self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll>; +} + +/// Utility methods for types implementing [`Timer`]. +pub trait TimerExt: Timer { + /// Starts the timer, meaning that ticks will start being produced. + fn start(&mut self) -> start::Start + where + Self: Unpin, + { + start::start(self) + } + + /// Awaits the next tick of the timer. + fn tick(&mut self) -> tick::Tick + where + Self: Unpin, + { + tick::tick(self) + } + + /// A stream of all the ticks produced by this timer, which for unbounded timers implies an + /// infinite stream of ticks. + fn ticks(&mut self) -> ticks::Ticks + where + Self: Unpin, + { + ticks::ticks(self) + } +} + +impl TimerExt for T where T: Timer {} + +/// A timer that can be ran in a periodic mode. +pub trait IntoPeriodicTimer: Timer { + /// The version of this timer that runs in a periodic mode. + type PeriodicTimer: Timer + Unpin; + /// The type for measuring the periodic tick rate; usually this is a type measuring Hertz (Hz). + type Rate; + + /// Re-configures this timer to be periodic. + fn into_periodic_timer(self, period: Self::Rate) -> Result; +} + +/// A timer that can be ran in a one shot mode. +pub trait IntoOneshotTimer: Timer { + /// The version of this timer that runs in a one shot mode. + type OneshotTimer: Timer + Unpin; + /// The type for measuring the one shot duration; usually this is a type measuring seconds. + type Duration; + + /// Re-configures this timer to be a one shot timer. + fn into_oneshot_timer(self, delay: Self::Duration) -> Result; +} diff --git a/src/asynchronous/timer/start.rs b/src/asynchronous/timer/start.rs new file mode 100644 index 000000000..8e5eb29d2 --- /dev/null +++ b/src/asynchronous/timer/start.rs @@ -0,0 +1,34 @@ +//! Defines futures for starting a timer. +use core::future; +use core::pin; +use core::task; + +/// A future which starts a timer. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Start<'a, A> +where + A: super::Timer + Unpin + ?Sized, +{ + timer: &'a mut A, +} + +/// Creates a new [`Start`] for the provided timer. +pub fn start(timer: &mut A) -> Start +where + A: super::Timer + Unpin + ?Sized, +{ + Start { timer } +} + +impl future::Future for Start<'_, A> +where + A: super::Timer + Unpin + ?Sized, +{ + type Output = Result<(), A::Error>; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.timer).poll_start(cx) + } +} diff --git a/src/asynchronous/timer/tick.rs b/src/asynchronous/timer/tick.rs new file mode 100644 index 000000000..9ef4d3059 --- /dev/null +++ b/src/asynchronous/timer/tick.rs @@ -0,0 +1,34 @@ +//! Defines futures for awaiting a single timer tick. +use core::future; +use core::pin; +use core::task; + +/// A future that awaits the next timer tick for a timer. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Tick<'a, A> +where + A: super::Timer + Unpin + ?Sized, +{ + timer: &'a mut A, +} + +/// Creates a new [`Tick`] for the provided timer. +pub fn tick(timer: &mut A) -> Tick +where + A: super::Timer + Unpin + ?Sized, +{ + Tick { timer } +} + +impl future::Future for Tick<'_, A> +where + A: super::Timer + Unpin + ?Sized, +{ + type Output = Result<(), A::Error>; + + fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + let this = &mut *self; + pin::Pin::new(&mut *this.timer).poll_tick(cx) + } +} diff --git a/src/asynchronous/timer/ticks.rs b/src/asynchronous/timer/ticks.rs new file mode 100644 index 000000000..3fbd08210 --- /dev/null +++ b/src/asynchronous/timer/ticks.rs @@ -0,0 +1,36 @@ +//! Defines streams for all the ticks of a timer. +use core::pin; +use core::task; + +/// A stream of ticks emitted by a timer, possibly infinite. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Ticks<'a, A> +where + A: super::Timer + Unpin + ?Sized, +{ + timer: &'a mut A, +} + +/// Creates a new [`Ticks`] for the provided timer. +pub fn ticks(timer: &mut A) -> Ticks +where + A: super::Timer + Unpin + ?Sized, +{ + Ticks { timer } +} + +impl futures::stream::Stream for Ticks<'_, A> +where + A: super::Timer + Unpin + ?Sized, +{ + type Item = Result<(), A::Error>; + + fn poll_next( + mut self: pin::Pin<&mut Self>, + cx: &mut task::Context<'_>, + ) -> task::Poll> { + let this = &mut *self; + pin::Pin::new(&mut *this.timer).poll_tick(cx).map(Some) + } +} diff --git a/src/lib.rs b/src/lib.rs index 9e04139ff..b8c18b12e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -691,6 +691,8 @@ pub mod blocking; pub mod capture; pub mod digital; pub mod fmt; +#[cfg(feature = "asynchronous")] +pub mod asynchronous; pub mod prelude; pub mod pwm; pub mod qei;