1
#![cfg_attr(docsrs, procmacros::doc_replace(
2
    "analog_pin" => gpio_for_signal!(ADC1_CH0),
3
))]
4
//! # Analog to Digital Converter (ADC)
5
//!
6
//! ## Overview
7
//!
8
//! The ADC is integrated on the chip, and is capable of measuring analog
9
//! signals from specific analog I/O pins. One or more ADC units are available,
10
//! depending on the device being used.
11
//!
12
//! ## Configuration
13
//!
14
//! The ADC can be configured to measure analog signals from specific pins. The
15
//! configuration includes the resolution of the ADC, the attenuation of the
16
//! input signal, and the pins to be measured.
17
//!
18
//! Some targets also support ADC calibration via different schemes like
19
//! basic calibration, curve fitting or linear interpolation. The calibration
20
//! schemes can be used to improve the accuracy of the ADC readings.
21
//!
22
//! ## Examples
23
//!
24
//! ### Read an analog signal from a pin
25
//!
26
//! ```rust, no_run
27
//! # {before_snippet}
28
//! # use esp_hal::analog::adc::AdcConfig;
29
//! # use esp_hal::peripherals::ADC1;
30
//! # use esp_hal::analog::adc::Attenuation;
31
//! # use esp_hal::analog::adc::Adc;
32
//! # use esp_hal::delay::Delay;
33
//! let mut adc1_config = AdcConfig::new();
34
//! let mut pin = adc1_config.enable_pin(peripherals.__analog_pin__, Attenuation::_11dB);
35
//! let mut adc1 = Adc::new(peripherals.ADC1, adc1_config);
36
//!
37
//! let mut delay = Delay::new();
38
//!
39
//! loop {
40
//!     let pin_value = nb::block!(adc1.read_oneshot(&mut pin))?;
41
//!
42
//!     delay.delay_millis(1500);
43
//! }
44
//! # }
45
//! ```
46
//!
47
//! ## Implementation State
48
//!
49
//!  - [ADC calibration is not implemented for all targets].
50
//!  - The ESP32-C3 has an ADC2 in silicon, but it is not exposed due to hardware errata in its
51
//!    digital controller. GPIO5 is therefore not an ADC pin on ESP32-C3.
52
//!  - The ESP32-S31 has no calibration scheme: calibration eFuses and curve fitting coefficients
53
//!    are not available for this chip.
54
//!  - The ESP32-S31 SAR ADC has one attenuation setting, so the attenuation given to
55
//!    [`AdcConfig::enable_pin`] has no effect.
56
//!  - The ESP32-S31 SAR is differential and its result is the weighted sum of 17 redundant
57
//!    comparator bits. Readings run from 0 to `FULL_SCALE` (4393), and an input tied to ground
58
//!    reads about `ZERO_DIFF_CODE` (2198), so a single-ended measurement only uses the codes above
59
//!    that point.
60
//!
61
//! [ADC calibration is not implemented for all targets]: https://github.com/esp-rs/esp-hal/issues/326
62
use core::marker::PhantomData;
63

            
64
use crate::gpio::AnalogPin;
65

            
66
// A chip lands in a file by the ADC controller it has, not by its CPU architecture: the RISC-V
67
// ESP32-P4 drives the same RTC-style controller as the Xtensa ESP32-S2 and ESP32-S3, while the
68
// chips in `dig_ctrl.rs` have no RTC controller and convert through the digital one instead.
69
#[cfg_attr(esp32, path = "esp32.rs")]
70
#[cfg_attr(esp32p4, path = "p4.rs")]
71
#[cfg_attr(esp32s31, path = "s31.rs")]
72
#[cfg_attr(adc_controller = "rtc", path = "s2_s3.rs")]
73
#[cfg_attr(adc_controller = "digital", path = "dig_ctrl.rs")]
74
#[cfg(feature = "unstable")]
75
mod implementation;
76

            
77
#[cfg(feature = "unstable")]
78
pub use self::implementation::*;
79

            
80
/// The approximate attenuation of the ADC pin.
81
///
82
/// The effective measurement range for a given attenuation is dependent on the
83
/// device being targeted. Refer to the "ADC Characteristics" section of the
84
/// device datasheet for more information.
85
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
87
#[allow(clippy::enum_variant_names, reason = "unit of measurement")]
88
pub enum Attenuation {
89
    /// About 0dB attenuation.
90
    _0dB   = 0b00,
91
    /// About 2.5dB attenuation.
92
    _2p5dB = 0b01,
93
    /// About 6dB attenuation.
94
    _6dB   = 0b10,
95
    /// About 11dB attenuation.
96
    _11dB  = 0b11,
97
}
98

            
99
/// The number of [`Attenuation`] variants, and in turn the length of tables indexed by one.
100
#[cfg(all(feature = "unstable", adc_calibration_v1_scheme_supported))]
101
const ATTENUATION_COUNT: usize = 4;
102

            
103
/// An I/O pin which can be read using the ADC.
104
pub struct AdcPin<PIN, ADCX, CS = ()> {
105
    /// The underlying GPIO pin.
106
    pub pin: PIN,
107
    /// Calibration scheme used for the configured ADC pin
108
    pub cal_scheme: CS,
109
    _phantom: PhantomData<ADCX>,
110
}
111

            
112
impl<PIN: core::fmt::Debug, ADCX, CS: core::fmt::Debug> core::fmt::Debug for AdcPin<PIN, ADCX, CS> {
113
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
114
        f.debug_struct("AdcPin")
115
            .field("pin", &self.pin)
116
            .field("cal_scheme", &self.cal_scheme)
117
            .finish()
118
    }
