Skip to content

Commit 89f0c58

Browse files
committed
Add AdcChannel trait.
1 parent 04b4f18 commit 89f0c58

File tree

5 files changed

+311
-0
lines changed

5 files changed

+311
-0
lines changed

embedded-hal-async/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"]
2020
[dependencies]
2121
embedded-hal = { version = "1.0.0", path = "../embedded-hal" }
2222
defmt-03 = { package = "defmt", version = "0.3", optional = true }
23+
tokio = { version = "1", features = ["rt", "macros"] }

embedded-hal-async/src/adc.rs

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
//! Asynchronous analog-digital conversion traits.
2+
3+
pub use embedded_hal::adc::{Error, ErrorKind, ErrorType};
4+
5+
/// Read data from an ADC.
6+
///
7+
/// # Examples
8+
///
9+
/// In the first naive example, [`read`](crate::adc::AdcChannel::read) is implemented
10+
/// using a spin loop and only returns once data is ready.
11+
///
12+
/// ```
13+
/// # use embedded_hal_async::adc::{AdcChannel, ErrorKind, ErrorType, Error};
14+
/// #
15+
/// struct MySpinningAdc;
16+
///
17+
/// impl MySpinningAdc {
18+
/// pub fn is_ready(&mut self) -> bool {
19+
/// // Just pretend this returns `false` the first few times.
20+
/// true
21+
/// }
22+
///
23+
/// pub fn data(&mut self) -> u32 {
24+
/// 42
25+
/// }
26+
/// }
27+
///
28+
/// impl ErrorType for MySpinningAdc {
29+
/// type Error = ErrorKind;
30+
/// }
31+
///
32+
/// impl AdcChannel for MySpinningAdc {
33+
/// async fn read(&mut self) -> Result<u32, Self::Error> {
34+
/// while !self.is_ready() {
35+
/// core::hint::spin_loop();
36+
/// }
37+
///
38+
/// Ok(self.data())
39+
/// }
40+
/// }
41+
/// ```
42+
///
43+
/// The second example assumes an ADC that supports a “ready pin” which implements [`Wait`](crate::digital::Wait).
44+
/// When the “ready pin” goes high, data is ready.
45+
///
46+
/// ```
47+
/// # use embedded_hal_async::{adc::{self, ErrorKind, ErrorType, Error, AdcChannel}, digital::{self, Wait, Error as _, ErrorType as _}};
48+
/// #
49+
/// struct MyWaitingAdc<T> {
50+
/// ready_pin: T,
51+
/// };
52+
///
53+
/// impl<T> MyWaitingAdc<T> {
54+
/// pub fn data(&mut self) -> u32 {
55+
/// 42
56+
/// }
57+
/// }
58+
///
59+
/// impl<T> adc::ErrorType for MyWaitingAdc<T> {
60+
/// type Error = adc::ErrorKind;
61+
/// }
62+
///
63+
/// impl<T: Wait> AdcChannel for MyWaitingAdc<T> {
64+
/// async fn read(&mut self) -> Result<u32, Self::Error> {
65+
/// match self.ready_pin.wait_for_high().await {
66+
/// Ok(()) => (),
67+
/// Err(err) => return Err(match err.kind() {
68+
/// digital::ErrorKind::Other => adc::ErrorKind::Other,
69+
/// _ => adc::ErrorKind::Other,
70+
/// })
71+
/// }
72+
///
73+
/// Ok(self.data())
74+
/// }
75+
/// }
76+
/// ```
77+
pub trait AdcChannel: ErrorType {
78+
/// Reads data from the ADC.
79+
///
80+
/// # Note for Implementers
81+
///
82+
/// This should wait until data is ready and then read it.
83+
/// If the ADC's precision is less than 32 bits, the value must be scaled accordingly.
84+
async fn read(&mut self) -> Result<u32, Self::Error>;
85+
}
86+
87+
impl<T> AdcChannel for &mut T
88+
where
89+
T: AdcChannel + ?Sized,
90+
{
91+
#[inline]
92+
async fn read(&mut self) -> Result<u32, Self::Error> {
93+
(*self).read().await
94+
}
95+
}
96+
97+
#[cfg(test)]
98+
mod test {
99+
use super::*;
100+
101+
/// Scale an integer containing `bits` bits to 32 bits.
102+
fn scale_bits(raw_data: u32, bits: u32) -> u32 {
103+
let mut scaled_data: u32 = 0;
104+
105+
let mut remaining_bits = u32::BITS;
106+
while remaining_bits > 0 {
107+
let shl = bits.min(remaining_bits);
108+
scaled_data = (scaled_data.wrapping_shl(shl)) | (raw_data.wrapping_shr(bits - shl));
109+
remaining_bits -= shl;
110+
}
111+
112+
scaled_data
113+
}
114+
115+
#[test]
116+
fn scale_bits_i8_to_i32() {
117+
let raw_data = u32::from(i8::MIN as u8);
118+
let scaled_data = scale_bits(raw_data, 8);
119+
assert!(i32::MIN <= (scaled_data as i32) && (scaled_data as i32) <= (i32::MIN + 1 << 8));
120+
}
121+
122+
macro_rules! impl_adc {
123+
($Adc:ident, $bits:literal, $uint:ty) => {
124+
struct $Adc($uint);
125+
126+
impl $Adc {
127+
const MAX: $uint = !(<$uint>::MAX.wrapping_shl($bits - 1).wrapping_shl(1));
128+
129+
pub fn data(&mut self) -> $uint {
130+
self.0
131+
}
132+
}
133+
134+
impl ErrorType for $Adc {
135+
type Error = core::convert::Infallible;
136+
}
137+
138+
impl AdcChannel for $Adc {
139+
async fn read(&mut self) -> Result<u32, Self::Error> {
140+
Ok(scale_bits(u32::from(self.data()), $bits))
141+
}
142+
}
143+
};
144+
}
145+
146+
macro_rules! test_adc {
147+
($Adc:ident, $bits:literal, $uint:ty) => {{
148+
impl_adc!($Adc, $bits, $uint);
149+
150+
// 0 should always be scaled to 0.
151+
let mut adc_0 = $Adc(0);
152+
assert_eq!(adc_0.read().await, Ok(0));
153+
154+
// `$Adc::MAX` should always be scaled to `u32::MAX`.
155+
let mut adc_max = $Adc($Adc::MAX);
156+
assert_eq!(adc_max.read().await, Ok(u32::MAX));
157+
}};
158+
}
159+
160+
#[tokio::test]
161+
async fn test_8_bit() {
162+
test_adc!(Adc8, 8, u8);
163+
}
164+
165+
#[tokio::test]
166+
async fn test_12_bit() {
167+
test_adc!(Adc12, 12, u16);
168+
}
169+
170+
#[tokio::test]
171+
async fn test_16_bit() {
172+
test_adc!(Adc16, 16, u16);
173+
}
174+
175+
#[tokio::test]
176+
async fn test_24_bit() {
177+
test_adc!(Adc24, 24, u32);
178+
}
179+
180+
#[tokio::test]
181+
async fn test_32_bit() {
182+
test_adc!(Adc32, 32, u32);
183+
}
184+
}

