atsamd_hal/peripherals/adc/d5x/
mod.rs

1pub mod pin;
2
3use pac::Supc;
4
5#[cfg(feature = "async")]
6use super::{FutureAdc, async_api};
7
8use super::{
9    ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource,
10    Error, Flags, PrimaryAdc, SampleCount,
11};
12use crate::{calibration, pac};
13
14/// ADC instance 0
15pub struct Adc0 {
16    _adc: pac::Adc0,
17}
18
19impl PrimaryAdc for Adc0 {}
20
21impl AdcInstance for Adc0 {
22    type Instance = pac::Adc0;
23
24    type ClockId = crate::clock::v2::pclk::ids::Adc0;
25
26    #[cfg(feature = "async")]
27    type Interrupt = crate::async_hal::interrupts::ADC0;
28
29    #[inline]
30    fn peripheral_reg_block(p: &mut pac::Peripherals) -> &pac::adc0::RegisterBlock {
31        &p.adc0
32    }
33
34    #[inline]
35    fn calibrate(instance: &Self::Instance) {
36        instance.calib().write(|w| unsafe {
37            w.biascomp().bits(calibration::adc0_biascomp_scale_cal());
38            w.biasrefbuf().bits(calibration::adc0_biasref_scale_cal());
39            w.biasr2r().bits(calibration::adc0_biasr2r_scale_cal())
40        });
41    }
42
43    #[cfg(feature = "async")]
44    #[inline]
45    fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
46        use super::async_api;
47        &async_api::ADC_WAKERS[0]
48    }
49}
50
51#[inline]
52/// Convert TP and TC values to degrees C for CPU temperature
53fn tp_tc_to_temp(tp: f32, tc: f32) -> f32 {
54    let tl = calibration::tl();
55    let th = calibration::th();
56    let vpl = calibration::vpl() as f32;
57    let vph = calibration::vph() as f32;
58    let vcl = calibration::vcl() as f32;
59    let vch = calibration::vch() as f32;
60
61    ((tl * vph * tc) - (vpl * th * tc) - (tl * vch * tp) + (th * vcl * tp))
62        / ((vcl * tp) - (vch * tp) - (vpl * tc) + (vph * tc))
63}
64
65/// ADC instance 0
66pub struct Adc1 {
67    _adc: pac::Adc1,
68}
69
70impl AdcInstance for Adc1 {
71    type Instance = pac::Adc1;
72
73    type ClockId = crate::clock::v2::pclk::ids::Adc1;
74
75    #[cfg(feature = "async")]
76    type Interrupt = crate::async_hal::interrupts::ADC1;
77
78    #[inline]
79    fn peripheral_reg_block(p: &mut pac::Peripherals) -> &pac::adc0::RegisterBlock {
80        &p.adc1
81    }
82
83    #[inline]
84    fn calibrate(instance: &Self::Instance) {
85        instance.calib().write(|w| unsafe {
86            w.biascomp().bits(calibration::adc1_biascomp_scale_cal());
87            w.biasrefbuf().bits(calibration::adc1_biasref_scale_cal());
88            w.biasr2r().bits(calibration::adc1_biasr2r_scale_cal())
89        });
90    }
91
92    #[cfg(feature = "async")]
93    #[inline]
94    fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
95        use super::async_api;
96        &async_api::ADC_WAKERS[1]
97    }
98}
99
100impl<I: AdcInstance> Adc<I> {
101    #[inline]
102    /// Configures the ADC.
103    pub(crate) fn configure(&mut self, cfg: AdcSettings) {
104        if cfg != self.cfg {
105            // Set discard flag for next read
106            self.discard = true;
107        }
108        // Stop ADC
109        self.power_down();
110        self.sync();
111        I::calibrate(&self.adc);
112        self.sync();
113        self.adc
114            .ctrla()
115            .modify(|_, w| w.prescaler().variant(cfg.clk_divider));
116        self.sync();
117        self.adc
118            .ctrlb()
119            .modify(|_, w| w.ressel().variant(cfg.accumulation.resolution()));
120        self.sync();
121
122        self.adc
123            .sampctrl()
124            .modify(|_, w| unsafe { w.samplen().bits(cfg.sample_clock_cycles.saturating_sub(1)) }); // sample length
125        self.sync();
126        self.adc.inputctrl().modify(|_, w| {
127            w.muxneg().gnd();
128            w.diffmode().clear_bit()
129        }); // No negative input (internal gnd)
130        self.sync();
131        let (sample_cnt, adjres) = match cfg.accumulation {
132            // 1 sample to be used as is
133            Accumulation::Single(_) => (SampleCount::_1, 0),
134            // A total of `adc_sample_count` elements will be averaged by the ADC
135            // before it returns the result
136            // Table 45-3 SAMx5x datasheet
137            Accumulation::Average(cnt) => (cnt, core::cmp::min(cnt as u8, 0x04)),
138            // A total of `adc_sample_count` elements will be summed by the ADC
139            // before it returns the result
140            Accumulation::Summed(cnt) => (cnt, 0),
141        };
142        self.adc.avgctrl().modify(|_, w| {
143            w.samplenum().variant(sample_cnt);
144            unsafe { w.adjres().bits(adjres) }
145        });
146        self.sync();
147        self.set_reference(cfg.vref);
148        self.sync();
149        self.adc.ctrla().modify(|_, w| w.enable().set_bit());
150        self.sync();
151        self.cfg = cfg;
152        self.power_up();
153    }
154}
155
156impl<I: AdcInstance + PrimaryAdc> Adc<I> {
157    #[inline]
158    /// Reads the CPU temperature in degrees C.
159    ///
160    /// n.b. Microchip's errata document for SAM D5x/E5x states:
161    /// > Both internal temperature sensors, TSENSP and TSENSC, are not
162    /// > supported and should not be used.
163    pub fn read_cpu_temperature(&mut self, supc: &mut Supc) -> f32 {
164        let old_state = supc.vref().read().bits();
165        supc.vref().modify(|_, w| {
166            w.ondemand().set_bit();
167            w.tsen().set_bit()
168        });
169
170        let (tp, tc) = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| {
171            (
172                adc.read_channel(0x1C) as f32, // Tp
173                adc.read_channel(0x1D) as f32, // Tc
174            )
175        });
176        // Restore vrefs old state
177        supc.vref().write(|w| unsafe { w.bits(old_state) });
178
179        tp_tc_to_temp(tp, tc)
180    }
181
182    #[inline]
183    pub fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 {
184        let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| {
185            let res = adc.read_channel(src as u8);
186            adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled
187        });
188        (voltage * 1000.0) as u16
189    }
190}
191
192impl<I: AdcInstance> Adc<I> {
193    #[inline]
194    pub(super) fn sync(&self) {
195        // Slightly more performant than checking the individual bits
196        // since we avoid an extra instruction to bit shift
197        while self.adc.syncbusy().read().bits() != 0 {
198            core::hint::spin_loop();
199        }
200    }
201
202    #[inline]
203    pub(super) fn power_up(&mut self) {
204        self.adc.ctrla().modify(|_, w| w.enable().set_bit());
205        self.sync();
206    }
207
208    #[inline]
209    #[allow(dead_code)]
210    pub(super) fn power_down(&mut self) {
211        self.adc.ctrla().modify(|_, w| w.enable().clear_bit());
212        self.sync();
213    }
214
215    #[inline]
216    pub(super) fn start_conversion(&mut self) {
217        self.adc.swtrig().modify(|_, w| w.start().set_bit());
218    }
219
220    #[inline]
221    pub(super) fn enable_freerunning(&mut self) {
222        self.adc.ctrlb().modify(|_, w| w.freerun().set_bit());
223        self.sync();
224    }
225
226    #[inline]
227    pub(super) fn disable_freerunning(&mut self) {
228        self.adc.ctrlb().modify(|_, w| w.freerun().clear_bit());
229        self.sync();
230    }
231
232    #[inline]
233    pub(super) fn read_flags(&self) -> Flags {
234        let bits = self.adc.intflag().read().bits();
235        Flags::from_bits_truncate(bits)
236    }
237
238    #[cfg(feature="async")]
239    /// Clear the specified interrupt flags
240    #[inline]
241    pub(super) fn clear_flags(&mut self, flags: &Flags) {
242        unsafe {
243            self.adc.intflag().write(|w| w.bits(flags.bits()));
244        }
245    }
246
247    /// Clear all interrupt flags
248    #[inline]
249    pub(super) fn clear_all_flags(&mut self) {
250        unsafe {
251            self.adc.intflag().write(|w| w.bits(0b111));
252        }
253    }
254
255    /// Check whether the provided flags contain an `OVERRUN` error
256    #[inline]
257    pub(super) fn check_overrun(&mut self, flags: &Flags) -> Result<(), Error> {
258        if flags.contains(Flags::OVERRUN) {
259            Err(Error::BufferOverrun)
260        } else {
261            Ok(())
262        }
263    }
264
265    /// Enables an interrupt when conversion is ready.
266    #[inline]
267    #[allow(dead_code)]
268    pub(super) fn enable_interrupts(&mut self, flags: Flags) {
269        unsafe { self.adc.intenset().write(|w| w.bits(flags.bits())) };
270    }
271
272    /// Disables the interrupt for when conversion is ready.
273    #[inline]
274    pub(super) fn disable_interrupts(&mut self, flags: Flags) {
275        unsafe { self.adc.intenclr().write(|w| w.bits(flags.bits())) };
276    }
277
278    #[inline]
279    pub(super) fn conversion_result(&self) -> u16 {
280        self.adc.result().read().result().bits()
281    }
282
283    #[inline]
284    pub(super) fn mux(&mut self, ch: u8) {
285        self.adc.inputctrl().modify(|r, w| {
286            if r.muxpos().bits() != ch {
287                self.discard = true;
288            }
289            unsafe { w.muxpos().bits(ch) }
290        });
291        self.sync()
292    }
293}
294
295#[cfg(feature = "async")]
296impl<I: AdcInstance + PrimaryAdc, F> FutureAdc<I, F>
297where
298    F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
299{
300    /// Reads the CPU temperature in degrees C.
301    ///
302    /// n.b. Microchip's errata document for SAM D5x/E5x states:
303    /// > Both internal temperature sensors, TSENSP and TSENSC, are not
304    /// > supported and should not be used.
305    pub async fn read_cpu_temperature(&mut self, supc: &mut Supc) -> f32 {
306        // We cannot pass into an async closure yet, so settings switching
307        // has to be done manually!
308        let old_adc_settings = self.inner.cfg;
309        let old_state = supc.vref().read().bits();
310        supc.vref().modify(|_, w| {
311            w.ondemand().set_bit();
312            w.tsen().set_bit()
313        });
314
315        self.inner.configure(ADC_SETTINGS_INTERNAL_READ);
316        let tp = self.read_channel(0x1C).await as f32;
317        let tc = self.read_channel(0x1D).await as f32;
318        // Restore vrefs old state
319        supc.vref().write(|w| unsafe { w.bits(old_state) });
320        self.inner.configure(old_adc_settings);
321        tp_tc_to_temp(tp, tc)
322    }
323
324    /// Reads a CPU voltage source. Value returned is in millivolts (mV)
325    pub async fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 {
326        let old_adc_settings = self.inner.cfg;
327        self.inner.configure(ADC_SETTINGS_INTERNAL_READ);
328
329        let res = self.read_channel(src as u8).await;
330
331        // x4 since the voltages are 1/4 scaled
332        let voltage = self.inner.reading_to_f32(res) * 3.3 * 4.0;
333
334        self.inner.configure(old_adc_settings);
335        (voltage * 1000.0) as u16
336    }
337}