Skip to content

Commit

Permalink
Add methods to measure voltage instead of raw value.
Browse files Browse the repository at this point in the history
  • Loading branch information
reitermarkus committed Jan 18, 2024
1 parent ddc0743 commit 902ee78
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 118 deletions.
1 change: 0 additions & 1 deletion embedded-hal-async/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
144 changes: 41 additions & 103 deletions embedded-hal-async/src/adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
/// ```
Expand All @@ -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
/// }
/// }
///
Expand All @@ -30,12 +34,16 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType};
/// }
///
/// impl AdcChannel for MySpinningAdc {
/// async fn read(&mut self) -> Result<u32, Self::Error> {
/// async fn measure_nv(&mut self) -> Result<i64, Self::Error> {
/// Ok(self.measure_mv().await? as i64 * 1_000_000)
/// }
///
/// async fn measure_mv(&mut self) -> Result<i32, Self::Error> {
/// while !self.is_ready() {
/// core::hint::spin_loop();
/// }
///
/// Ok(self.data())
/// Ok(self.data() as i32)
/// }
/// }
/// ```
Expand All @@ -51,8 +59,8 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType};
/// };
///
/// impl<T> MyWaitingAdc<T> {
/// pub fn data(&mut self) -> u32 {
/// 42
/// pub fn data(&mut self) -> u16 {
/// 3300
/// }
/// }
///
Expand All @@ -61,7 +69,11 @@ pub use embedded_hal::adc::{Error, ErrorKind, ErrorType};
/// }
///
/// impl<T: Wait> AdcChannel for MyWaitingAdc<T> {
/// async fn read(&mut self) -> Result<u32, Self::Error> {
/// async fn measure_nv(&mut self) -> Result<i64, Self::Error> {
/// Ok(self.measure_mv().await? as i64 * 1_000_000)
/// }
///
/// async fn measure_mv(&mut self) -> Result<i32, Self::Error> {
/// match self.ready_pin.wait_for_high().await {
/// Ok(()) => (),
/// Err(err) => return Err(match err.kind() {
Expand All @@ -70,115 +82,41 @@ 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<u32, Self::Error>;
/// Take a measurement in nV (nanovolts).
async fn measure_nv(&mut self) -> Result<i64, Self::Error>;

/// Take a measurement in mV (microvolts).
async fn measure_uv(&mut self) -> Result<i32, Self::Error> {
Ok((self.measure_nv().await? / 1_000) as i32)
}

/// Take a measurement in mV (millivolts).
async fn measure_mv(&mut self) -> Result<i32, Self::Error> {
Ok(self.measure_uv().await? / 1_000)
}
}

impl<T> AdcChannel for &mut T
where
T: AdcChannel + ?Sized,
{
#[inline]
async fn read(&mut self) -> Result<u32, Self::Error> {
(*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<i64, Self::Error> {
(*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<u32, Self::Error> {
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<i32, Self::Error> {
(*self).measure_uv().await
}

#[tokio::test]
async fn test_32_bit() {
test_adc!(Adc32, 32, u32);
#[inline]
async fn measure_mv(&mut self) -> Result<i32, Self::Error> {
(*self).measure_mv().await
}
}
51 changes: 37 additions & 14 deletions embedded-hal/src/adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
/// ```
Expand All @@ -23,8 +27,8 @@ use crate::defmt;
/// true
/// }
///
/// pub fn data(&mut self) -> u32 {
/// 42
/// pub fn data(&mut self) -> u16 {
/// 3300
/// }
/// }
///
Expand All @@ -33,32 +37,51 @@ use crate::defmt;
/// }
///
/// impl AdcChannel for MySpinningAdc {
/// fn read(&mut self) -> Result<u32, Self::Error> {
/// fn measure_nv(&mut self) -> Result<i64, Self::Error> {
/// Ok(self.measure_mv()? as i64 * 1_000_000)
/// }
///
/// fn measure_mv(&mut self) -> Result<i32, Self::Error> {
/// 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<u32, Self::Error>;
/// Take a measurement in nV (nanovolts).
fn measure_nv(&mut self) -> Result<i64, Self::Error>;

/// Take a measurement in mV (microvolts).
fn measure_uv(&mut self) -> Result<i32, Self::Error> {
Ok((self.measure_nv()? / 1_000) as i32)
}

/// Take a measurement in mV (millivolts).
fn measure_mv(&mut self) -> Result<i32, Self::Error> {
Ok(self.measure_uv()? / 1_000)
}
}

impl<T> AdcChannel for &mut T
where
T: AdcChannel + ?Sized,
{
#[inline]
fn read(&mut self) -> Result<u32, Self::Error> {
(*self).read()
fn measure_nv(&mut self) -> Result<i64, Self::Error> {
(*self).measure_nv()
}

#[inline]
fn measure_uv(&mut self) -> Result<i32, Self::Error> {
(*self).measure_uv()
}

#[inline]
fn measure_mv(&mut self) -> Result<i32, Self::Error> {
(*self).measure_mv()
}
}

Expand Down

0 comments on commit 902ee78

Please sign in to comment.