atsamd_hal/peripherals/adc/d11/
mod.rs

1use super::{
2    ADC_SETTINGS_INTERNAL_READ, ADC_SETTINGS_INTERNAL_READ_D21_TEMP, Accumulation, Adc,
3    AdcInstance, AdcSettings, CpuVoltageSource, Error, Flags, PrimaryAdc, SampleCount,
4};
5
6#[cfg(feature = "async")]
7use super::{FutureAdc, async_api};
8
9use crate::{calibration, pac};
10use pac::Peripherals;
11use pac::Sysctrl;
12use pac::adc::inputctrl::Gainselect;
13pub mod pin;
14
15/// Wrapper around the ADC instance
16pub struct Adc0 {
17    _adc: pac::Adc,
18}
19
20impl PrimaryAdc for Adc0 {}
21
22impl AdcInstance for Adc0 {
23    type Instance = pac::Adc;
24
25    #[cfg(feature = "async")]
26    type Interrupt = crate::async_hal::interrupts::ADC;
27
28    #[inline]
29    fn peripheral_reg_block(p: &mut Peripherals) -> &pac::adc::RegisterBlock {
30        &p.adc
31    }
32
33    #[inline]
34    fn enable_pm(pm: &mut pac::Pm) {
35        pm.apbcmask().modify(|_, w| w.adc_().set_bit());
36    }
37
38    #[inline]
39    fn calibrate(instance: &Self::Instance) {
40        instance.calib().write(|w| unsafe {
41            w.bias_cal().bits(calibration::adc_bias_cal());
42            w.linearity_cal().bits(calibration::adc_linearity_cal())
43        });
44    }
45
46    #[cfg(feature = "async")]
47    #[inline]
48    fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
49        use super::async_api;
50        &async_api::ADC_WAKERS[0]
51    }
52}
53
54impl<I: AdcInstance> Adc<I> {
55    #[inline]
56    /// Configures the ADC.
57    pub(crate) fn configure(&mut self, cfg: AdcSettings) {
58        if cfg != self.cfg {
59            // Set discard flag for next read
60            self.discard = true;
61        }
62        self.power_down();
63        self.sync();
64        I::calibrate(&self.adc);
65        self.sync();
66        self.adc
67            .ctrlb()
68            .modify(|_, w| w.prescaler().variant(cfg.clk_divider));
69        self.sync();
70        self.adc
71            .ctrlb()
72            .modify(|_, w| w.ressel().variant(cfg.accumulation.resolution()));
73        self.sync();
74
75        self.adc
76            .sampctrl()
77            .modify(|_, w| unsafe { w.samplen().bits(cfg.sample_clock_cycles.saturating_sub(1)) }); // sample length
78        self.sync();
79        self.adc.inputctrl().modify(|_, w| {
80            w.muxneg().gnd();
81            w.gain().variant(Gainselect::Div2)
82        }); // No negative input (internal gnd)
83        self.sync();
84        let (sample_cnt, adjres) = match cfg.accumulation {
85            // 1 sample to be used as is
86            Accumulation::Single(_) => (SampleCount::_1, 0),
87            // A total of `adc_sample_count` elements will be averaged by the ADC
88            // before it returns the result
89            // Table 45-3 SAMx5x datasheet
90            Accumulation::Average(cnt) => (cnt, core::cmp::min(cnt as u8, 0x04)),
91            // A total of `adc_sample_count` elements will be summed by the ADC
92            // before it returns the result
93            Accumulation::Summed(cnt) => (cnt, 0),
94        };
95        // Write so we completely overwrite the last setting
96        self.adc.avgctrl().write(|w| {
97            w.samplenum().variant(sample_cnt);
98            unsafe { w.adjres().bits(adjres) }
99        });
100        self.sync();
101        self.set_reference(cfg.vref);
102        self.sync();
103        self.adc.ctrla().modify(|_, w| w.enable().set_bit());
104        self.sync();
105        self.cfg = cfg;
106        self.power_up();
107    }
108
109    #[inline]
110    pub(super) fn sync(&self) {
111        while self.adc.status().read().syncbusy().bit_is_set() {
112            core::hint::spin_loop();
113        }
114    }
115
116    #[inline]
117    pub(super) fn power_up(&mut self) {
118        self.adc.ctrla().modify(|_, w| w.enable().set_bit());
119        self.sync();
120    }
121
122    #[inline]
123    #[allow(dead_code)]
124    pub(super) fn power_down(&mut self) {
125        self.adc.ctrla().modify(|_, w| w.enable().clear_bit());
126        self.sync();
127    }
128
129    #[inline]
130    pub(super) fn start_conversion(&mut self) {
131        self.adc.swtrig().modify(|_, w| w.start().set_bit());
132        self.sync();
133    }
134
135    #[inline]
136    pub(super) fn enable_freerunning(&mut self) {
137        self.adc.ctrlb().modify(|_, w| w.freerun().set_bit());
138        self.sync();
139    }
140
141    #[inline]
142    pub(super) fn disable_freerunning(&mut self) {
143        self.adc.ctrlb().modify(|_, w| w.freerun().clear_bit());
144        self.sync();
145    }
146
147    #[inline]
148    pub(super) fn read_flags(&self) -> Flags {
149        let bits = self.adc.intflag().read().bits();
150        Flags::from_bits_truncate(bits)
151    }
152
153    #[cfg(feature = "async")]
154    /// Clear the specified interrupt flags
155    #[inline]
156    pub(super) fn clear_flags(&mut self, flags: &Flags) {
157        unsafe {
158            self.adc.intflag().write(|w| w.bits(flags.bits()));
159        }
160    }
161
162    /// Clear all interrupt flags
163    #[inline]
164    pub(super) fn clear_all_flags(&mut self) {
165        unsafe {
166            self.adc.intflag().write(|w| w.bits(0b1111));
167        }
168    }
169
170    /// Check whether the provided flags contain an `OVERRUN` error
171    #[inline]
172    pub(super) fn check_overrun(&mut self, flags: &Flags) -> Result<(), Error> {
173        if flags.contains(Flags::OVERRUN) {
174            Err(Error::BufferOverrun)
175        } else {
176            Ok(())
177        }
178    }
179
180    /// Enables an interrupt when conversion is ready.
181    #[inline]
182    #[allow(dead_code)]
183    pub(super) fn enable_interrupts(&mut self, flags: Flags) {
184        unsafe { self.adc.intenset().write(|w| w.bits(flags.bits())) };
185    }
186
187    /// Disables the interrupt for when conversion is ready.
188    #[inline]
189    pub(super) fn disable_interrupts(&mut self, flags: Flags) {
190        unsafe { self.adc.intenclr().write(|w| w.bits(flags.bits())) };
191    }
192
193    #[inline]
194    pub(super) fn conversion_result(&self) -> u16 {
195        self.adc.result().read().result().bits()
196    }
197
198    #[inline]
199    pub(super) fn mux(&mut self, ch: u8) {
200        self.adc.inputctrl().modify(|r, w| {
201            if r.muxpos().bits() != ch {
202                self.discard = true;
203            }
204            unsafe { w.muxpos().bits(ch) }
205        });
206        self.sync()
207    }
208
209    #[inline]
210    fn cpu_raw_to_temp(&self, reading: u16) -> f32 {
211        // Source:
212        // https://github.com/ElectronicCats/ElectronicCats_InternalTemperatureZero/blob/master/src/TemperatureZero.cpp
213        let room_temp = calibration::room_temp();
214        let adc_room = calibration::room_temp_adc_val() as f32;
215
216        let hot_temp = calibration::hot_temp();
217        let adc_hot = calibration::hot_temp_adc_val() as f32;
218
219        let room_int1v = 1.0 - (calibration::room_int1v_val() as f32 / 1000.0);
220        let hot_int1v = 1.0 - (calibration::hot_int1v_val() as f32 / 1000.0);
221
222        let v_adc_room = (adc_room * room_int1v) / 4095.0;
223        let v_adc_hot = (adc_hot * hot_int1v) / 4095.0;
224
225        let v_adc = reading as f32 / 4095.0; // 1.0 is ommitted here (adc_val * 1.0) - 1.0 is the INT_V1 voltage (Always 1.0V)
226
227        let coarse_temp = room_temp
228            + (((hot_temp - room_temp) / (v_adc_hot - v_adc_room)) * (v_adc - v_adc_room));
229
230        let int1v_real = room_int1v
231            + (((hot_int1v - room_int1v) * (coarse_temp - room_temp)) / (hot_temp - room_temp));
232
233        let v_adc_real = (reading as f32 * int1v_real) / 4095.0;
234
235        room_temp
236            + (((hot_temp - room_temp) / (v_adc_hot - v_adc_room)) * (v_adc_real - v_adc_room))
237    }
238}
239
240impl<I: AdcInstance + PrimaryAdc> Adc<I> {
241    #[inline]
242    /// Returns the CPU temperature in degrees C
243    ///
244    /// NOTE: The temperature sensor is known to be out by up to ±10C, it
245    /// therefore should not be relied on for critical temperature readings
246    pub fn read_cpu_temperature(&mut self, sysctrl: &mut Sysctrl) -> f32 {
247        let old_state = sysctrl.vref().read().tsen().bit();
248        sysctrl.vref().modify(|_, w| w.tsen().set_bit());
249
250        let adc_val = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ_D21_TEMP, |adc| {
251            // IMPORTANT - We also have to modify gain, but this is not exposed in ADC
252            // Settings
253            adc.adc.inputctrl().modify(|_, w| w.gain()._1x());
254            adc.discard = true;
255            let res = adc.read_channel(0x18);
256            // Set gain back to normal
257            adc.adc.inputctrl().modify(|_, w| w.gain().div2());
258            adc.discard = true;
259            res
260        });
261
262        // Set sysctrl back
263        sysctrl.vref().modify(|_, w| w.tsen().variant(old_state));
264        self.cpu_raw_to_temp(adc_val)
265    }
266
267    #[inline]
268    pub fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 {
269        let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| {
270            let res = adc.read_channel(src as u8);
271            let mut res_f = adc.reading_to_f32(res) * 3.3;
272            if CpuVoltageSource::Bandgap != src {
273                res_f *= 4.0;
274            }
275            res_f
276        });
277        (voltage * 1000.0) as u16
278    }
279}
280
281#[cfg(feature = "async")]
282impl<I: AdcInstance + PrimaryAdc, F> FutureAdc<I, F>
283where
284    F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
285{
286    /// Reads the CPU temperature. Value returned is in Celcius
287    pub async fn read_cpu_temperature(&mut self, sysctrl: &mut Sysctrl) -> f32 {
288        let old_state = sysctrl.vref().read().tsen().bit();
289        sysctrl.vref().modify(|_, w| w.tsen().set_bit());
290
291        let old_adc_settings = self.inner.cfg;
292        self.inner.configure(ADC_SETTINGS_INTERNAL_READ);
293        self.inner.adc.inputctrl().modify(|_, w| w.gain()._1x());
294        self.inner.discard = true;
295
296        let res = self.read_channel(0x18).await;
297
298        self.inner.adc.inputctrl().modify(|_, w| w.gain().div2());
299        self.inner.configure(old_adc_settings);
300        self.inner.discard = true;
301
302        // Set sysctrl back
303        sysctrl.vref().modify(|_, w| w.tsen().variant(old_state));
304        self.inner.cpu_raw_to_temp(res)
305    }
306
307    /// Reads a CPU voltage source. Value returned is in millivolts (mV)
308    pub async fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 {
309        let old_adc_settings = self.inner.cfg;
310        self.inner.configure(ADC_SETTINGS_INTERNAL_READ);
311
312        let res = self.read_channel(src as u8).await;
313        let mut voltage = self.inner.reading_to_f32(res) * 3.3;
314        if CpuVoltageSource::Bandgap != src {
315            voltage *= 4.0;
316        }
317
318        self.inner.configure(old_adc_settings);
319        (voltage * 1000.0) as u16
320    }
321}