avr_hal_generic/
adc.rs

1/// Analog-to-Digial converter
2use core::marker::PhantomData;
3
4/// The division factor between the system clock frequency and the input clock to the AD converter.
5///
6/// To get 10-bit precision, clock from 50kHz to 200kHz must be supplied.  If you need less
7/// precision, you can supply a higher clock.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum ClockDivider {
11    Factor2,
12    Factor4,
13    Factor8,
14    Factor16,
15    Factor32,
16    Factor64,
17    /// (default)
18    Factor128,
19}
20
21impl Default for ClockDivider {
22    fn default() -> Self {
23        Self::Factor128
24    }
25}
26
27/// Internal trait for the low-level ADC peripheral.
28///
29/// **Prefer using the [`Adc`] API instead of this trait.**
30pub trait AdcOps<H> {
31    /// Channel ID type for this ADC.
32    type Channel: PartialEq + Copy;
33
34    /// Settings type for this ADC.
35    type Settings: PartialEq + Copy;
36
37    /// Initialize the ADC peripheral with the specified settings.
38    ///
39    /// **Warning**: This is a low-level method and should not be called directly from user code.
40    fn raw_init(&mut self, settings: Self::Settings);
41
42    /// Read out the ADC data register.
43    ///
44    /// This method must only be called after a conversion completed.
45    ///
46    /// **Warning**: This is a low-level method and should not be called directly from user code.
47    fn raw_read_adc(&self) -> u16;
48
49    /// Check whether the ADC is currently converting a signal.
50    ///
51    /// **Warning**: This is a low-level method and should not be called directly from user code.
52    fn raw_is_converting(&self) -> bool;
53
54    /// Start a conversion on the currently selected channel.
55    ///
56    /// **Warning**: This is a low-level method and should not be called directly from user code.
57    fn raw_start_conversion(&mut self);
58
59    /// Set the multiplexer to a certain channel.
60    ///
61    /// **Warning**: This is a low-level method and should not be called directly from user code.
62    fn raw_set_channel(&mut self, channel: Self::Channel);
63
64    /// Set the DIDR (Digital Input Disable) for a certain channel.
65    ///
66    /// This disabled digital logic on the corresponding pin and allows measuring analog signals.
67    ///
68    /// **Warning**: This is a low-level method and should not be called directly from user code.
69    fn raw_enable_channel(&mut self, channel: Self::Channel);
70
71    /// Clear the DIDR (Digital Input Disable) for a certain channel.
72    ///
73    /// Enables digital logic on the corresponding pin after it has been used as an ADC channel.
74    ///
75    /// **Warning**: This is a low-level method and should not be called directly from user code.
76    fn raw_disable_channel(&mut self, channel: Self::Channel);
77}
78
79/// Trait marking a type as an ADC channel for a certain ADC.
80pub trait AdcChannel<H, ADC: AdcOps<H>> {
81    fn channel(&self) -> ADC::Channel;
82}
83
84/// Representation of any ADC Channel.
85///
86/// Typically, distinct types are used per channel, like for example `Pin<mode::Analog, PC0>`.  In
87/// some situations, however, a type is needed which can represent _any_ channel.  This is required
88/// to, for example, store multiple channels in an array.
89///
90/// `Channel` is such a type.  It can be created by calling the [`into_channel()`][into-channel]
91/// method of a distinct type:
92///
93/// ```
94/// let a0 = pins.a0.into_analog_input(&mut adc);
95/// let a1 = pins.a1.into_analog_input(&mut adc);
96///
97/// let channels: [atmega_hal::adc::Channel; 2] = [
98///     a0.into_channel(),
99///     a1.into_channel(),
100/// ];
101///
102/// for ch in channels.iter() {
103///     adc.read_blocking(ch);
104/// }
105/// ```
106///
107/// [into-channel]: crate::port::Pin::into_channel
108pub struct Channel<H, ADC: AdcOps<H>> {
109    ch: ADC::Channel,
110    _h: PhantomData<H>,
111}
112
113impl<H, ADC: AdcOps<H>> Channel<H, ADC> {
114    pub fn new<CH: AdcChannel<H, ADC>>(ch: CH) -> Self {
115        Self {
116            ch: ch.channel(),
117            _h: PhantomData,
118        }
119    }
120}
121
122impl<H, ADC: AdcOps<H>> AdcChannel<H, ADC> for Channel<H, ADC> {
123    #[inline]
124    fn channel(&self) -> ADC::Channel {
125        self.ch
126    }
127}
128
129/// Analog-to-Digital Converter
130/// ```
131/// let dp = atmega_hal::Peripherals::take().unwrap();
132/// let pins = atmega_hal::pins!(dp);
133/// let mut adc = atmega_hal::Adc::new(dp.ADC, Default::default());
134///
135/// let a0 = pins.pc0.into_analog_input(&mut adc);
136///
137/// // the following two calls are equivalent
138/// let voltage = a0.analog_read(&mut adc);
139/// let voltage = adc.read_blocking(&a0);
140///
141/// // alternatively, a non-blocking interface exists
142/// let voltage = nb::block!(adc.read_nonblocking(&a0)).unwrap_infallible();
143/// ```
144pub struct Adc<H, ADC: AdcOps<H>, CLOCK> {
145    p: ADC,
146    reading_channel: Option<ADC::Channel>,
147    _clock: PhantomData<CLOCK>,
148    _h: PhantomData<H>,
149}
150
151impl<H, ADC, CLOCK> Adc<H, ADC, CLOCK>
152where
153    ADC: AdcOps<H>,
154    CLOCK: crate::clock::Clock,
155{
156    pub fn new(p: ADC, settings: ADC::Settings) -> Self {
157        let mut adc = Self {
158            p,
159            reading_channel: None,
160            _clock: PhantomData,
161            _h: PhantomData,
162        };
163        adc.initialize(settings);
164        adc
165    }
166
167    pub fn initialize(&mut self, settings: ADC::Settings) {
168        self.p.raw_init(settings);
169    }
170
171    #[inline]
172    pub(crate) fn enable_pin<PIN: AdcChannel<H, ADC>>(&mut self, pin: &PIN) {
173        self.p.raw_enable_channel(pin.channel());
174    }
175
176    #[inline]
177    pub(crate) fn disable_pin<PIN: AdcChannel<H, ADC>>(&mut self, pin: &PIN) {
178        self.p.raw_disable_channel(pin.channel());
179    }
180
181    pub fn read_blocking<PIN: AdcChannel<H, ADC>>(&mut self, pin: &PIN) -> u16 {
182        // assert!(self.reading_channel.is_none());
183        self.p.raw_set_channel(pin.channel());
184        self.p.raw_start_conversion();
185        while self.p.raw_is_converting() {}
186        self.p.raw_read_adc()
187    }
188
189    pub fn read_nonblocking<PIN: AdcChannel<H, ADC>>(
190        &mut self,
191        pin: &PIN,
192    ) -> nb::Result<u16, core::convert::Infallible> {
193        match (&self.reading_channel, self.p.raw_is_converting()) {
194            // Measurement on current pin is ongoing
195            (Some(channel), true) if *channel == pin.channel() => Err(nb::Error::WouldBlock),
196            // Measurement on current pin completed
197            (Some(channel), false) if *channel == pin.channel() => {
198                self.reading_channel = None;
199                Ok(self.p.raw_read_adc())
200            }
201            // Measurement on other pin is ongoing
202            (Some(_), _) => {
203                self.reading_channel = None;
204                Err(nb::Error::WouldBlock)
205            }
206            // Start measurement
207            (None, _) => {
208                self.reading_channel = Some(pin.channel());
209                self.p.raw_set_channel(pin.channel());
210                self.p.raw_start_conversion();
211                Err(nb::Error::WouldBlock)
212            }
213        }
214    }
215}
216
217#[macro_export]
218macro_rules! impl_adc {
219    (
220        hal: $HAL:ty,
221        peripheral: $ADC:ty,
222        settings: $Settings:ty,
223        apply_settings: |$settings_periph_var:ident, $settings_var:ident| $apply_settings:block,
224        channel_id: $Channel:ty,
225        set_channel: |$periph_var:ident, $chan_var:ident| $set_channel:block,
226        pins: {
227            $(
228                $(#[$pin_attr:meta])*
229                $pin:ty: ($pin_channel:expr$(, $didr:ident::$didr_method:ident)?),
230            )+
231        },
232        $(channels: {
233            $(
234                $(#[$channel_attr:meta])*
235                $channel_ty:ty: $channel:expr,
236            )*
237        },)?
238    ) => {
239        impl $crate::adc::AdcOps<$HAL> for $ADC {
240            type Channel = $Channel;
241            type Settings = $Settings;
242
243            #[inline]
244            fn raw_init(&mut self, settings: Self::Settings) {
245                let $settings_periph_var = self;
246                let $settings_var = settings;
247
248                $apply_settings
249            }
250
251            #[inline]
252            fn raw_read_adc(&self) -> u16 {
253                self.adc.read().bits()
254            }
255
256            #[inline]
257            fn raw_is_converting(&self) -> bool {
258                self.adcsra.read().adsc().bit_is_set()
259            }
260
261            #[inline]
262            fn raw_start_conversion(&mut self) {
263                self.adcsra.modify(|_, w| w.adsc().set_bit());
264            }
265
266            #[inline]
267            fn raw_set_channel(&mut self, channel: Self::Channel) {
268                let $periph_var = self;
269                let $chan_var = channel;
270
271                $set_channel
272            }
273
274            #[inline]
275            fn raw_enable_channel(&mut self, channel: Self::Channel) {
276                match channel {
277                    $(
278                        x if x == $pin_channel => {
279                            $(self.$didr.modify(|_, w| w.$didr_method().set_bit());)?
280                        }
281                    )+
282                    _ => unreachable!(),
283                }
284            }
285
286            #[inline]
287            fn raw_disable_channel(&mut self, channel: Self::Channel) {
288                match channel {
289                    $(
290                        x if x == $pin_channel => {
291                            $(self.$didr.modify(|_, w| w.$didr_method().clear_bit());)?
292                        }
293                    )+
294                    _ => unreachable!(),
295                }
296            }
297        }
298
299        $(
300        $(#[$pin_attr])*
301        impl $crate::adc::AdcChannel<$HAL, $ADC> for $crate::port::Pin<$crate::port::mode::Analog, $pin> {
302            #[inline]
303            fn channel(&self) -> $Channel {
304                $pin_channel
305            }
306        }
307        )+
308
309        $($(
310        $(#[$channel_attr])*
311        impl $crate::adc::AdcChannel<$HAL, $ADC> for $channel_ty {
312            #[inline]
313            fn channel(&self) -> $Channel {
314                $channel
315            }
316        }
317
318        /// Convert this channel into a generic "[`Channel`][adc-channel]" type.
319        ///
320        /// The generic channel type can be used to store multiple channels in an array.
321        ///
322        /// [adc-channel]: crate::adc::Channel
323        $(#[$channel_attr])*
324        impl $channel_ty {
325            pub fn into_channel(self) -> $crate::adc::Channel<$HAL, $ADC> {
326                $crate::adc::Channel::new(self)
327            }
328        }
329        )*)?
330    };
331}