From 89f0c580f2073fd8de111f215eb695f72341f9ba Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 17 Jan 2024 01:59:48 +0100 Subject: [PATCH 1/4] Add `AdcChannel` trait. --- embedded-hal-async/Cargo.toml | 1 + embedded-hal-async/src/adc.rs | 184 ++++++++++++++++++++++++++++++++++ embedded-hal-async/src/lib.rs | 1 + embedded-hal/src/adc.rs | 124 +++++++++++++++++++++++ embedded-hal/src/lib.rs | 1 + 5 files changed, 311 insertions(+) create mode 100644 embedded-hal-async/src/adc.rs create mode 100644 embedded-hal/src/adc.rs diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 4d2679336..93e322c46 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -20,3 +20,4 @@ defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } +tokio = { version = "1", features = ["rt", "macros"] } diff --git a/embedded-hal-async/src/adc.rs b/embedded-hal-async/src/adc.rs new file mode 100644 index 000000000..811659232 --- /dev/null +++ b/embedded-hal-async/src/adc.rs @@ -0,0 +1,184 @@ +//! Asynchronous analog-digital conversion traits. + +pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; + +/// Read data from an ADC. +/// +/// # Examples +/// +/// In the first naive example, [`read`](crate::adc::AdcChannel::read) is implemented +/// using a spin loop and only returns once data is ready. +/// +/// ``` +/// # use embedded_hal_async::adc::{AdcChannel, ErrorKind, ErrorType, Error}; +/// # +/// struct MySpinningAdc; +/// +/// impl MySpinningAdc { +/// pub fn is_ready(&mut self) -> bool { +/// // Just pretend this returns `false` the first few times. +/// true +/// } +/// +/// pub fn data(&mut self) -> u32 { +/// 42 +/// } +/// } +/// +/// impl ErrorType for MySpinningAdc { +/// type Error = ErrorKind; +/// } +/// +/// impl AdcChannel for MySpinningAdc { +/// async fn read(&mut self) -> Result { +/// while !self.is_ready() { +/// core::hint::spin_loop(); +/// } +/// +/// Ok(self.data()) +/// } +/// } +/// ``` +/// +/// 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, AdcChannel}, digital::{self, Wait, Error as _, ErrorType as _}}; +/// # +/// struct MyWaitingAdc { +/// ready_pin: T, +/// }; +/// +/// impl MyWaitingAdc { +/// pub fn data(&mut self) -> u32 { +/// 42 +/// } +/// } +/// +/// impl adc::ErrorType for MyWaitingAdc { +/// type Error = adc::ErrorKind; +/// } +/// +/// impl AdcChannel for MyWaitingAdc { +/// async fn read(&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()) +/// } +/// } +/// ``` +pub trait AdcChannel: ErrorType { + /// Reads data from the ADC. + /// + /// # Note for Implementers + /// + /// This should wait until data is ready and then read it. + /// If the ADC's precision is less than 32 bits, the value must be scaled accordingly. + async fn read(&mut self) -> Result; +} + +impl AdcChannel for &mut T +where + T: AdcChannel + ?Sized, +{ + #[inline] + async fn read(&mut self) -> Result { + (*self).read().await + } +} + +#[cfg(test)] +mod test { + use super::*; + + /// Scale an integer containing `bits` bits to 32 bits. + fn scale_bits(raw_data: u32, bits: u32) -> u32 { + let mut scaled_data: u32 = 0; + + let mut remaining_bits = u32::BITS; + while remaining_bits > 0 { + let shl = bits.min(remaining_bits); + scaled_data = (scaled_data.wrapping_shl(shl)) | (raw_data.wrapping_shr(bits - shl)); + remaining_bits -= shl; + } + + scaled_data + } + + #[test] + fn scale_bits_i8_to_i32() { + let raw_data = u32::from(i8::MIN as u8); + let scaled_data = scale_bits(raw_data, 8); + assert!(i32::MIN <= (scaled_data as i32) && (scaled_data as i32) <= (i32::MIN + 1 << 8)); + } + + macro_rules! impl_adc { + ($Adc:ident, $bits:literal, $uint:ty) => { + struct $Adc($uint); + + impl $Adc { + const MAX: $uint = !(<$uint>::MAX.wrapping_shl($bits - 1).wrapping_shl(1)); + + pub fn data(&mut self) -> $uint { + self.0 + } + } + + impl ErrorType for $Adc { + type Error = core::convert::Infallible; + } + + impl AdcChannel for $Adc { + async fn read(&mut self) -> Result { + Ok(scale_bits(u32::from(self.data()), $bits)) + } + } + }; + } + + macro_rules! test_adc { + ($Adc:ident, $bits:literal, $uint:ty) => {{ + impl_adc!($Adc, $bits, $uint); + + // 0 should always be scaled to 0. + let mut adc_0 = $Adc(0); + assert_eq!(adc_0.read().await, Ok(0)); + + // `$Adc::MAX` should always be scaled to `u32::MAX`. + let mut adc_max = $Adc($Adc::MAX); + assert_eq!(adc_max.read().await, Ok(u32::MAX)); + }}; + } + + #[tokio::test] + async fn test_8_bit() { + test_adc!(Adc8, 8, u8); + } + + #[tokio::test] + async fn test_12_bit() { + test_adc!(Adc12, 12, u16); + } + + #[tokio::test] + async fn test_16_bit() { + test_adc!(Adc16, 16, u16); + } + + #[tokio::test] + async fn test_24_bit() { + test_adc!(Adc24, 24, u32); + } + + #[tokio::test] + async fn test_32_bit() { + test_adc!(Adc32, 32, u32); + } +} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 44901deca..b6d504d7b 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 000000000..fb22b9434 --- /dev/null +++ b/embedded-hal/src/adc.rs @@ -0,0 +1,124 @@ +//! Blocking analog-digital conversion traits. + +use core::fmt::Debug; + +#[cfg(feature = "defmt-03")] +use crate::defmt; + +/// Read data from an ADC. +/// +/// # Examples +/// +/// In the first naive example, [`read`](crate::adc::AdcChannel::read) is implemented +/// using a spin loop and only returns once data is ready. +/// +/// ``` +/// use embedded_hal::adc::{AdcChannel, ErrorKind, ErrorType, Error}; +/// +/// struct MySpinningAdc; +/// +/// impl MySpinningAdc { +/// pub fn is_ready(&mut self) -> bool { +/// // Just pretend this returns `false` the first few times. +/// true +/// } +/// +/// pub fn data(&mut self) -> u32 { +/// 42 +/// } +/// } +/// +/// impl ErrorType for MySpinningAdc { +/// type Error = ErrorKind; +/// } +/// +/// impl AdcChannel for MySpinningAdc { +/// fn read(&mut self) -> Result { +/// while !self.is_ready() { +/// core::hint::spin_loop(); +/// } +/// +/// Ok(self.data()) +/// } +/// } +/// ``` +pub trait AdcChannel: ErrorType { + /// Reads data from the ADC. + /// + /// # Note for Implementers + /// + /// This should wait until data is ready and then read it. + /// If the ADC's precision is less than 32 bits, the value must be scaled accordingly. + fn read(&mut self) -> Result; +} + +impl AdcChannel for &mut T +where + T: AdcChannel + ?Sized, +{ + #[inline] + fn read(&mut self) -> Result { + (*self).read() + } +} + +/// 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 { + /// A different error occurred. The original error may contain more information. + Other, +} + +impl Error for ErrorKind { + #[inline] + fn kind(&self) -> ErrorKind { + *self + } +} + +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" + ), + } + } +} + +/// 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 f5eb76c32..4a65fad40 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; From f0db02ea6846e53885658ef033e9f7274cdf648b Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 17 Jan 2024 20:16:24 +0100 Subject: [PATCH 2/4] Add methods to measure voltage instead of raw value. --- embedded-hal-async/Cargo.toml | 1 - embedded-hal-async/src/adc.rs | 144 ++++++++++------------------------ embedded-hal/src/adc.rs | 51 ++++++++---- 3 files changed, 78 insertions(+), 118 deletions(-) diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 93e322c46..4d2679336 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -20,4 +20,3 @@ defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } -tokio = { version = "1", features = ["rt", "macros"] } diff --git a/embedded-hal-async/src/adc.rs b/embedded-hal-async/src/adc.rs index 811659232..1863fdfe8 100644 --- a/embedded-hal-async/src/adc.rs +++ b/embedded-hal-async/src/adc.rs @@ -4,9 +4,13 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// Read data from an ADC. /// +/// # Note for Implementers +/// +/// This should wait until data is ready and then read it. +/// /// # Examples /// -/// In the first naive example, [`read`](crate::adc::AdcChannel::read) is implemented +/// In the first naive example, [`AdcChannel`] is implemented /// using a spin loop and only returns once data is ready. /// /// ``` @@ -20,8 +24,8 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// true /// } /// -/// pub fn data(&mut self) -> u32 { -/// 42 +/// pub fn data(&mut self) -> u16 { +/// 3300 /// } /// } /// @@ -30,12 +34,16 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// } /// /// impl AdcChannel for MySpinningAdc { -/// async fn read(&mut self) -> Result { +/// 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()) +/// Ok(self.data() as i32) /// } /// } /// ``` @@ -51,8 +59,8 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// }; /// /// impl MyWaitingAdc { -/// pub fn data(&mut self) -> u32 { -/// 42 +/// pub fn data(&mut self) -> u16 { +/// 3300 /// } /// } /// @@ -61,7 +69,11 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// } /// /// impl AdcChannel for MyWaitingAdc { -/// async fn read(&mut self) -> Result { +/// 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() { @@ -70,18 +82,23 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// }) /// } /// -/// Ok(self.data()) +/// Ok(self.data() as i32) /// } /// } /// ``` pub trait AdcChannel: ErrorType { - /// Reads data from the ADC. - /// - /// # Note for Implementers - /// - /// This should wait until data is ready and then read it. - /// If the ADC's precision is less than 32 bits, the value must be scaled accordingly. - async fn read(&mut self) -> Result; + /// Take a measurement in nV (nanovolts). + async fn measure_nv(&mut self) -> Result; + + /// Take a measurement in mV (microvolts). + async fn measure_uv(&mut self) -> Result { + Ok((self.measure_nv().await? / 1_000) as i32) + } + + /// Take a measurement in mV (millivolts). + async fn measure_mv(&mut self) -> Result { + Ok(self.measure_uv().await? / 1_000) + } } impl AdcChannel for &mut T @@ -89,96 +106,17 @@ where T: AdcChannel + ?Sized, { #[inline] - async fn read(&mut self) -> Result { - (*self).read().await - } -} - -#[cfg(test)] -mod test { - use super::*; - - /// Scale an integer containing `bits` bits to 32 bits. - fn scale_bits(raw_data: u32, bits: u32) -> u32 { - let mut scaled_data: u32 = 0; - - let mut remaining_bits = u32::BITS; - while remaining_bits > 0 { - let shl = bits.min(remaining_bits); - scaled_data = (scaled_data.wrapping_shl(shl)) | (raw_data.wrapping_shr(bits - shl)); - remaining_bits -= shl; - } - - scaled_data - } - - #[test] - fn scale_bits_i8_to_i32() { - let raw_data = u32::from(i8::MIN as u8); - let scaled_data = scale_bits(raw_data, 8); - assert!(i32::MIN <= (scaled_data as i32) && (scaled_data as i32) <= (i32::MIN + 1 << 8)); + async fn measure_nv(&mut self) -> Result { + (*self).measure_nv().await } - macro_rules! impl_adc { - ($Adc:ident, $bits:literal, $uint:ty) => { - struct $Adc($uint); - - impl $Adc { - const MAX: $uint = !(<$uint>::MAX.wrapping_shl($bits - 1).wrapping_shl(1)); - - pub fn data(&mut self) -> $uint { - self.0 - } - } - - impl ErrorType for $Adc { - type Error = core::convert::Infallible; - } - - impl AdcChannel for $Adc { - async fn read(&mut self) -> Result { - Ok(scale_bits(u32::from(self.data()), $bits)) - } - } - }; - } - - macro_rules! test_adc { - ($Adc:ident, $bits:literal, $uint:ty) => {{ - impl_adc!($Adc, $bits, $uint); - - // 0 should always be scaled to 0. - let mut adc_0 = $Adc(0); - assert_eq!(adc_0.read().await, Ok(0)); - - // `$Adc::MAX` should always be scaled to `u32::MAX`. - let mut adc_max = $Adc($Adc::MAX); - assert_eq!(adc_max.read().await, Ok(u32::MAX)); - }}; - } - - #[tokio::test] - async fn test_8_bit() { - test_adc!(Adc8, 8, u8); - } - - #[tokio::test] - async fn test_12_bit() { - test_adc!(Adc12, 12, u16); - } - - #[tokio::test] - async fn test_16_bit() { - test_adc!(Adc16, 16, u16); - } - - #[tokio::test] - async fn test_24_bit() { - test_adc!(Adc24, 24, u32); + #[inline] + async fn measure_uv(&mut self) -> Result { + (*self).measure_uv().await } - #[tokio::test] - async fn test_32_bit() { - test_adc!(Adc32, 32, u32); + #[inline] + async fn measure_mv(&mut self) -> Result { + (*self).measure_mv().await } } diff --git a/embedded-hal/src/adc.rs b/embedded-hal/src/adc.rs index fb22b9434..7a6c4c093 100644 --- a/embedded-hal/src/adc.rs +++ b/embedded-hal/src/adc.rs @@ -7,9 +7,13 @@ use crate::defmt; /// Read data from an ADC. /// +/// # Note for Implementers +/// +/// This should wait until data is ready and then read it. +/// /// # Examples /// -/// In the first naive example, [`read`](crate::adc::AdcChannel::read) is implemented +/// In the first naive example, [`AdcChannel`] is implemented /// using a spin loop and only returns once data is ready. /// /// ``` @@ -23,8 +27,8 @@ use crate::defmt; /// true /// } /// -/// pub fn data(&mut self) -> u32 { -/// 42 +/// pub fn data(&mut self) -> u16 { +/// 3300 /// } /// } /// @@ -33,23 +37,32 @@ use crate::defmt; /// } /// /// impl AdcChannel for MySpinningAdc { -/// fn read(&mut self) -> Result { +/// 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()) +/// Ok(self.data() as i32) /// } /// } /// ``` pub trait AdcChannel: ErrorType { - /// Reads data from the ADC. - /// - /// # Note for Implementers - /// - /// This should wait until data is ready and then read it. - /// If the ADC's precision is less than 32 bits, the value must be scaled accordingly. - fn read(&mut self) -> Result; + /// Take a measurement in nV (nanovolts). + fn measure_nv(&mut self) -> Result; + + /// Take a measurement in mV (microvolts). + fn measure_uv(&mut self) -> Result { + Ok((self.measure_nv()? / 1_000) as i32) + } + + /// Take a measurement in mV (millivolts). + fn measure_mv(&mut self) -> Result { + Ok(self.measure_uv()? / 1_000) + } } impl AdcChannel for &mut T @@ -57,8 +70,18 @@ where T: AdcChannel + ?Sized, { #[inline] - fn read(&mut self) -> Result { - (*self).read() + 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() } } From 9ed5035782a676492b60b404eb902dab518073c8 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Thu, 18 Jan 2024 19:36:25 +0100 Subject: [PATCH 3/4] Add ADC `Clip` error. --- embedded-hal/src/adc.rs | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/embedded-hal/src/adc.rs b/embedded-hal/src/adc.rs index 7a6c4c093..f6d6ccc56 100644 --- a/embedded-hal/src/adc.rs +++ b/embedded-hal/src/adc.rs @@ -1,6 +1,6 @@ //! Blocking analog-digital conversion traits. -use core::fmt::Debug; +use core::fmt::{Debug, Display}; #[cfg(feature = "defmt-03")] use crate::defmt; @@ -111,10 +111,22 @@ impl Error for core::convert::Infallible { #[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 { @@ -122,15 +134,23 @@ impl Error for ErrorKind { } } -impl core::fmt::Display for ErrorKind { +impl 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" - ), - } + 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, + ) } } From 608b39ebbde087dc7e66eaf8e288e5ca1c7b6108 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Fri, 19 Jan 2024 21:55:50 +0100 Subject: [PATCH 4/4] Rename `AdcChannel` to `Voltmeter` and add `Ammeter`. --- embedded-hal-async/src/adc.rs | 130 +++++++++++++++++++++++++--------- embedded-hal/src/adc.rs | 109 +++++++++++++++++++++------- 2 files changed, 182 insertions(+), 57 deletions(-) diff --git a/embedded-hal-async/src/adc.rs b/embedded-hal-async/src/adc.rs index 1863fdfe8..0d1395c23 100644 --- a/embedded-hal-async/src/adc.rs +++ b/embedded-hal-async/src/adc.rs @@ -2,23 +2,18 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; -/// Read data from an ADC. -/// -/// # Note for Implementers -/// -/// This should wait until data is ready and then read it. +/// Asynchronous voltmeter for measuring voltage. /// /// # Examples /// -/// In the first naive example, [`AdcChannel`] is implemented -/// using a spin loop and only returns once data is ready. +/// In the first naive example, [`Voltmeter`] is implemented using a spin loop. /// /// ``` -/// # use embedded_hal_async::adc::{AdcChannel, ErrorKind, ErrorType, Error}; -/// # -/// struct MySpinningAdc; +/// use embedded_hal_async::adc::{ErrorKind, ErrorType, Error, Voltmeter}; +/// +/// struct MySpinningVoltmeter; /// -/// impl MySpinningAdc { +/// impl MySpinningVoltmeter { /// pub fn is_ready(&mut self) -> bool { /// // Just pretend this returns `false` the first few times. /// true @@ -29,21 +24,21 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// } /// } /// -/// impl ErrorType for MySpinningAdc { +/// impl ErrorType for MySpinningVoltmeter { /// type Error = ErrorKind; /// } /// -/// impl AdcChannel for MySpinningAdc { +/// 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 { +/// async fn measure_mv(&mut self) -> Result { /// while !self.is_ready() { /// core::hint::spin_loop(); /// } /// -/// Ok(self.data() as i32) +/// Ok(self.data() as i16) /// } /// } /// ``` @@ -52,28 +47,31 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// When the “ready pin” goes high, data is ready. /// /// ``` -/// # use embedded_hal_async::{adc::{self, ErrorKind, ErrorType, Error, AdcChannel}, digital::{self, Wait, Error as _, ErrorType as _}}; -/// # -/// struct MyWaitingAdc { +/// use embedded_hal_async::{ +/// adc::{self, ErrorKind, ErrorType, Error, Voltmeter}, +/// digital::{self, Wait, Error as _, ErrorType as _}, +/// }; +/// +/// struct MyWaitingVoltmeter { /// ready_pin: T, /// }; /// -/// impl MyWaitingAdc { +/// impl MyWaitingVoltmeter { /// pub fn data(&mut self) -> u16 { /// 3300 /// } /// } /// -/// impl adc::ErrorType for MyWaitingAdc { +/// impl adc::ErrorType for MyWaitingVoltmeter { /// type Error = adc::ErrorKind; /// } /// -/// impl AdcChannel for MyWaitingAdc { +/// 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 { +/// async fn measure_mv(&mut self) -> Result { /// match self.ready_pin.wait_for_high().await { /// Ok(()) => (), /// Err(err) => return Err(match err.kind() { @@ -82,28 +80,43 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType}; /// }) /// } /// -/// Ok(self.data() as i32) +/// Ok(self.data() as i16) /// } /// } /// ``` -pub trait AdcChannel: ErrorType { - /// Take a measurement in nV (nanovolts). +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; - /// Take a measurement in mV (microvolts). + /// 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) as i32) + Ok((self.measure_nv().await? / 1_000).clamp(i32::MIN.into(), i32::MAX.into()) as i32) } - /// Take a measurement in mV (millivolts). - async fn measure_mv(&mut self) -> Result { - Ok(self.measure_uv().await? / 1_000) + /// 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 AdcChannel for &mut T +impl Voltmeter for &mut T where - T: AdcChannel + ?Sized, + T: Voltmeter + ?Sized, { #[inline] async fn measure_nv(&mut self) -> Result { @@ -116,7 +129,58 @@ where } #[inline] - async fn measure_mv(&mut self) -> Result { + 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/src/adc.rs b/embedded-hal/src/adc.rs index f6d6ccc56..9c84b3ffd 100644 --- a/embedded-hal/src/adc.rs +++ b/embedded-hal/src/adc.rs @@ -5,23 +5,18 @@ use core::fmt::{Debug, Display}; #[cfg(feature = "defmt-03")] use crate::defmt; -/// Read data from an ADC. -/// -/// # Note for Implementers -/// -/// This should wait until data is ready and then read it. +/// Blocking voltmeter for measuring voltage. /// /// # Examples /// -/// In the first naive example, [`AdcChannel`] is implemented -/// using a spin loop and only returns once data is ready. +/// In the first naive example, [`Voltmeter`] is implemented using a spin loop. /// /// ``` -/// use embedded_hal::adc::{AdcChannel, ErrorKind, ErrorType, Error}; +/// use embedded_hal::adc::{ErrorKind, ErrorType, Error, Voltmeter}; /// -/// struct MySpinningAdc; +/// struct MySpinningVoltmeter; /// -/// impl MySpinningAdc { +/// impl MySpinningVoltmeter { /// pub fn is_ready(&mut self) -> bool { /// // Just pretend this returns `false` the first few times. /// true @@ -32,42 +27,57 @@ use crate::defmt; /// } /// } /// -/// impl ErrorType for MySpinningAdc { +/// impl ErrorType for MySpinningVoltmeter { /// type Error = ErrorKind; /// } /// -/// impl AdcChannel for MySpinningAdc { +/// 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 { +/// fn measure_mv(&mut self) -> Result { /// while !self.is_ready() { /// core::hint::spin_loop(); /// } /// -/// Ok(self.data() as i32) +/// Ok(self.data() as i16) /// } /// } /// ``` -pub trait AdcChannel: ErrorType { - /// Take a measurement in nV (nanovolts). +pub trait Voltmeter: ErrorType { + /// Measures voltage in nV (nanovolts). + /// + /// This can measure between -9223372036.854775808V and 9223372036.854775807V. fn measure_nv(&mut self) -> Result; - /// Take a measurement in mV (microvolts). + /// 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) as i32) + Ok((self.measure_nv()? / 1_000).clamp(i32::MIN.into(), i32::MAX.into()) as i32) } - /// Take a measurement in mV (millivolts). - fn measure_mv(&mut self) -> Result { - Ok(self.measure_uv()? / 1_000) + /// 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 AdcChannel for &mut T +impl Voltmeter for &mut T where - T: AdcChannel + ?Sized, + T: Voltmeter + ?Sized, { #[inline] fn measure_nv(&mut self) -> Result { @@ -80,11 +90,62 @@ where } #[inline] - fn measure_mv(&mut self) -> Result { + 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.