diff --git a/embedded-hal-async/src/adc.rs b/embedded-hal-async/src/adc.rs new file mode 100644 index 00000000..0d1395c2 --- /dev/null +++ b/embedded-hal-async/src/adc.rs @@ -0,0 +1,186 @@ +//! Asynchronous analog-digital conversion traits. + +pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; + +/// Asynchronous voltmeter for measuring voltage. +/// +/// # Examples +/// +/// In the first naive example, [`Voltmeter`] is implemented using a spin loop. +/// +/// ``` +/// use embedded_hal_async::adc::{ErrorKind, ErrorType, Error, Voltmeter}; +/// +/// struct MySpinningVoltmeter; +/// +/// impl MySpinningVoltmeter { +/// pub fn is_ready(&mut self) -> bool { +/// // Just pretend this returns `false` the first few times. +/// true +/// } +/// +/// pub fn data(&mut self) -> u16 { +/// 3300 +/// } +/// } +/// +/// impl ErrorType for MySpinningVoltmeter { +/// type Error = ErrorKind; +/// } +/// +/// impl Voltmeter for MySpinningVoltmeter { +/// async fn measure_nv(&mut self) -> Result { +/// Ok(self.measure_mv().await? as i64 * 1_000_000) +/// } +/// +/// async fn measure_mv(&mut self) -> Result { +/// while !self.is_ready() { +/// core::hint::spin_loop(); +/// } +/// +/// Ok(self.data() as i16) +/// } +/// } +/// ``` +/// +/// The second example assumes an ADC that supports a “ready pin” which implements [`Wait`](crate::digital::Wait). +/// When the “ready pin” goes high, data is ready. +/// +/// ``` +/// use embedded_hal_async::{ +/// adc::{self, ErrorKind, ErrorType, Error, Voltmeter}, +/// digital::{self, Wait, Error as _, ErrorType as _}, +/// }; +/// +/// struct MyWaitingVoltmeter { +/// ready_pin: T, +/// }; +/// +/// impl MyWaitingVoltmeter { +/// pub fn data(&mut self) -> u16 { +/// 3300 +/// } +/// } +/// +/// impl adc::ErrorType for MyWaitingVoltmeter { +/// type Error = adc::ErrorKind; +/// } +/// +/// impl Voltmeter for MyWaitingVoltmeter { +/// async fn measure_nv(&mut self) -> Result { +/// Ok(self.measure_mv().await? as i64 * 1_000_000) +/// } +/// +/// async fn measure_mv(&mut self) -> Result { +/// match self.ready_pin.wait_for_high().await { +/// Ok(()) => (), +/// Err(err) => return Err(match err.kind() { +/// digital::ErrorKind::Other => adc::ErrorKind::Other, +/// _ => adc::ErrorKind::Other, +/// }) +/// } +/// +/// Ok(self.data() as i16) +/// } +/// } +/// ``` +pub trait Voltmeter: ErrorType { + /// Measures voltage in nV (nanovolts). + /// + /// This can measure between -9223372036.854775808V and 9223372036.854775807V. + async fn measure_nv(&mut self) -> Result; + + /// Measures voltage in mV (microvolts). + /// + /// This can measure between -2147.483648V and 2147.483647V. + /// If you need to measure a larger range, use [`measure_nv`](Voltmeter::measure_nv) instead. + /// + /// When overriding the default implementation, ensure that the measured voltage is clamped + /// between [`i32::MIN`] and [`i32::MAX`]. + async fn measure_uv(&mut self) -> Result { + Ok((self.measure_nv().await? / 1_000).clamp(i32::MIN.into(), i32::MAX.into()) as i32) + } + + /// Measures voltage in mV (millivolts). + /// + /// This can measure between between -32.768V and 32.767V. + /// If you need to measure a larger range, + /// use [`measure_uv`](Voltmeter::measure_uv) or [`measure_nv`](Voltmeter::measure_nv) instead. + /// + /// When overriding the default implementation, ensure that the measured voltage is clamped + /// between [`i16::MIN`] and [`i16::MAX`]. + async fn measure_mv(&mut self) -> Result { + Ok((self.measure_uv().await? / 1_000).clamp(i16::MIN.into(), i16::MAX.into()) as i16) + } +} + +impl Voltmeter for &mut T +where + T: Voltmeter + ?Sized, +{ + #[inline] + async fn measure_nv(&mut self) -> Result { + (*self).measure_nv().await + } + + #[inline] + async fn measure_uv(&mut self) -> Result { + (*self).measure_uv().await + } + + #[inline] + async fn measure_mv(&mut self) -> Result { + (*self).measure_mv().await + } +} + +/// Asynchronous ammeter (ampere meter) for measuring current. +pub trait Ammeter: ErrorType { + /// Measures current in nA (nanoampere). + /// + /// This can measure between -9223372036.854775808A and 9223372036.854775807A. + async fn measure_na(&mut self) -> Result; + + /// Measures current in uA (microampere). + /// + /// This can measure between -2147.483648A and 2147.483647A. + /// If you need to measure a larger range, use [`measure_na`](Ammeter::measure_na) instead. + /// + /// When overriding the default implementation, ensure that the measured current is clamped + /// between [`i32::MIN`] and [`i32::MAX`]. + async fn measure_ua(&mut self) -> Result { + Ok((self.measure_na().await? / 1_000).clamp(i32::MIN.into(), i32::MAX.into()) as i32) + } + + /// Measures current in mA (milliampere). + /// + /// This can measure between between -32.768A and 32.767A. + /// If you need to measure a larger range, + /// use [`measure_ua`](Ammeter::measure_ua) or [`measure_na`](Ammeter::measure_na) instead. + /// + /// When overriding the default implementation, ensure that the measured voltage is clamped + /// between [`i16::MIN`] and [`i16::MAX`]. + async fn measure_ma(&mut self) -> Result { + Ok((self.measure_ua().await? / 1_000).clamp(i16::MIN.into(), i16::MAX.into()) as i16) + } +} + +impl Ammeter for &mut T +where + T: Ammeter + ?Sized, +{ + #[inline] + async fn measure_na(&mut self) -> Result { + (*self).measure_na().await + } + + #[inline] + async fn measure_ua(&mut self) -> Result { + (*self).measure_ua().await + } + + #[inline] + async fn measure_ma(&mut self) -> Result { + (*self).measure_ma().await + } +} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 44901dec..b6d504d7 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -9,6 +9,7 @@ #![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![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..9c84b3ff --- /dev/null +++ b/embedded-hal/src/adc.rs @@ -0,0 +1,228 @@ +//! Blocking analog-digital conversion traits. + +use core::fmt::{Debug, Display}; + +#[cfg(feature = "defmt-03")] +use crate::defmt; + +/// Blocking voltmeter for measuring voltage. +/// +/// # Examples +/// +/// In the first naive example, [`Voltmeter`] is implemented using a spin loop. +/// +/// ``` +/// use embedded_hal::adc::{ErrorKind, ErrorType, Error, Voltmeter}; +/// +/// struct MySpinningVoltmeter; +/// +/// impl MySpinningVoltmeter { +/// pub fn is_ready(&mut self) -> bool { +/// // Just pretend this returns `false` the first few times. +/// true +/// } +/// +/// pub fn data(&mut self) -> u16 { +/// 3300 +/// } +/// } +/// +/// impl ErrorType for MySpinningVoltmeter { +/// type Error = ErrorKind; +/// } +/// +/// impl Voltmeter for MySpinningVoltmeter { +/// fn measure_nv(&mut self) -> Result { +/// Ok(self.measure_mv()? as i64 * 1_000_000) +/// } +/// +/// fn measure_mv(&mut self) -> Result { +/// while !self.is_ready() { +/// core::hint::spin_loop(); +/// } +/// +/// Ok(self.data() as i16) +/// } +/// } +/// ``` +pub trait Voltmeter: ErrorType { + /// Measures voltage in nV (nanovolts). + /// + /// This can measure between -9223372036.854775808V and 9223372036.854775807V. + fn measure_nv(&mut self) -> Result; + + /// Measures voltage in mV (microvolts). + /// + /// This can measure between -2147.483648V and 2147.483647V. + /// If you need to measure a larger range, use [`measure_nv`](Voltmeter::measure_nv) instead. + /// + /// When overriding the default implementation, ensure that the measured voltage is clamped + /// between [`i32::MIN`] and [`i32::MAX`]. + fn measure_uv(&mut self) -> Result { + Ok((self.measure_nv()? / 1_000).clamp(i32::MIN.into(), i32::MAX.into()) as i32) + } + + /// Measures voltage in mV (millivolts). + /// + /// This can measure between between -32.768V and 32.767V. + /// If you need to measure a larger range, + /// use [`measure_uv`](Voltmeter::measure_uv) or [`measure_mv`](Voltmeter::measure_mv) instead. + /// + /// When overriding the default implementation, ensure that the measured voltage is clamped + /// between [`i16::MIN`] and [`i16::MAX`]. + fn measure_mv(&mut self) -> Result { + Ok((self.measure_uv()? / 1_000).clamp(i16::MIN.into(), i16::MAX.into()) as i16) + } +} + +impl Voltmeter for &mut T +where + T: Voltmeter + ?Sized, +{ + #[inline] + fn measure_nv(&mut self) -> Result { + (*self).measure_nv() + } + + #[inline] + fn measure_uv(&mut self) -> Result { + (*self).measure_uv() + } + + #[inline] + fn measure_mv(&mut self) -> Result { + (*self).measure_mv() + } +} + +/// Blocking ammeter (ampere meter) for measuring current. +pub trait Ammeter: ErrorType { + /// Measures current in nA (nanoampere). + /// + /// This can measure between -9223372036.854775808A and 9223372036.854775807A. + fn measure_na(&mut self) -> Result; + + /// Measures current in uA (microampere). + /// + /// This can measure between -2147.483648A and 2147.483647A. + /// If you need to measure a larger range, use [`measure_na`](Ammeter::measure_na) instead. + /// + /// When overriding the default implementation, ensure that the measured current is clamped + /// between [`i32::MIN`] and [`i32::MAX`]. + fn measure_ua(&mut self) -> Result { + Ok((self.measure_na()? / 1_000).clamp(i32::MIN.into(), i32::MAX.into()) as i32) + } + + /// Measures current in mA (milliampere). + /// + /// This can measure between between -32.768A and 32.767A. + /// If you need to measure a larger range, + /// use [`measure_ua`](Ammeter::measure_ua) or [`measure_na`](Ammeter::measure_na) instead. + /// + /// When overriding the default implementation, ensure that the measured voltage is clamped + /// between [`i16::MIN`] and [`i16::MAX`]. + fn measure_ma(&mut self) -> Result { + Ok((self.measure_ua()? / 1_000).clamp(i16::MIN.into(), i16::MAX.into()) as i16) + } +} + +impl Ammeter for &mut T +where + T: Ammeter + ?Sized, +{ + #[inline] + fn measure_na(&mut self) -> Result { + (*self).measure_na() + } + + #[inline] + fn measure_ua(&mut self) -> Result { + (*self).measure_ua() + } + + #[inline] + fn measure_ma(&mut self) -> Result { + (*self).measure_ma() + } +} + +/// ADC error. +pub trait Error: Debug { + /// Convert error to a generic ADC error kind. + /// + /// By using this method, ADC errors freely defined by HAL implementations + /// can be converted to a set of generic ADC errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + #[inline] + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// ADC error kind. +/// +/// This represents a common set of ADC operation errors. HAL implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common ADC 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 { + /// Measurement was clipped. + Clip(Clip), + /// A different error occurred. The original error may contain more information. + Other, +} + +/// ADC clip error. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +pub enum Clip { + /// Measurement was clipped due to an undershoot of the measurement range. + Undershoot, + /// Measurement was clipped due to an overshoot of the measurement range. + Overshoot, +} + +impl Error for ErrorKind { + #[inline] + fn kind(&self) -> ErrorKind { + *self + } +} + +impl Display for ErrorKind { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + Display::fmt( + match self { + Self::Clip(Clip::Undershoot) => { + "Measurement was clipped due to an undershoot of the measurement range." + } + Self::Clip(Clip::Overshoot) => { + "Measurement was clipped due to an overshoot of the measurement range." + } + Self::Other => { + "A different error occurred. The original error may contain more information." + } + }, + f, + ) + } +} + +/// ADC error type trait. +/// +/// This just defines the error type, to be used by the other ADC traits. +pub trait ErrorType { + /// Error type. + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} 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;