119
}
120

            
121
#[cfg(feature = "defmt")]
122
impl<PIN: defmt::Format, ADCX, CS: defmt::Format> defmt::Format for AdcPin<PIN, ADCX, CS> {
123
    fn format(&self, fmt: defmt::Formatter<'_>) {
124
        defmt::write!(
125
            fmt,
126
            "AdcPin {{ pin: {}, cal_scheme: {} }}",
127
            self.pin,
128
            self.cal_scheme
129
        );
130
    }
131
}
132

            
133
/// Configuration for the ADC.
134
#[cfg(feature = "unstable")]
135
pub struct AdcConfig<ADCX> {
136
    #[cfg(esp32)]
137
    resolution: Resolution,
138
    attenuations: [Option<Attenuation>; NUM_ATTENS],
139
    _phantom: PhantomData<ADCX>,
140
}
141

            
142
#[cfg(feature = "unstable")]
143
impl<ADCX> AdcConfig<ADCX> {
144
    /// Creates a new configuration struct with its default values.
145
    pub fn new() -> Self {
146
        Self::default()
147
    }
148

            
149
    /// Enables the specified pin with the given attenuation.
150
    pub fn enable_pin<PIN>(&mut self, pin: PIN, attenuation: Attenuation) -> AdcPin<PIN, ADCX>
151
    where
152
        PIN: AdcChannel + AnalogPin,
153
    {
154
        // TODO revert this on drop
155
2
        pin.set_analog(crate::private::Internal);
156
2
        self.attenuations[pin.adc_channel() as usize] = Some(attenuation);
157

            
158
        AdcPin {
159
            pin,
160
            cal_scheme: AdcCalScheme::<()>::new_cal(attenuation),
161
            _phantom: PhantomData,
162
        }
163
    }
164

            
165
    /// Enables the specified pin with the given attenuation and calibration
166
    /// scheme.
167
    #[cfg(feature = "unstable")]
168
    pub fn enable_pin_with_cal<PIN, CS>(
169
        &mut self,
170
        pin: PIN,
171
        attenuation: Attenuation,
172
    ) -> AdcPin<PIN, ADCX, CS>
173
    where
174
        PIN: AdcChannel + AnalogPin,
175
        CS: AdcCalScheme<ADCX>,
176
    {
177
        // TODO revert this on drop
178
2
        pin.set_analog(crate::private::Internal);
179
        let channel = pin.adc_channel();
180
        self.attenuations[channel as usize] = Some(attenuation);
181

            
182
        AdcPin {
183
            pin,
184
            cal_scheme: CS::new_cal_with_channel(attenuation, channel),
185
            _phantom: PhantomData,
186
        }
187
    }
188
}
189

            
190
#[cfg(feature = "unstable")]
191
impl<ADCX> Default for AdcConfig<ADCX> {
192
    fn default() -> Self {
193
        Self {
194
            #[cfg(esp32)]
195
            resolution: Resolution::default(),
196
2
            attenuations: [None; NUM_ATTENS],
197
            _phantom: PhantomData,
198
        }
199
    }
200
}
201

            
202
#[cfg(not(esp32))]
203
#[doc(hidden)]
204
#[cfg(feature = "unstable")]
205
pub trait CalibrationAccess: RegisterAccess {
206
    const ADC_VAL_MASK: u16;
207

            
208
    /// Returns whether the unit can measure a calibration source instead of a pad.
209
    ///
210
    /// Hardware self-calibration is not supported on ESP32-C5, where calibration depends on eFuse
211
    /// data.
212
    const SUPPORTS_SELF_CALIBRATION: bool = true;
213

            
214
    fn enable_vdef(enable: bool);
215

            
216
    /// Disconnects the unit from its pad and ties the input to internal ground.
217
    fn connect_gnd(enable: bool);
218

            
219
    /// Configures the unit for calibration at the specified attenuation.
220
    ///
221
    /// See `cal_setup` in
222
    /// <https://github.com/espressif/esp-idf/blob/v6.1/components/esp_hal_ana_conv/adc_hal_common.c>.
223
    fn setup_calibration(atten: Attenuation);
224
}
225

            
226
/// Number of times the search below is repeated. The extremes are discarded and the rest averaged.
227
#[cfg(all(feature = "unstable", adc_calibration_v1_scheme_supported))]
228
const ADC_CAL_TIMES: u32 = 10;
229

            
230
/// One past the highest initial code. The field is twelve bits wide on every chip.
231
#[cfg(all(feature = "unstable", adc_calibration_v1_scheme_supported))]
232
const ADC_CAL_OFFSET_RANGE: u16 = 4096;
233

            
234
/// Finds the initial code that cancels out the calibration source the unit is currently connected
235
/// to.
236
///
237
/// The initial code shifts the SAR's transfer curve down, so the reading drops to zero once the
238
/// code reaches the source, and finding that step is an ordinary bisection. Noise moves the step
239
/// by a code or two between runs, hence the repetitions.
240
///
241
/// `measure` converts once and returns the result, leaving the initial code alone.
242
///
243
/// See `adc_hal_self_calibration` in
244
/// <https://github.com/espressif/esp-idf/blob/v6.1/components/esp_hal_ana_conv/adc_hal_common.c>.
245
#[cfg(all(feature = "unstable", adc_calibration_v1_scheme_supported))]
246
fn search_init_code<ADCX: CalibrationAccess>(mut measure: impl FnMut() -> u16) -> u16 {
247
    let mut sum = 0;
248
    let mut lowest = u16::MAX;
249
    let mut highest = 0;
250

            
251
    for _ in 0..ADC_CAL_TIMES {
252
        // `low` still reads non-zero, `high` already reads zero.
253
        let mut low = 0;
254
        let mut high = ADC_CAL_OFFSET_RANGE;
255

            
256
1170
        while high - low > 1 {
257
1080
            let code = (low + high) / 2;
258
1080
            ADCX::set_init_code(code);
259

            
260
1080
            if measure() == 0 {
261
                high = code;
262
            } else {
263
                low = code;
264
            }
265
        }
266

            
267
90
        sum += u32::from(high);
268
        lowest = lowest.min(high);
269
        highest = highest.max(high);
270
    }
271

            
272
    let count = ADC_CAL_TIMES - 2;
273
9
    let trimmed = sum - u32::from(lowest) - u32::from(highest);
274

            
275
    // Round to nearest.
276
9
    ((trimmed + count / 2) / count) as u16
277
}
278

            
279
/// A helper trait to get the ADC channel of a compatible GPIO pin.
280
pub trait AdcChannel {
281
    /// Channel number used by the ADC
282
    fn adc_channel(&self) -> u8;
283
}
284

            
285
/// A trait abstracting over calibration methods.
286
///
287
/// The methods in this trait are mostly for internal use. Call
288
/// `enable_pin_with_cal` with an implementor of this trait to get calibrated
289
/// ADC reads.
290
pub trait AdcCalScheme<ADCX>: Sized + crate::private::Sealed {
291
    /// Creates a new calibration scheme for the given attenuation.
292
    fn new_cal(atten: Attenuation) -> Self;
293

            
294
    /// Creates a new calibration scheme for the given attenuation and ADC
295
    /// channel.
296
    ///
297
    /// The default implementation ignores `channel` and calls [`Self::new_cal`].
298
    fn new_cal_with_channel(atten: Attenuation, _channel: u8) -> Self {
299
        Self::new_cal(atten)
300
    }
301

            
302
    /// Converts ADC value.
303
    fn adc_val(&self, val: u16) -> u16 {
304
        val
305
    }
306
}
307

            
308
impl crate::private::Sealed for () {}
309

            
310
impl<ADCX> AdcCalScheme<ADCX> for () {
311
    fn new_cal(_atten: Attenuation) -> Self {}
312
}
313

            
314
/// Returns the attenuation channel `channel` was configured with.
315
#[cfg(all(feature = "unstable", adc_calibration_v1_scheme_supported))]
316
288
fn channel_attenuation(attenuations: &[Option<Attenuation>], channel: u8) -> Attenuation {
317
288
    match attenuations[channel as usize] {
318
        Some(attenuation) => attenuation,
319
        None => panic!("Channel {} is not configured for reading", channel),
320
    }
321
288
}
322

            
323
// Returns the hardware calibration code (`Dout0`) for every configured attenuation.
324
//
325
// The hardware subtracts this code from the conversion result before truncating it, so it has
326
// to be programmed regardless of the calibration scheme in use - otherwise the usable output
327
// range shrinks by the ADC's zero-voltage offset.
328
//
329
// Determining a code can involve measuring the ADC, and that measurement programs channel 0's
330
// attenuation, so the caller has to apply the configured attenuations afterwards.
331
#[cfg(all(feature = "unstable", adc_calibration_v1_scheme_supported))]
332
fn hw_init_codes<ADCX>(attenuations: &[Option<Attenuation>]) -> [u16; ATTENUATION_COUNT]
333
where
334
    ADCX: AdcCalEfuse + CalibrationAccess,
