Skip to main content

esp_hal/analog/adc/
esp32.rs

1use core::{
2    marker::PhantomData,
3    sync::atomic::{AtomicBool, Ordering},
4};
5
6use super::{AdcCalScheme, AdcConfig, Attenuation};
7use crate::{
8    peripherals::{ADC1, ADC2, SENS},
9    private,
10};
11
12pub(super) const NUM_ATTENS: usize = 10;
13
14mod calibration;
15pub use self::calibration::*;
16
17// ADC2 cannot be used with `radio` functionality on `esp32`, this global helps us to track it's
18// state to prevent unexpected behaviour
19static ADC2_IN_USE: AtomicBool = AtomicBool::new(false);
20
21/// ADC Error
22#[derive(Debug)]
23pub enum Error {
24    /// `ADC2` is used together with `radio`.
25    Adc2InUse,
26}
27
28#[doc(hidden)]
29/// Tries to "claim" `ADC2` peripheral and set its status
30pub fn try_claim_adc2(_: private::Internal) -> Result<(), Error> {
31    if ADC2_IN_USE.fetch_or(true, Ordering::Relaxed) {
32        Err(Error::Adc2InUse)
33    } else {
34        Ok(())
35    }
36}
37
38#[doc(hidden)]
39/// Resets `ADC2` usage status to `Unused`
40pub fn release_adc2(_: private::Internal) {
41    ADC2_IN_USE.store(false, Ordering::Relaxed);
42}
43
44/// The sampling/readout resolution of the ADC.
45#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47#[allow(clippy::enum_variant_names, reason = "unit of measurement")]
48pub enum Resolution {
49    /// 9-bit resolution
50    _9Bit,
51    /// 10-bit resolution
52    _10Bit,
53    /// 11-bit resolution
54    _11Bit,
55    /// 12-bit resolution
56    #[default]
57    _12Bit,
58}
59
60#[doc(hidden)]
61pub trait RegisterAccess {
62    fn set_resolution(resolution: u8);
63
64    fn set_attenuation(channel: usize, attenuation: u8);
65
66    fn clear_dig_force();
67
68    fn set_start_force();
69
70    fn set_en_pad_force();
71
72    fn set_en_pad(channel: u8);
73
74    fn clear_start_sar();
75
76    fn set_start_sar();
77
78    fn read_done_sar() -> bool;
79
80    fn read_data_sar() -> u16;
81
82    fn instance_number() -> u8;
83}
84
85impl RegisterAccess for ADC1<'_> {
86    fn set_resolution(resolution: u8) {
87        SENS::regs()
88            .sar_start_force()
89            .modify(|_, w| unsafe { w.sar1_bit_width().bits(resolution) });
90        SENS::regs()
91            .sar_read_ctrl()
92            .modify(|_, w| unsafe { w.sar1_sample_bit().bits(resolution) });
93    }
94
95    fn set_attenuation(channel: usize, attenuation: u8) {
96        SENS::regs().sar_atten1().modify(|r, w| {
97            let new_value = (r.bits() & !(0b11 << (channel * 2)))
98                | (((attenuation & 0b11) as u32) << (channel * 2));
99
100            unsafe { w.sar1_atten().bits(new_value) }
101        });
102    }
103
104    fn clear_dig_force() {
105        SENS::regs()
106            .sar_read_ctrl()
107            .modify(|_, w| w.sar1_dig_force().clear_bit());
108    }
109
110    fn set_start_force() {
111        SENS::regs()
112            .sar_meas_start1()
113            .modify(|_, w| w.meas1_start_force().set_bit());
114    }
115
116    fn set_en_pad_force() {
117        SENS::regs()
118            .sar_meas_start1()
119            .modify(|_, w| w.sar1_en_pad_force().set_bit());
120    }
121
122    fn set_en_pad(channel: u8) {
123        SENS::regs()
124            .sar_meas_start1()
125            .modify(|_, w| unsafe { w.sar1_en_pad().bits(1 << channel) });
126    }
127
128    fn clear_start_sar() {
129        SENS::regs()
130            .sar_meas_start1()
131            .modify(|_, w| w.meas1_start_sar().clear_bit());
132    }
133
134    fn set_start_sar() {
135        SENS::regs()
136            .sar_meas_start1()
137            .modify(|_, w| w.meas1_start_sar().set_bit());
138    }
139
140    fn read_done_sar() -> bool {
141        SENS::regs()
142            .sar_meas_start1()
143            .read()
144            .meas1_done_sar()
145            .bit_is_set()
146    }
147
148    fn read_data_sar() -> u16 {
149        SENS::regs()
150            .sar_meas_start1()
151            .read()
152            .meas1_data_sar()
153            .bits()
154    }
155
156    fn instance_number() -> u8 {
157        1
158    }
159}
160
161impl RegisterAccess for ADC2<'_> {
162    fn set_resolution(resolution: u8) {
163        SENS::regs()
164            .sar_start_force()
165            .modify(|_, w| unsafe { w.sar2_bit_width().bits(resolution) });
166        SENS::regs()
167            .sar_read_ctrl2()
168            .modify(|_, w| unsafe { w.sar2_sample_bit().bits(resolution) });
169    }
170
171    fn set_attenuation(channel: usize, attenuation: u8) {
172        SENS::regs().sar_atten2().modify(|r, w| {
173            let new_value = (r.bits() & !(0b11 << (channel * 2)))
174                | (((attenuation & 0b11) as u32) << (channel * 2));
175
176            unsafe { w.sar2_atten().bits(new_value) }
177        });
178    }
179
180    fn clear_dig_force() {
181        SENS::regs()
182            .sar_read_ctrl2()
183            .modify(|_, w| w.sar2_dig_force().clear_bit());
184    }
185
186    fn set_start_force() {
187        SENS::regs()
188            .sar_meas_start2()
189            .modify(|_, w| w.meas2_start_force().set_bit());
190    }
191
192    fn set_en_pad_force() {
193        SENS::regs()
194            .sar_meas_start2()
195            .modify(|_, w| w.sar2_en_pad_force().set_bit());
196    }
197
198    fn set_en_pad(channel: u8) {
199        SENS::regs()
200            .sar_meas_start2()
201            .modify(|_, w| unsafe { w.sar2_en_pad().bits(1 << channel) });
202    }
203
204    fn clear_start_sar() {
205        SENS::regs()
206            .sar_meas_start2()
207            .modify(|_, w| w.meas2_start_sar().clear_bit());
208    }
209
210    fn set_start_sar() {
211        SENS::regs()
212            .sar_meas_start2()
213            .modify(|_, w| w.meas2_start_sar().set_bit());
214    }
215
216    fn read_done_sar() -> bool {
217        SENS::regs()
218            .sar_meas_start2()
219            .read()
220            .meas2_done_sar()
221            .bit_is_set()
222    }
223
224    fn read_data_sar() -> u16 {
225        SENS::regs()
226            .sar_meas_start2()
227            .read()
228            .meas2_data_sar()
229            .bits()
230    }
231
232    fn instance_number() -> u8 {
233        2
234    }
235}
236
237/// Analog-to-Digital Converter peripheral driver.
238pub struct Adc<'d, ADC, Dm: crate::DriverMode> {
239    _adc: ADC,
240    attenuations: [Option<Attenuation>; NUM_ATTENS],
241    active_channel: Option<u8>,
242    resolution_bits: u8,
243    _phantom: PhantomData<(Dm, &'d mut ())>,
244}
245
246impl<'d, ADCX> Adc<'d, ADCX, crate::Blocking>
247where
248    ADCX: RegisterAccess + 'd,
249{
250    /// Configure a given ADC instance using the provided configuration, and
251    /// initialize the ADC for use
252    ///
253    /// # Panics
254    ///
255    /// `ADC2` cannot be used simultaneously with `radio` functionalities, otherwise this function
256    /// will panic.
257    pub fn new(adc_instance: ADCX, config: AdcConfig<ADCX>) -> Self {
258        if ADCX::instance_number() == 2 && try_claim_adc2(private::Internal).is_err() {
259            panic!(
260                "ADC2 is already in use by Radio. On ESP32, ADC2 cannot be used simultaneously with Wi-Fi or Bluetooth."
261            );
262        }
263
264        let sensors = SENS::regs();
265
266        // Set reading and sampling resolution
267        ADCX::set_resolution(config.resolution as u8);
268
269        // Set attenuation for pins
270        let attenuations = config.attenuations;
271
272        for (channel, attenuation) in attenuations.iter().enumerate() {
273            if let Some(attenuation) = attenuation {
274                ADCX::set_attenuation(channel, *attenuation as u8);
275            }
276        }
277
278        // Set controller to RTC
279        ADCX::clear_dig_force();
280        ADCX::set_start_force();
281        ADCX::set_en_pad_force();
282        sensors.sar_touch_ctrl1().modify(|_, w| {
283            w.xpd_hall_force().set_bit();
284            w.hall_phase_force().set_bit()
285        });
286
287        sensors.sar_meas_wait2().modify(|_, w| unsafe {
288            // Set power to SW power on
289            w.force_xpd_sar().bits(0b11);
290            // disable AMP
291            w.force_xpd_amp().bits(0b10)
292        });
293        sensors.sar_meas_ctrl().modify(|_, w| unsafe {
294            w.amp_rst_fb_fsm().bits(0);
295            w.amp_short_ref_fsm().bits(0);
296            w.amp_short_ref_gnd_fsm().bits(0)
297        });
298        sensors.sar_meas_wait1().modify(|_, w| unsafe {
299            w.sar_amp_wait1().bits(1);
300            w.sar_amp_wait2().bits(1)
301        });
302        sensors
303            .sar_meas_wait2()
304            .modify(|_, w| unsafe { w.sar_amp_wait3().bits(1) });
305
306        // Do *not* invert the output
307        // NOTE: This seems backwards, but was verified experimentally.
308        sensors
309            .sar_read_ctrl()
310            .modify(|_, w| w.sar1_data_inv().set_bit());
311        sensors
312            .sar_read_ctrl2()
313            .modify(|_, w| w.sar2_data_inv().set_bit());
314
315        Adc {
316            _adc: adc_instance,
317            attenuations: config.attenuations,
318            active_channel: None,
319            resolution_bits: match config.resolution {
320                Resolution::_9Bit => 9,
321                Resolution::_10Bit => 10,
322                Resolution::_11Bit => 11,
323                Resolution::_12Bit => 12,
324            },
325            _phantom: PhantomData,
326        }
327    }
328
329    /// Request that the ADC begin a conversion on the specified pin
330    ///
331    /// This method takes an [AdcPin](super::AdcPin) reference, as it is
332    /// expected that the ADC will be able to sample whatever channel
333    /// underlies the pin.
334    pub fn read_oneshot<PIN, CS>(
335        &mut self,
336        pin: &mut super::AdcPin<PIN, ADCX, CS>,
337    ) -> nb::Result<u16, ()>
338    where
339        PIN: super::AdcChannel,
340        CS: AdcCalScheme<ADCX>,
341    {
342        if self.attenuations[pin.pin.adc_channel() as usize].is_none() {
343            panic!(
344                "Channel {} is not configured reading!",
345                pin.pin.adc_channel()
346            );
347        }
348
349        if let Some(active_channel) = self.active_channel {
350            // There is conversion in progress:
351            // - if it's for a different channel try again later
352            // - if it's for the given channel, go ahead and check progress
353            if active_channel != pin.pin.adc_channel() {
354                return Err(nb::Error::WouldBlock);
355            }
356        } else {
357            // If no conversions are in progress, start a new one for given channel
358            self.active_channel = Some(pin.pin.adc_channel());
359
360            ADCX::set_en_pad(pin.pin.adc_channel());
361
362            ADCX::clear_start_sar();
363            ADCX::set_start_sar();
364        }
365
366        // Wait for ADC to finish conversion
367        let conversion_finished = ADCX::read_done_sar();
368        if !conversion_finished {
369            return Err(nb::Error::WouldBlock);
370        }
371
372        // Get converted value and scale to 12 bits
373        let mut converted_value = ADCX::read_data_sar() as u32;
374        converted_value <<= 12 - self.resolution_bits;
375        if converted_value > 4095 {
376            converted_value = 4095;
377        }
378
379        // Mark that no conversions are currently in progress
380        self.active_channel = None;
381
382        Ok(pin.cal_scheme.adc_val(converted_value as u16))
383    }
384}
385
386impl Drop for ADC2<'_> {
387    fn drop(&mut self) {
388        release_adc2(private::Internal);
389    }
390}