diff --git a/embedded-hal-async/src/adc.rs b/embedded-hal-async/src/adc.rs new file mode 100644 index 00000000..3bbc295a --- /dev/null +++ b/embedded-hal-async/src/adc.rs @@ -0,0 +1,38 @@ +//! Asynchronous Analog to Digital conversion. +//! +//! See the [blocking](embedded_hal::adc) types documentation for more information. + +use core::future::Future; + +use embedded_hal::adc::{Channel, ErrorType}; + +/// Asynchronous ADC channel capable of a one shot measurement. +pub trait OneShotChannel: Channel + ErrorType { + /// The maximum value that may be produced by the ADC. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the max value is obtained. Some implementations such + /// as a channel connected over I2C may return [`Pending`](core::task::Poll::Pending) while the max value is + /// being obtained. + /// + /// This is guaranteed to not change once a channel is constructed. + /// + /// For example, a 12-bit ADC would return 4095. + async fn max_value(&self) -> Result; + + /// Sample from the ADC. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the sample is complete. + async fn sample(&mut self) -> Result; +} + +impl OneShotChannel for &mut T { + #[inline] + fn max_value(&self) -> impl Future> { + T::max_value(self) + } + + #[inline] + fn sample(&mut self) -> impl Future> { + T::sample(self) + } +} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index cbd74f5d..ecfb9146 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -3,6 +3,7 @@ #![no_std] #![allow(async_fn_in_trait)] +pub mod adc; pub mod delay; pub mod digital; pub mod i2c; diff --git a/embedded-hal/src/adc.rs b/embedded-hal/src/adc.rs new file mode 100644 index 00000000..dadc6078 --- /dev/null +++ b/embedded-hal/src/adc.rs @@ -0,0 +1,131 @@ +//! Blocking Analog to Digital conversion. +//! +//! # Multi channel ADCs +//! +//! One of the challenges of writing portable drivers is that the device may support +//! multiple ADC channels but driver may only be capable of sampling one channel at a +//! time.. +//! +//! To solve this, the implementor of [`OneShotChannel`] is expected to multiplex access to +//! the ADC. This means a driver may not directly implement the ADC traits for you. + +#[cfg(feature = "defmt-03")] +use crate::defmt; + +/// Error. +pub trait Error: core::fmt::Debug { + /// Convert error to a generic error kind + /// + /// By using this method, errors freely defined by HAL implementations + /// can be converted to a set of generic errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// Error kind. +/// +/// This represents a common set of operation errors. HAL implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common errors, generic code can still react to them. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[non_exhaustive] +pub enum ErrorKind { + /// A different error occurred. The original error may contain more information. + Other, +} + +impl Error for ErrorKind { + #[inline] + fn kind(&self) -> ErrorKind { + *self + } +} + +impl core::error::Error for ErrorKind {} + +impl core::fmt::Display for ErrorKind { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +/// Error type trait. +/// +/// This just defines the error type, to be used by the other traits. +pub trait ErrorType { + /// Error type + type Error: Error; +} + +impl ErrorType for &T { + type Error = T::Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} + +/// ADC channel. +/// +/// This does not contain any logic. It only defines the type of a sample read by the ADC. +pub trait Channel { + /// The size of a sample value from the ADC. + type Word: Sized + Copy; +} + +/// ADC channel capable of a one shot measurement. +pub trait OneShotChannel: Channel + ErrorType { + /// The maximum value that may be produced by the ADC. + /// + /// This is guaranteed to not change once a channel is constructed. + /// + /// For example, a 12-bit ADC would return 4095. + fn max_value(&self) -> Result; + + /// Sample from the ADC. + fn sample(&mut self) -> Result; +} + +impl Channel for &T { + type Word = T::Word; +} + +impl Channel for &mut T { + type Word = T::Word; +} + +impl OneShotChannel for &mut T { + #[inline] + fn max_value(&self) -> Result { + T::max_value(self) + } + + #[inline] + fn sample(&mut self) -> Result { + T::sample(self) + } +} + +#[cfg(test)] +mod tests { + use core::convert::Infallible; + + use crate::adc::OneShotChannel; + + /// Verify the blocking trait is dyn compatible. + #[allow(unused)] + fn dyn_compatible(channel: &mut dyn OneShotChannel) {} +} diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index f5eb76c3..4a65fad4 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -2,6 +2,7 @@ #![warn(missing_docs)] #![no_std] +pub mod adc; pub mod delay; pub mod digital; pub mod i2c;