embedded-hal-async/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))]
1010
#![allow(async_fn_in_trait)]
1111

12+
pub mod adc;
1213
pub mod delay;
1314
pub mod digital;
1415
pub mod i2c;

embedded-hal/src/adc.rs

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//! Blocking analog-digital conversion traits.
2+
3+
use core::fmt::Debug;
4+
5+
#[cfg(feature = "defmt-03")]
6+
use crate::defmt;
7+
8+
/// Read data from an ADC.
9+
///
10+
/// # Examples
11+
///
12+
/// In the first naive example, [`read`](crate::adc::AdcChannel::read) is implemented
13+
/// using a spin loop and only returns once data is ready.
14+
///
15+
/// ```
16+
/// use embedded_hal::adc::{AdcChannel, ErrorKind, ErrorType, Error};
17+
///
18+
/// struct MySpinningAdc;
19+
///
20+
/// impl MySpinningAdc {
21+
/// pub fn is_ready(&mut self) -> bool {
22+
/// // Just pretend this returns `false` the first few times.
23+
/// true
24+
/// }
25+
///
26+
/// pub fn data(&mut self) -> u32 {
27+
/// 42
28+
/// }
29+
/// }
30+
///
31+
/// impl ErrorType for MySpinningAdc {
32+
/// type Error = ErrorKind;
33+
/// }
34+
///
35+
/// impl AdcChannel for MySpinningAdc {
36+
/// fn read(&mut self) -> Result<u32, Self::Error> {
37+
/// while !self.is_ready() {
38+
/// core::hint::spin_loop();
39+
/// }
40+
///
41+
/// Ok(self.data())
42+
/// }
43+
/// }
44+
/// ```
45+
pub trait AdcChannel: ErrorType {
46+
/// Reads data from the ADC.
47+
///
48+
/// # Note for Implementers
49+
///
50+
/// This should wait until data is ready and then read it.
51+
/// If the ADC's precision is less than 32 bits, the value must be scaled accordingly.
52+
fn read(&mut self) -> Result<u32, Self::Error>;
53+
}
54+
55+
impl<T> AdcChannel for &mut T
56+
where
57+
T: AdcChannel + ?Sized,
58+
{
59+
#[inline]
60+
fn read(&mut self) -> Result<u32, Self::Error> {
61+
(*self).read()
62+
}
63+
}
64+
65+
/// ADC error.
66+
pub trait Error: Debug {
67+
/// Convert error to a generic ADC error kind.
68+
///
69+
/// By using this method, ADC errors freely defined by HAL implementations
70+
/// can be converted to a set of generic ADC errors upon which generic
71+
/// code can act.
72+
fn kind(&self) -> ErrorKind;
73+
}
74+
75+
impl Error for core::convert::Infallible {
76+
#[inline]
77+
fn kind(&self) -> ErrorKind {
78+
match *self {}
79+
}
80+
}
81+
82+
/// ADC error kind.
83+
///
84+
/// This represents a common set of ADC operation errors. HAL implementations are
85+
/// free to define more specific or additional error types. However, by providing
86+
/// a mapping to these common ADC errors, generic code can still react to them.
87+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
88+
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
89+
#[non_exhaustive]
90+
pub enum ErrorKind {
91+
/// A different error occurred. The original error may contain more information.
92+
Other,
93+
}
94+
95+
impl Error for ErrorKind {
96+
#[inline]
97+
fn kind(&self) -> ErrorKind {
98+
*self
99+
}
100+
}
101+
102+
impl core::fmt::Display for ErrorKind {
103+
#[inline]
104+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105+
match self {
106+
Self::Other => write!(
107+
f,
108+
"A different error occurred. The original error may contain more information"
109+
),
110+
}
111+
}
112+
}
113+
114+
/// ADC error type trait.
115+
///
116+
/// This just defines the error type, to be used by the other ADC traits.
117+
pub trait ErrorType {
118+
/// Error type.
119+
type Error: Error;
120+
}
121+
122+
impl<T: ErrorType + ?Sized> ErrorType for &mut T {
123+
type Error = T::Error;
124+
}

embedded-hal/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![warn(missing_docs)]
33
#![no_std]
44

5+
pub mod adc;
56
pub mod delay;
67
pub mod digital;
78
pub mod i2c;

0 commit comments

Comments
 (0)