atsamd_hal/peripherals/adc/
mod.rs

1//! Analog-to-Digital Converter
2//!
3//! This module provides an interface to the Analog-to-Digital Converter (ADC)
4//! peripheral(s).  Support is provided for single-ended, software triggered
5//! conversions.  Async functionality can be enabled via the `async` feature.
6//!
7//! ```
8//! # use atsamd_hal::adc::{AdcResolution, Reference};
9//! let apb_adc0 = buses.apb.enable(tokens.apbs.adc0);
10//! let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0);
11//!
12//! let mut adc = AdcBuilder::new(Accumulation::single(AdcResolution::_12))
13//!     .with_clock_cycles_per_sample(5)
14//!     .with_clock_divider(Prescaler::Div32)
15//!     .with_vref(Reference::Arefa)
16//!     .enable(peripherals.adc0, apb_adc0, &pclk_adc0)
17//!     .unwrap();
18//!
19//! let mut adc_pin = pins.a0.into_alternate();
20//!
21//! let mut _buffer = [0; 16];
22//! adc.read_buffer(&mut adc_pin, &mut _buffer).unwrap();
23//! ```
24
25use core::ops::Deref;
26
27use atsamd_hal_macros::{hal_cfg, hal_module};
28use pac::Peripherals;
29
30use crate::{gpio::AnyPin, pac, typelevel::Sealed};
31
32#[hal_module(
33    any("adc-d11", "adc-d21") => "d11/mod.rs",
34    "adc-d5x" => "d5x/mod.rs",
35)]
36mod impls {}
37
38pub use impls::*;
39
40#[cfg(feature = "async")]
41mod async_api;
42#[cfg(feature = "async")]
43pub use async_api::*;
44
45mod builder;
46pub use builder::*;
47
48#[hal_cfg(any("adc-d11", "adc-d21"))]
49use crate::pac::adc as adc0;
50#[hal_cfg("adc-d5x")]
51use crate::pac::adc0;
52
53pub use adc0::refctrl::Refselselect as Reference;
54
55/// ADC Settings when reading Internal sensors (Like VREF and Temperatures)
56/// These settings are based on the minimums suggested in the datasheet
57const ADC_SETTINGS_INTERNAL_READ: AdcSettings = AdcSettings {
58    clk_divider: Prescaler::Div64,
59    sample_clock_cycles: 32,
60    accumulation: Accumulation::average(SampleCount::_4),
61    vref: Reference::Intvcc1,
62};
63
64/// Based on Temperature log row information (NVM)x
65#[hal_cfg(any("adc-d21", "adc-d11"))]
66const ADC_SETTINGS_INTERNAL_READ_D21_TEMP: AdcSettings = AdcSettings {
67    clk_divider: Prescaler::Div64,
68    sample_clock_cycles: 32,
69    accumulation: Accumulation::average(SampleCount::_4),
70    vref: Reference::Int1v,
71};
72
73/// Errors that may occur when operating the ADC
74#[derive(Debug, Copy, Clone)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76pub enum Error {
77    /// Clock too fast.
78    ///
79    /// The ADC requires that it's fed a GCLK that does not exceed a certain
80    /// frequency. These maximums are:
81    ///
82    /// * **SAMD/E5x** - 100Mhz
83    /// * **SAMC/D21** - 48Mhz
84    /// * **SAMD11** - 48Mhz
85    ///
86    /// SAMx51 specific: If you are running the CPU at temperatures past 100C,
87    /// then the maximum GCLK clock speed should be 90Mhz
88    ClockTooFast,
89    /// Buffer overflowed
90    BufferOverrun,
91}
92
93/// Voltage source to use when using the ADC to measure the CPU voltage
94#[hal_cfg("adc-d5x")]
95#[derive(Copy, Clone, PartialEq, Eq)]
96#[repr(u8)]
97pub enum CpuVoltageSource {
98    /// Core voltage
99    Core = 0x18,
100    /// VBAT supply voltage
101    Vbat = 0x19,
102    /// IO supply voltage
103    Io = 0x1A,
104}
105
106/// Voltage source to use when using the ADC to measure the CPU voltage
107#[hal_cfg(any("adc-d21", "adc-d11"))]
108#[derive(Copy, Clone, PartialEq, Eq)]
109#[repr(u8)]
110pub enum CpuVoltageSource {
111    /// Bandgap reference voltage - 1.1V
112    Bandgap = 0x19,
113    /// Core voltage - 1.2V
114    Core = 0x1A,
115    /// IO voltage - 1.62V to 3.63V
116    Io = 0x1B,
117}
118
119bitflags::bitflags! {
120    /// ADC interrupt flags
121    #[derive(Clone, Copy)]
122    pub struct Flags: u8 {
123        /// Window monitor interrupt
124        const WINMON = 0x04;
125        /// Buffer overrun interrupt
126        const OVERRUN = 0x02;
127        /// Result ready interrupt
128        const RESRDY = 0x01;
129    }
130}
131
132/// Marker for which ADC has access to the CPUs internal sensors
133pub trait PrimaryAdc {}
134
135/// Trait representing an ADC instance
136pub trait AdcInstance {
137    #[cfg(feature = "async")]
138    type Interrupt: crate::async_hal::interrupts::InterruptSource;
139
140    // The Adc0 and Adc1 PAC types implement Deref
141    type Instance: Deref<Target = adc0::RegisterBlock>;
142
143    #[hal_cfg("adc-d5x")]
144    type ClockId: crate::clock::v2::apb::ApbId + crate::clock::v2::pclk::PclkId;
145
146    fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock;
147
148    #[hal_cfg(any("adc-d11", "adc-d21"))]
149    fn enable_pm(pm: &mut pac::Pm);
150
151    fn calibrate(instance: &Self::Instance);
152
153    #[cfg(feature = "async")]
154    fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker;
155}
156
157/// Trait representing a GPIO pin which can be used as an input for an ADC
158pub trait AdcPin<I>: AnyPin<Mode = crate::gpio::AlternateB> + Sealed
159where
160    I: AdcInstance,
161{
162    const CHANNEL: u8;
163}
164
165/// ADC Instance
166#[hal_cfg(any("adc-d11", "adc-d21"))]
167pub struct Adc<I: AdcInstance> {
168    adc: I::Instance,
169    cfg: AdcSettings,
170    discard: bool,
171}
172
173/// ADC Instance
174#[hal_cfg("adc-d5x")]
175pub struct Adc<I: AdcInstance> {
176    adc: I::Instance,
177    _apbclk: crate::clock::v2::apb::ApbClk<I::ClockId>,
178    cfg: AdcSettings,
179    discard: bool,
180}
181
182#[cfg(feature = "async")]
183pub struct FutureAdc<I: AdcInstance, F> {
184    inner: Adc<I>,
185    irqs: F,
186}
187
188impl<I: AdcInstance> Adc<I> {
189    /// Construct a new ADC instance
190    ///
191    /// ## Important
192    ///
193    /// This function will return `Err` if the clock source provided
194    /// is faster than 100 MHz, since this is the maximum frequency for
195    /// GCLK_ADCx as per the datasheet.
196    ///
197    /// The [`new`](Self::new) function currently takes an `&` reference to a
198    /// [`Pclk`](crate::clock::v2::pclk::Pclk). In the future this will likely
199    /// change to taking full ownership of it; in the meantime, you must ensure
200    /// that the PCLK is enabled for the `Adc` struct's lifetime.
201    ///
202    /// NOTE: If you plan to run the chip above 100°C, then the maximum GCLK
203    /// frequency for the ADC is restricted to 90Mhz for stable performance.
204    #[hal_cfg("adc-d5x")]
205    #[inline]
206    pub(crate) fn new<PS: crate::clock::v2::pclk::PclkSourceId>(
207        adc: I::Instance,
208        settings: AdcSettings,
209        clk: crate::clock::v2::apb::ApbClk<I::ClockId>,
210        pclk: &crate::clock::v2::pclk::Pclk<I::ClockId, PS>,
211    ) -> Result<Self, Error> {
212        // TODO: Ideally, the ADC struct would take ownership of the Pclk type here.
213        // However, since clock::v2 is not implemented for all chips yet, the
214        // generics for the Adc type would be different between chip families,
215        // leading to massive and unnecessary code duplication. In the meantime,
216        // we use a "lite" variation of the typelevel guarantees laid out by the
217        // clock::v2 module, meaning that we can guarantee that the clocks are enabled
218        // at the time of creation of the Adc struct; however we can't guarantee
219        // that the clock will stay enabled for the duration of its lifetime.
220
221        if pclk.freq() > fugit::HertzU32::from_raw(100_000_000) {
222            // Clock source is too fast
223            return Err(Error::ClockTooFast);
224        }
225
226        let mut new_adc = Self {
227            adc,
228            _apbclk: clk,
229            cfg: settings,
230            discard: true,
231        };
232        new_adc.configure(settings);
233        Ok(new_adc)
234    }
235
236    /// Construct a new ADC instance
237    ///
238    /// ## Important
239    ///
240    /// This function will return [Error::ClockTooFast] if the clock source
241    /// provided is faster than 48 MHz, since this is the maximum frequency
242    /// for the ADC as per the datasheet.
243    #[hal_cfg(any("adc-d11", "adc-d21"))]
244    #[inline]
245    pub(crate) fn new(
246        adc: I::Instance,
247        settings: AdcSettings,
248        pm: &mut pac::Pm,
249        clock: &crate::clock::AdcClock,
250    ) -> Result<Self, Error> {
251        if (clock.freq() as crate::time::Hertz).to_Hz() > 48_000_000 {
252            // Clock source is too fast
253            return Err(Error::ClockTooFast);
254        }
255
256        I::enable_pm(pm);
257        let mut new_adc = Self {
258            adc,
259            cfg: settings,
260            discard: true,
261        };
262        new_adc.configure(settings);
263        Ok(new_adc)
264    }
265
266    /// Switch the ['Adc'] to ['FutureAdc'], allowing for the use of async
267    /// reading methods. You are required to provide the struct created by
268    /// the [`bind_interrupts`](crate::bind_interrupts) macro to prove
269    /// that the interrupt sources have been correctly configured. This function
270    /// will automatically enable the relevant NVIC interrupt sources. However,
271    /// you are required to configure the desired interrupt priorities prior to
272    /// calling this method. Consult [`crate::async_hal::interrupts`]
273    /// module-level documentation for more information.
274    /// [`bind_interrupts`](crate::bind_interrupts).
275    #[cfg(feature = "async")]
276    #[atsamd_hal_macros::hal_macro_helper]
277    #[inline]
278    pub fn into_future<F>(self, irqs: F) -> FutureAdc<I, F>
279    where
280        F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
281    {
282        use crate::async_hal::interrupts::InterruptSource;
283        unsafe {
284            I::Interrupt::unpend();
285            I::Interrupt::enable();
286        }
287        FutureAdc { inner: self, irqs }
288    }
289}
290
291impl<I: AdcInstance> Adc<I> {
292    /// Converts our ADC Reading (0-n) to the range 0.0-1.0, where
293    /// 1.0 = 2^(reading_bitwidth)
294    fn reading_to_f32(&self, raw: u16) -> f32 {
295        let max = match self.cfg.accumulation.output_resolution() {
296            Resolution::_16bit => 65535,
297            Resolution::_12bit => 4095,
298            Resolution::_10bit => 1023,
299            Resolution::_8bit => 255,
300        };
301        raw as f32 / max as f32
302    }
303
304    /// Runs something using the ADC with overridden settings
305    ///
306    /// This is used mainly for internal voltage readings, where the ADC
307    /// must be configured with specific settings for optimal and accurate
308    /// reading
309    pub(crate) fn with_specific_settings<F: FnOnce(&mut Adc<I>) -> T, T>(
310        &mut self,
311        settings: AdcSettings,
312        f: F,
313    ) -> T {
314        let old_cfg = self.cfg;
315        self.configure(settings);
316        let ret = f(self);
317        self.configure(old_cfg);
318        ret
319    }
320
321    #[inline]
322    fn set_reference(&mut self, reference: Reference) {
323        self.adc
324            .refctrl()
325            .modify(|_, w| w.refsel().variant(reference));
326        self.sync();
327    }
328
329    /// Read a single value from the provided ADC pin.
330    #[inline]
331    pub fn read<P: AdcPin<I>>(&mut self, _pin: &mut P) -> u16 {
332        self.read_channel(P::CHANNEL)
333    }
334
335    /// Read a single value from the provided channel, in a blocking fashion
336    #[inline]
337    fn read_channel(&mut self, ch: u8) -> u16 {
338        // Clear overrun errors that might've occured before we try to read anything
339        self.clear_all_flags();
340        self.disable_interrupts(Flags::all());
341        self.disable_freerunning();
342        self.sync();
343        self.mux(ch);
344        self.check_read_discard();
345        self.start_conversion();
346        while !self.read_flags().contains(Flags::RESRDY) {
347            core::hint::spin_loop();
348        }
349        self.conversion_result()
350    }
351
352    // If the ADC has to discard the next value, then we try to read it
353    // and then discard it
354    #[inline]
355    pub fn check_read_discard(&mut self) {
356        if self.discard {
357            self.start_conversion();
358            while !self.read_flags().contains(Flags::RESRDY) {
359                core::hint::spin_loop();
360            }
361            self.discard = false;
362        }
363    }
364
365    /// Read into a buffer from the provided ADC pin, in a blocking fashion
366    #[inline]
367    pub fn read_buffer<P: AdcPin<I>>(
368        &mut self,
369        _pin: &mut P,
370        dst: &mut [u16],
371    ) -> Result<(), Error> {
372        self.read_buffer_channel(P::CHANNEL, dst)
373    }
374
375    /// Read into a buffer from the provided channel, in a blocking fashion
376    #[inline]
377    fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> {
378        // Clear overrun errors that might've occured before we try to read anything
379        self.clear_all_flags();
380        self.disable_interrupts(Flags::all());
381        self.mux(ch);
382        self.enable_freerunning();
383        self.start_conversion();
384        if self.discard {
385            // Discard first result
386            while !self.read_flags().contains(Flags::RESRDY) {
387                core::hint::spin_loop();
388            }
389            self.clear_all_flags();
390            self.discard = false;
391        }
392
393        for result in dst.iter_mut() {
394            while !self.read_flags().contains(Flags::RESRDY) {
395                core::hint::spin_loop();
396            }
397
398            let flags = self.read_flags();
399            self.clear_all_flags();
400            if let Err(e) = self.check_overrun(&flags) {
401                //self.power_down();
402                self.disable_freerunning();
403
404                return Err(e);
405            }
406
407            *result = self.conversion_result();
408        }
409        //self.power_down();
410        self.disable_freerunning();
411
412        Ok(())
413    }
414
415    /// Return the underlying ADC PAC object.
416    #[hal_cfg(any("adc-d11", "adc-d21"))]
417    #[inline]
418    pub fn free(mut self) -> I::Instance {
419        self.software_reset();
420        self.adc
421    }
422
423    /// Return the underlying ADC PAC object and the enabled APB ADC clock.
424    #[hal_cfg("adc-d5x")]
425    #[inline]
426    pub fn free(mut self) -> (I::Instance, crate::clock::v2::apb::ApbClk<I::ClockId>) {
427        self.software_reset();
428        (self.adc, self._apbclk)
429    }
430
431    /// Reset the peripheral.
432    ///
433    /// This also disables the ADC.
434    #[inline]
435    fn software_reset(&mut self) {
436        self.adc.ctrla().modify(|_, w| w.swrst().set_bit());
437        self.sync();
438    }
439}
440
441#[cfg(feature = "async")]
442/// Implementation for async mode only methods
443impl<I: AdcInstance, F> FutureAdc<I, F>
444where
445    F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
446{
447    /// Convert the Async ADC back into a Blocking ADC, and return
448    /// the IRQs
449    pub fn into_blocking(self) -> (Adc<I>, F) {
450        (self.inner, self.irqs)
451    }
452
453    /// Read a single value from the provided ADC pin.
454    #[inline]
455    pub async fn read<P: AdcPin<I>>(&mut self, _pin: &mut P) -> u16 {
456        self.read_channel(P::CHANNEL).await
457    }
458
459    /// Read a single value from the provided channel ID
460    #[inline]
461    async fn read_channel(&mut self, ch: u8) -> u16 {
462        // Clear overrun errors that might've occured before we try to read anything
463        self.inner.clear_all_flags();
464        self.inner.disable_freerunning();
465        self.inner.mux(ch);
466        if self.inner.discard {
467            // Read and discard if something was changed
468            self.inner.start_conversion();
469            let _ = self.wait_flags(Flags::RESRDY).await;
470            self.inner.discard = false;
471            let _ = self.inner.conversion_result();
472        }
473        self.inner.start_conversion();
474        // Here we explicitly ignore the result, because we know that
475        // overrun errors are impossible since the ADC is configured in one-shot mode.
476        let _ = self.wait_flags(Flags::RESRDY).await;
477        let res = self.inner.conversion_result();
478        //self.inner.power_down();
479        self.inner.sync();
480        res
481    }
482
483    /// Read into a buffer from the provided ADC pin
484    #[inline]
485    pub async fn read_buffer<P: AdcPin<I>>(
486        &mut self,
487        _pin: &mut P,
488        dst: &mut [u16],
489    ) -> Result<(), Error> {
490        self.read_buffer_channel(P::CHANNEL, dst).await
491    }
492
493    /// Read into a buffer from the provided channel ID
494    #[inline]
495    async fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> {
496        // Clear overrun errors that might've occured before we try to read anything
497        self.inner.clear_all_flags();
498        self.inner.mux(ch);
499        self.inner.enable_freerunning();
500
501        if self.inner.discard {
502            // Discard first result
503            let _ = self.wait_flags(Flags::RESRDY).await;
504            let _ = self.inner.conversion_result();
505            self.inner.discard = false;
506            self.inner.clear_all_flags();
507        }
508
509        // Don't re-trigger start conversion now, its already enabled in free running
510        for result in dst.iter_mut() {
511            if let Err(e) = self.wait_flags(Flags::RESRDY).await {
512                //self.inner.power_down();
513                self.inner.disable_freerunning();
514
515                return Err(e);
516            }
517            *result = self.inner.conversion_result();
518        }
519
520        //self.inner.power_down();
521        self.inner.disable_freerunning();
522
523        Ok(())
524    }
525}