335
{
336
18
    let mut init_codes = [0; ATTENUATION_COUNT];
337

            
338
    for atten in attenuations.iter().flatten() {
339
        // Boards with calibration eFuses never reach the self-calibration fallback, so HIL
340
        // tests ignore the eFuses to exercise it.
341
        #[cfg(not(__test_adc_self_cal))]
342
9
        let efuse_code = ADCX::init_code(*atten);
343
        #[cfg(__test_adc_self_cal)]
344
        let efuse_code = None;
345

            
346
18
        init_codes[*atten as usize] = efuse_code.unwrap_or_else(|| {
347
            if ADCX::SUPPORTS_SELF_CALIBRATION {
348
                AdcConfig::<ADCX>::adc_calibrate(*atten)
349
            } else {
350
                0
351
            }
352
        });
353
    }
354

            
355
    init_codes
356
}
357

            
358
/// A helper trait to get access to ADC calibration efuses.
359
#[cfg(adc_calibration_v1_scheme_supported)]
360
#[doc(hidden)]
361
pub trait AdcCalEfuse {
362
    /// Returns the ADC calibration init code.
363
    ///
364
    /// Returns digital value for zero voltage for a given attenuation.
365
    fn init_code(atten: Attenuation) -> Option<u16>;
366

            
367
    /// Returns the ADC calibration reference point voltage.
368
    ///
369
    /// Returns reference voltage (millivolts) for a given attenuation.
370
    fn cal_mv(atten: Attenuation) -> u16;
371

            
372
    /// Returns the ADC calibration reference point digital value.
373
    ///
374
    /// Returns digital value for reference voltage for a given attenuation.
375
    fn cal_code(atten: Attenuation) -> Option<u16>;
376

            
377
    /// Returns the ADC channel specific calibration.
378
    ///
379
    /// Returns digital per channel offset from reference voltage.
380
    #[cfg(adc_channel_compensation)]
381
    fn cal_chan_compens(atten: Attenuation, channel: u8) -> Option<i32>;
382
}
383

            
384
for_each_analog_function! {
385
    (($ch_name:ident, ADCn_CHm, $adc:literal, $ch:literal), $gpio:ident) => {
386
        impl $crate::analog::adc::AdcChannel for $crate::peripherals::$gpio<'_> {
387
            fn adc_channel(&self) -> u8 {
388
                $ch
389
            }
390
        }
391
    };
392
}