diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..535cf3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/target +Cargo.lock + + +#Added by cargo +# +#already existing elements were commented out + +#/target +#Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b67b807 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "async-embedded-hal" +version = "0.0.1" +authors = ["Sjoerd Simons "] +edition = "2018" +license = "MIT OR Apache-2.0" + +[dependencies] +futures = "0.3" + diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..52cb453 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017-2018 Jorge Aparicio + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..17f1fd7 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# `async-embedded-hal` + +> A Async Hardware Abstraction Layer (HAL) for embedded systems + +This project is a POC. It's the async traits proposed for the embedded-hal[0] in +a stand-alone crate for experimentation + +0: https://github.com/rust-embedded/embedded-hal/pull/206 + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/src/gpio.rs b/src/gpio.rs new file mode 100644 index 0000000..266f259 --- /dev/null +++ b/src/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/gpio/get.rs b/src/gpio/get.rs new file mode 100644 index 0000000..c0d3d84 --- /dev/null +++ b/src/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/gpio/set.rs b/src/gpio/set.rs new file mode 100644 index 0000000..d910e2e --- /dev/null +++ b/src/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/i2c.rs b/src/i2c.rs new file mode 100644 index 0000000..861cf3b --- /dev/null +++ b/src/i2c.rs @@ -0,0 +1,182 @@ +//! Definitions for I²C peripherals. +use crate::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::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::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::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::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/i2c/begin_read.rs b/src/i2c/begin_read.rs new file mode 100644 index 0000000..def4387 --- /dev/null +++ b/src/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/i2c/begin_write.rs b/src/i2c/begin_write.rs new file mode 100644 index 0000000..baaf4d2 --- /dev/null +++ b/src/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/i2c/initialize.rs b/src/i2c/initialize.rs new file mode 100644 index 0000000..fa8fbc7 --- /dev/null +++ b/src/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/io.rs b/src/io.rs new file mode 100644 index 0000000..0b22d7e --- /dev/null +++ b/src/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 flush; +pub mod read; +pub mod read_exact; +pub mod shutdown; +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/io/flush.rs b/src/io/flush.rs new file mode 100644 index 0000000..866ccd9 --- /dev/null +++ b/src/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/io/read.rs b/src/io/read.rs new file mode 100644 index 0000000..a407cb2 --- /dev/null +++ b/src/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/io/read_exact.rs b/src/io/read_exact.rs new file mode 100644 index 0000000..0c3bb08 --- /dev/null +++ b/src/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/io/shutdown.rs b/src/io/shutdown.rs new file mode 100644 index 0000000..728002b --- /dev/null +++ b/src/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/io/write.rs b/src/io/write.rs new file mode 100644 index 0000000..c515502 --- /dev/null +++ b/src/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/io/write_all.rs b/src/io/write_all.rs new file mode 100644 index 0000000..c23783b --- /dev/null +++ b/src/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/lib.rs b/src/lib.rs new file mode 100644 index 0000000..19816ea --- /dev/null +++ b/src/lib.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/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..f5c1899 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,16 @@ +//! Re-exports of all of the async extension traits. +pub use crate::gpio::InputPinExt as _; +pub use crate::gpio::IntoFloatingInputPin as _; +pub use crate::gpio::IntoOpenDrainOutputPin as _; +pub use crate::gpio::IntoPullDownInputPin as _; +pub use crate::gpio::IntoPullUpInputPin as _; +pub use crate::gpio::IntoPushPullOutputPin as _; +pub use crate::gpio::OutputPinExt as _; +pub use crate::i2c::I2cBusMappingExt as _; +pub use crate::i2c::I2cReadExt as _; +pub use crate::i2c::I2cWriteExt as _; +pub use crate::io::ReadExt as _; +pub use crate::io::WriteExt as _; +pub use crate::timer::IntoOneshotTimer as _; +pub use crate::timer::IntoPeriodicTimer as _; +pub use crate::timer::TimerExt as _; diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000..369bb68 --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,8 @@ +//! Serial data transfer support. +use crate::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/spi.rs b/src/spi.rs new file mode 100644 index 0000000..6923793 --- /dev/null +++ b/src/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/spi/begin_transaction.rs b/src/spi/begin_transaction.rs new file mode 100644 index 0000000..d2135a0 --- /dev/null +++ b/src/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/spi/complete.rs b/src/spi/complete.rs new file mode 100644 index 0000000..de751a2 --- /dev/null +++ b/src/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/timer.rs b/src/timer.rs new file mode 100644 index 0000000..3db820a --- /dev/null +++ b/src/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/timer/start.rs b/src/timer/start.rs new file mode 100644 index 0000000..8e5eb29 --- /dev/null +++ b/src/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/timer/tick.rs b/src/timer/tick.rs new file mode 100644 index 0000000..9ef4d30 --- /dev/null +++ b/src/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/timer/ticks.rs b/src/timer/ticks.rs new file mode 100644 index 0000000..3fbd082 --- /dev/null +++ b/src/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) + } +}