Skip to main content

esp_hal/analog/adc/
s31.rs

1use core::{
2    marker::PhantomData,
3    pin::Pin,
4    task::{Context, Poll},
5};
6
7// Both SAR units report completion through the single LP_ADC interrupt line.
8use Interrupt::LP_ADC as InterruptSource;
9// Both units share one interrupt, so the enabled instances have to be counted.
10use portable_atomic::{AtomicU32, Ordering};
11use procmacros::handler;
12
13use super::{AdcCalScheme, AdcChannel, AdcConfig, AdcPin};
14use crate::{
15    Async,
16    Blocking,
17    asynch::AtomicWaker,
18    interrupt::{InterruptConfigurable, InterruptHandler},
19    peripherals::{APB_SARADC, Interrupt, LP_PERI},
20    rtc_cntl::WakeLock,
21    system::{GenericPeripheralGuard, Peripheral},
22};
23
24/// Both units have 8 channels, and the attenuation table is indexed by channel.
25pub(super) const NUM_ATTENS: usize = 8;
26
27/// Mask of the conversion data in `sarN_data_status`. The rest of the register
28/// holds the channel and the unit that produced the sample.
29const ADC_DATA_MASK: u32 = 0x1_ffff;
30
31/// The largest value a conversion can return.
32///
33/// The SAR has 17 redundant comparator bits with non-uniform weights, and the
34/// hardware reports their weighted sum. The result is therefore not a 17-bit
35/// number: it spans a little more than 12 bits.
36///
37/// See `adc_digi_output_data_t` for the ESP32-S31 in
38/// `components/esp_hal_ana_conv/include/hal/adc_types.h`.
39#[instability::unstable]
40pub const FULL_SCALE: u16 = 4393;
41
42/// The value a conversion returns for a zero input difference.
43///
44/// The SAR is differential, so an input tied to ground reads about this value
45/// instead of zero, and only the codes above it are available to a single-ended
46/// measurement.
47///
48/// See `ADC_LL_ZERO_DIFF_CODE` in
49/// `components/esp_hal_ana_conv/esp32s31/include/hal/adc_ll.h`.
50#[instability::unstable]
51pub const ZERO_DIFF_CODE: u16 = 2198;
52
53/// Software trigger mode, used for one-shot conversions.
54const TRIGGER_MODE_SW: u8 = 2;
55/// Trigger disabled.
56const TRIGGER_MODE_OFF: u8 = 0;
57
58/// Power the SAR up by software, instead of leaving it to the FSM.
59const FORCE_XPD_SAR_PU: u8 = 3;
60
61/// Clock divider for the digital controller. The divider is `CLK_DIV_NUM + 1`.
62///
63/// See `ADC_LL_CLKM_DIV_NUM_DEFAULT` in
64/// `components/esp_hal_ana_conv/esp32s31/include/hal/adc_ll.h`.
65const CLK_DIV_NUM: u8 = 4;
66
67/// Digital controller clock source select value for XTAL.
68const CLK_SRC_XTAL: u8 = 1;
69
70/// Enable the reference generator shared by both units.
71fn enable_refgen() {
72    APB_SARADC::regs().ref_control().modify(|_, w| {
73        w.rtc_xpd_refgen().set_bit();
74        w.rtc_pre_charge().set_bit();
75        w.rtc_ref_delay().set_bit()
76    });
77}
78
79#[doc(hidden)]
80pub trait RegisterAccess {
81    /// Power the SAR up and put the unit into single-conversion mode.
82    fn enable();
83
84    /// Program the single-entry pattern table with the given channel.
85    fn program_pattern(channel: u8);
86
87    /// Trigger one conversion.
88    fn start_sample();
89
90    /// Check if sampling is done
91    fn is_done() -> bool;
92
93    /// Read sample data
94    fn read_data() -> u16;
95
96    /// Clear the done flag and stop triggering.
97    fn reset();
98}
99
100#[cfg(adc_adc1)]
101impl RegisterAccess for crate::peripherals::ADC1<'_> {
102    fn enable() {
103        APB_SARADC::regs()
104            .ctrl2()
105            .modify(|_, w| w.timer_en().clear_bit());
106
107        enable_refgen();
108
109        APB_SARADC::regs().ctrl0().modify(|_, w| unsafe {
110            w.xpd_sar1_force().bits(FORCE_XPD_SAR_PU);
111            w.sar1_continue_mode_en().clear_bit();
112            w.sar1_trigger_stop().set_bit()
113        });
114    }
115
116    fn program_pattern(channel: u8) {
117        let regs = APB_SARADC::regs();
118
119        // Each pattern entry is 6 bits wide and the first entry occupies the
120        // most significant bits of the 24-bit table.
121        let entry = (((channel & 0xf) as u32) << 2) << 18;
122
123        regs.ctrl0().modify(|_, w| unsafe {
124            w.sar1_patt_type().set_bit();
125            w.sar1_patt_len().bits(0)
126        });
127        regs.sar1_patt_tab1()
128            .write(|w| unsafe { w.sar1_patt_tab1().bits(entry) });
129
130        regs.ctrl0().modify(|_, w| w.sar1_patt_p_clear().set_bit());
131        regs.ctrl0()
132            .modify(|_, w| w.sar1_patt_p_clear().clear_bit());
133
134        regs.ctrl0()
135            .modify(|_, w| unsafe { w.sar1_trigger_mode().bits(TRIGGER_MODE_SW) });
136    }
137
138    fn start_sample() {
139        APB_SARADC::regs()
140            .ctrl0()
141            .modify(|_, w| w.sar1_trigger_start().set_bit());
142    }
143
144    fn is_done() -> bool {
145        APB_SARADC::regs().int_raw().read().sar1_done().bit_is_set()
146    }
147
148    fn read_data() -> u16 {
149        (APB_SARADC::regs()
150            .sar1_data_status()
151            .read()
152            .apb_saradc1_data()
153            .bits()
154            & ADC_DATA_MASK) as u16
155    }
156
157    fn reset() {
158        APB_SARADC::regs()
159            .int_clr()
160            .write(|w| w.sar1_done().clear_bit_by_one());
161
162        APB_SARADC::regs()
163            .ctrl0()
164            .modify(|_, w| unsafe { w.sar1_trigger_mode().bits(TRIGGER_MODE_OFF) });
165    }
166}
167
168#[cfg(adc_adc2)]
169impl RegisterAccess for crate::peripherals::ADC2<'_> {
170    fn enable() {
171        APB_SARADC::regs()
172            .ctrl2()
173            .modify(|_, w| w.timer_en().clear_bit());
174
175        enable_refgen();
176
177        APB_SARADC::regs().ctrl1().modify(|_, w| unsafe {
178            w.xpd_sar2_force().bits(FORCE_XPD_SAR_PU);
179            w.sar2_continue_mode_en().clear_bit();
180            w.sar2_trigger_stop().set_bit()
181        });
182    }
183
184    fn program_pattern(channel: u8) {
185        let regs = APB_SARADC::regs();
186
187        let entry = (((channel & 0xf) as u32) << 2) << 18;
188
189        regs.ctrl1().modify(|_, w| unsafe {
190            w.sar2_patt_type().set_bit();
191            w.sar2_patt_len().bits(0)
192        });
193        regs.sar2_patt_tab1()
194            .write(|w| unsafe { w.sar2_patt_tab1().bits(entry) });
195
196        regs.ctrl1().modify(|_, w| w.sar2_patt_p_clear().set_bit());
197        regs.ctrl1()
198            .modify(|_, w| w.sar2_patt_p_clear().clear_bit());
199
200        regs.ctrl1()
201            .modify(|_, w| unsafe { w.sar2_trigger_mode().bits(TRIGGER_MODE_SW) });
202    }
203
204    fn start_sample() {
205        APB_SARADC::regs()
206            .ctrl1()
207            .modify(|_, w| w.sar2_trigger_start().set_bit());
208    }
209
210    fn is_done() -> bool {
211        APB_SARADC::regs().int_raw().read().sar2_done().bit_is_set()
212    }
213
214    fn read_data() -> u16 {
215        (APB_SARADC::regs()
216            .sar2_data_status()
217            .read()
218            .apb_saradc2_data()
219            .bits()
220            & ADC_DATA_MASK) as u16
221    }
222
223    fn reset() {
224        APB_SARADC::regs()
225            .int_clr()
226            .write(|w| w.sar2_done().clear_bit_by_one());
227
228        APB_SARADC::regs()
229            .ctrl1()
230            .modify(|_, w| unsafe { w.sar2_trigger_mode().bits(TRIGGER_MODE_OFF) });
231    }
232}
233
234/// Analog-to-Digital Converter peripheral driver.
235pub struct Adc<'d, ADC, Dm: crate::DriverMode> {
236    _adc: ADC,
237    active_channel: Option<u8>,
238    _guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>,
239    _phantom: PhantomData<(Dm, &'d mut ())>,
240}
241
242impl<'d, ADCX> Adc<'d, ADCX, Blocking>
243where
244    ADCX: RegisterAccess + 'd,
245{
246    /// Configure a given ADC instance using the provided configuration, and
247    /// initialize the ADC for use
248    ///
249    /// The ESP32-S31 SAR ADC has a single attenuation setting, so the
250    /// attenuation given per pin has no effect.
251    pub fn new(adc_instance: ADCX, _config: AdcConfig<ADCX>) -> Self {
252        let guard = GenericPeripheralGuard::new();
253
254        // Select XTAL as the digital controller clock and divide it down.
255        LP_PERI::regs().adc_ctrl().modify(|_, w| unsafe {
256            w.lp_adc_clk_sel().bits(CLK_SRC_XTAL);
257            w.lp_adc_div_num().bits(CLK_DIV_NUM)
258        });
259
260        // Function clock of the digital controller.
261        APB_SARADC::regs()
262            .ctrl_date()
263            .modify(|_, w| w.clk_en().set_bit());
264
265        ADCX::enable();
266
267        Adc {
268            _adc: adc_instance,
269            active_channel: None,
270            _guard: guard,
271            _phantom: PhantomData,
272        }
273    }
274
275    /// Reconfigures the ADC driver to operate in asynchronous mode.
276    pub fn into_async(mut self) -> Adc<'d, ADCX, Async> {
277        acquire_async_adc();
278        self.set_interrupt_handler(adc_interrupt_handler);
279
280        // Clear a stale done flag so that the first future does not complete early.
281        ADCX::reset();
282
283        Adc {
284            _adc: self._adc,
285            active_channel: self.active_channel,
286            _guard: self._guard,
287            _phantom: PhantomData,
288        }
289    }
290
291    /// Start and wait for a conversion on the specified pin and return the
292    /// result
293    ///
294    /// The result is in the range 0..=[`FULL_SCALE`], and an input tied to
295    /// ground reads about [`ZERO_DIFF_CODE`].
296    pub fn read_blocking<PIN, CS>(&mut self, pin: &mut AdcPin<PIN, ADCX, CS>) -> u16
297    where
298        PIN: AdcChannel,
299        CS: AdcCalScheme<ADCX>,
300    {
301        ADCX::program_pattern(pin.pin.adc_channel());
302        ADCX::start_sample();
303
304        while !ADCX::is_done() {}
305
306        let converted_value = ADCX::read_data();
307        ADCX::reset();
308
309        converted_value
310    }
311
312    /// Request that the ADC begin a conversion on the specified pin
313    ///
314    /// This method takes an [AdcPin](super::AdcPin) reference, as it is
315    /// expected that the ADC will be able to sample whatever channel
316    /// underlies the pin.
317    ///
318    /// The result is in the range 0..=[`FULL_SCALE`], and an input tied to
319    /// ground reads about [`ZERO_DIFF_CODE`].
320    pub fn read_oneshot<PIN, CS>(
321        &mut self,
322        pin: &mut super::AdcPin<PIN, ADCX, CS>,
323    ) -> nb::Result<u16, ()>
324    where
325        PIN: super::AdcChannel,
326        CS: super::AdcCalScheme<ADCX>,
327    {
328        if let Some(active_channel) = self.active_channel {
329            // There is conversion in progress:
330            // - if it's for a different channel try again later
331            // - if it's for the given channel, go ahead and check progress
332            if active_channel != pin.pin.adc_channel() {
333                return Err(nb::Error::WouldBlock);
334            }
335        } else {
336            // If no conversions are in progress, start a new one for given channel
337            self.active_channel = Some(pin.pin.adc_channel());
338
339            ADCX::program_pattern(pin.pin.adc_channel());
340            ADCX::start_sample();
341        }
342
343        if !ADCX::is_done() {
344            return Err(nb::Error::WouldBlock);
345        }
346
347        let converted_value = ADCX::read_data();
348        ADCX::reset();
349
350        // Mark that no conversions are currently in progress
351        self.active_channel = None;
352
353        Ok(converted_value)
354    }
355}
356
357impl<ADCX> crate::private::Sealed for Adc<'_, ADCX, Blocking> {}
358
359impl<ADCX> InterruptConfigurable for Adc<'_, ADCX, Blocking> {
360    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
361        for core in crate::system::Cpu::other() {
362            crate::interrupt::disable(core, InterruptSource);
363        }
364        crate::interrupt::bind_handler(InterruptSource, handler);
365    }
366}
367
368impl<'d, ADCX> Adc<'d, ADCX, Async>
369where
370    ADCX: RegisterAccess + 'd,
371{
372    /// Create a new instance in [crate::Blocking] mode.
373    pub fn into_blocking(self) -> Adc<'d, ADCX, Blocking> {
374        if release_async_adc() {
375            // Disable the ADC interrupt on all cores once the last async instance goes away.
376            for cpu in crate::system::Cpu::all() {
377                crate::interrupt::disable(cpu, InterruptSource);
378            }
379        }
380        Adc {
381            _adc: self._adc,
382            active_channel: self.active_channel,
383            _guard: self._guard,
384            _phantom: PhantomData,
385        }
386    }
387
388    /// Start a conversion on the specified pin and wait for the result.
389    ///
390    /// This method takes an [AdcPin](super::AdcPin) reference, as it is
391    /// expected that the ADC will be able to sample whatever channel
392    /// underlies the pin.
393    ///
394    /// The result is in the range 0..=[`FULL_SCALE`], and an input tied to
395    /// ground reads about [`ZERO_DIFF_CODE`].
396    pub async fn read_oneshot<PIN, CS>(&mut self, pin: &mut super::AdcPin<PIN, ADCX, CS>) -> u16
397    where
398        ADCX: Instance,
399        PIN: super::AdcChannel,
400        CS: super::AdcCalScheme<ADCX>,
401    {
402        ADCX::program_pattern(pin.pin.adc_channel());
403        ADCX::start_sample();
404
405        AdcFuture::<ADCX>::new(self).await;
406
407        let converted_value = ADCX::read_data();
408        ADCX::reset();
409
410        converted_value
411    }
412}
413
414static ASYNC_ADC_COUNT: AtomicU32 = AtomicU32::new(0);
415
416fn acquire_async_adc() {
417    ASYNC_ADC_COUNT.fetch_add(1, Ordering::Relaxed);
418}
419
420fn release_async_adc() -> bool {
421    ASYNC_ADC_COUNT.fetch_sub(1, Ordering::Relaxed) == 1
422}
423
424#[handler]
425pub(crate) fn adc_interrupt_handler() {
426    let interrupt_status = APB_SARADC::regs().int_st().read();
427
428    #[cfg(adc_adc1)]
429    if interrupt_status.sar1_done().bit_is_set() {
430        unsafe { handle_async(crate::peripherals::ADC1::steal()) }
431    }
432
433    #[cfg(adc_adc2)]
434    if interrupt_status.sar2_done().bit_is_set() {
435        unsafe { handle_async(crate::peripherals::ADC2::steal()) }
436    }
437}
438
439fn handle_async<ADCX: Instance>(_instance: ADCX) {
440    ADCX::waker().wake();
441    ADCX::unlisten();
442}
443
444/// Enable asynchronous access.
445pub trait Instance: crate::private::Sealed {
446    /// Enable the ADC interrupt
447    fn listen();
448
449    /// Disable the ADC interrupt
450    fn unlisten();
451
452    /// Clear the ADC interrupt
453    fn clear_interrupt();
454
455    /// Obtain the waker for the ADC interrupt
456    fn waker() -> &'static AtomicWaker;
457}
458
459#[cfg(adc_adc1)]
460impl Instance for crate::peripherals::ADC1<'_> {
461    fn listen() {
462        APB_SARADC::regs()
463            .int_ena()
464            .modify(|_, w| w.sar1_done().set_bit());
465    }
466
467    fn unlisten() {
468        APB_SARADC::regs()
469            .int_ena()
470            .modify(|_, w| w.sar1_done().clear_bit());
471    }
472
473    fn clear_interrupt() {
474        APB_SARADC::regs()
475            .int_clr()
476            .write(|w| w.sar1_done().clear_bit_by_one());
477    }
478
479    fn waker() -> &'static AtomicWaker {
480        static WAKER: AtomicWaker = AtomicWaker::new();
481
482        &WAKER
483    }
484}
485
486#[cfg(adc_adc2)]
487impl Instance for crate::peripherals::ADC2<'_> {
488    fn listen() {
489        APB_SARADC::regs()
490            .int_ena()
491            .modify(|_, w| w.sar2_done().set_bit());
492    }
493
494    fn unlisten() {
495        APB_SARADC::regs()
496            .int_ena()
497            .modify(|_, w| w.sar2_done().clear_bit());
498    }
499
500    fn clear_interrupt() {
501        APB_SARADC::regs()
502            .int_clr()
503            .write(|w| w.sar2_done().clear_bit_by_one());
504    }
505
506    fn waker() -> &'static AtomicWaker {
507        static WAKER: AtomicWaker = AtomicWaker::new();
508
509        &WAKER
510    }
511}
512
513#[must_use = "futures do nothing unless you `.await` or poll them"]
514pub(crate) struct AdcFuture<ADCX: Instance> {
515    phantom: PhantomData<ADCX>,
516    _wake_lock: WakeLock,
517}
518
519impl<ADCX: Instance> AdcFuture<ADCX> {
520    pub fn new(_self: &super::Adc<'_, ADCX, Async>) -> Self {
521        Self {
522            phantom: PhantomData,
523            _wake_lock: WakeLock::new(),
524        }
525    }
526}
527
528impl<ADCX: Instance + super::RegisterAccess> core::future::Future for AdcFuture<ADCX> {
529    type Output = ();
530
531    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
532        if ADCX::is_done() {
533            ADCX::clear_interrupt();
534            Poll::Ready(())
535        } else {
536            ADCX::waker().register(cx.waker());
537            ADCX::listen();
538            Poll::Pending
539        }
540    }
541}
542
543impl<ADCX: Instance> Drop for AdcFuture<ADCX> {
544    fn drop(&mut self) {
545        ADCX::unlisten();
546    }
547}