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, Error};
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/// Powers 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/// Enables 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// Powers both SAR units up.
80//
81// The power bits are shared, so both units come up together.
82fn power_on_sar_units() {
83    enable_refgen();
84
85    APB_SARADC::regs()
86        .ctrl0()
87        .modify(|_, w| unsafe { w.xpd_sar1_force().bits(FORCE_XPD_SAR_PU) });
88    APB_SARADC::regs()
89        .ctrl1()
90        .modify(|_, w| unsafe { w.xpd_sar2_force().bits(FORCE_XPD_SAR_PU) });
91}
92
93#[doc(hidden)]
94pub trait RegisterAccess {
95    /// Prepares the unit for a one-shot conversion.
96    fn prepare();
97
98    /// Programs the single-entry pattern table with the given channel.
99    fn program_pattern(channel: u8);
100
101    /// Triggers one conversion.
102    fn start_sample();
103
104    /// Returns whether sampling is done.
105    fn is_done() -> bool;
106
107    /// Reads sample data.
108    fn read_data() -> u16;
109
110    /// Clears the done flag and stops triggering.
111    fn reset();
112
113    /// Starts one conversion on `channel`.
114    ///
115    /// The done flag does not have to be cleared first: [`Self::reset`] does that at the end of
116    /// every conversion, and once more while the driver is built.
117    fn start(channel: u8) {
118        Self::prepare();
119        Self::program_pattern(channel);
120        Self::start_sample();
121    }
122
123    /// Runs one blocking conversion on `channel`.
124    fn sample(channel: u8) -> u16 {
125        Self::start(channel);
126
127        while !Self::is_done() {}
128
129        let converted_value = Self::read_data();
130        Self::reset();
131        converted_value
132    }
133}
134
135#[cfg(adc_adc1)]
136impl RegisterAccess for crate::peripherals::ADC1<'_> {
137    fn prepare() {
138        APB_SARADC::regs()
139            .ctrl2()
140            .modify(|_, w| w.timer_en().clear_bit());
141
142        power_on_sar_units();
143
144        // Only this unit's trigger mode is touched, so a driver for the other unit
145        // keeps its own state - `adc_oneshot_ll_enable` is per unit too.
146        APB_SARADC::regs().ctrl0().modify(|_, w| {
147            w.sar1_continue_mode_en().clear_bit();
148            w.sar1_trigger_stop().set_bit()
149        });
150    }
151
152    fn program_pattern(channel: u8) {
153        let regs = APB_SARADC::regs();
154
155        // Each pattern entry is 6 bits wide and the first entry occupies the
156        // most significant bits of the 24-bit table.
157        let entry = (((channel & 0xf) as u32) << 2) << 18;
158
159        regs.ctrl0().modify(|_, w| unsafe {
160            w.sar1_patt_type().set_bit();
161            w.sar1_patt_len().bits(0)
162        });
163        regs.sar1_patt_tab1()
164            .write(|w| unsafe { w.sar1_patt_tab1().bits(entry) });
165
166        regs.ctrl0().modify(|_, w| w.sar1_patt_p_clear().set_bit());
167        regs.ctrl0()
168            .modify(|_, w| w.sar1_patt_p_clear().clear_bit());
169
170        regs.ctrl0()
171            .modify(|_, w| unsafe { w.sar1_trigger_mode().bits(TRIGGER_MODE_SW) });
172    }
173
174    fn start_sample() {
175        APB_SARADC::regs()
176            .ctrl0()
177            .modify(|_, w| w.sar1_trigger_start().set_bit());
178    }
179
180    fn is_done() -> bool {
181        APB_SARADC::regs().int_raw().read().sar1_done().bit_is_set()
182    }
183
184    fn read_data() -> u16 {
185        (APB_SARADC::regs()
186            .sar1_data_status()
187            .read()
188            .apb_saradc1_data()
189            .bits()
190            & ADC_DATA_MASK) as u16
191    }
192
193    fn reset() {
194        APB_SARADC::regs()
195            .int_clr()
196            .write(|w| w.sar1_done().clear_bit_by_one());
197
198        APB_SARADC::regs()
199            .ctrl0()
200            .modify(|_, w| unsafe { w.sar1_trigger_mode().bits(TRIGGER_MODE_OFF) });
201    }
202}
203
204#[cfg(adc_adc2)]
205impl RegisterAccess for crate::peripherals::ADC2<'_> {
206    fn prepare() {
207        APB_SARADC::regs()
208            .ctrl2()
209            .modify(|_, w| w.timer_en().clear_bit());
210
211        power_on_sar_units();
212
213        APB_SARADC::regs().ctrl1().modify(|_, w| {
214            w.sar2_continue_mode_en().clear_bit();
215            w.sar2_trigger_stop().set_bit()
216        });
217    }
218
219    fn program_pattern(channel: u8) {
220        let regs = APB_SARADC::regs();
221
222        let entry = (((channel & 0xf) as u32) << 2) << 18;
223
224        regs.ctrl1().modify(|_, w| unsafe {
225            w.sar2_patt_type().set_bit();
226            w.sar2_patt_len().bits(0)
227        });
228        regs.sar2_patt_tab1()
229            .write(|w| unsafe { w.sar2_patt_tab1().bits(entry) });
230
231        regs.ctrl1().modify(|_, w| w.sar2_patt_p_clear().set_bit());
232        regs.ctrl1()
233            .modify(|_, w| w.sar2_patt_p_clear().clear_bit());
234
235        regs.ctrl1()
236            .modify(|_, w| unsafe { w.sar2_trigger_mode().bits(TRIGGER_MODE_SW) });
237    }
238
239    fn start_sample() {
240        APB_SARADC::regs()
241            .ctrl1()
242            .modify(|_, w| w.sar2_trigger_start().set_bit());
243    }
244
245    fn is_done() -> bool {
246        APB_SARADC::regs().int_raw().read().sar2_done().bit_is_set()
247    }
248
249    fn read_data() -> u16 {
250        (APB_SARADC::regs()
251            .sar2_data_status()
252            .read()
253            .apb_saradc2_data()
254            .bits()
255            & ADC_DATA_MASK) as u16
256    }
257
258    fn reset() {
259        APB_SARADC::regs()
260            .int_clr()
261            .write(|w| w.sar2_done().clear_bit_by_one());
262
263        APB_SARADC::regs()
264            .ctrl1()
265            .modify(|_, w| unsafe { w.sar2_trigger_mode().bits(TRIGGER_MODE_OFF) });
266    }
267}
268
269/// Analog-to-Digital Converter peripheral driver.
270pub struct Adc<'d, ADC, Dm: crate::DriverMode> {
271    _adc: ADC,
272    configured: [bool; NUM_ATTENS],
273    active_channel: Option<u8>,
274    _guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>,
275    _phantom: PhantomData<(Dm, &'d mut ())>,
276}
277
278fn check_channel_configured(configured: &[bool; NUM_ATTENS], channel: u8) -> Result<(), Error> {
279    if configured[channel as usize] {
280        Ok(())
281    } else {
282        Err(Error::ChannelNotConfigured(channel))
283    }
284}
285
286impl<'d, ADCX> Adc<'d, ADCX, Blocking>
287where
288    ADCX: RegisterAccess + 'd,
289{
290    /// Creates a new ADC instance with the given configuration.
291    ///
292    /// The ESP32-S31 SAR ADC has a single attenuation setting, so the
293    /// attenuation given per pin has no effect.
294    pub fn new(adc_instance: ADCX, config: AdcConfig<ADCX>) -> Result<Self, Error> {
295        let guard = GenericPeripheralGuard::new();
296
297        let configured = config.attenuations.map(|attenuation| attenuation.is_some());
298
299        // Select XTAL as the digital controller clock and divide it down.
300        LP_PERI::regs().adc_ctrl().modify(|_, w| unsafe {
301            w.lp_adc_clk_sel().bits(CLK_SRC_XTAL);
302            w.lp_adc_div_num().bits(CLK_DIV_NUM)
303        });
304
305        // Function clock of the digital controller.
306        APB_SARADC::regs()
307            .ctrl_date()
308            .modify(|_, w| w.clk_en().set_bit());
309
310        ADCX::prepare();
311        ADCX::reset();
312
313        Ok(Adc {
314            _adc: adc_instance,
315            configured,
316            active_channel: None,
317            _guard: guard,
318            _phantom: PhantomData,
319        })
320    }
321
322    /// Reconfigures the ADC driver to operate in asynchronous mode.
323    pub fn into_async(mut self) -> Adc<'d, ADCX, Async> {
324        acquire_async_adc();
325        self.set_interrupt_handler(adc_interrupt_handler);
326
327        // Clear a stale done flag so that the first future does not complete early.
328        ADCX::reset();
329
330        Adc {
331            _adc: self._adc,
332            configured: self.configured,
333            active_channel: self.active_channel,
334            _guard: self._guard,
335            _phantom: PhantomData,
336        }
337    }
338
339    /// Starts and waits for a conversion on the specified pin and returns the
340    /// result.
341    ///
342    /// The result is in the range 0..=[`FULL_SCALE`], and an input tied to
343    /// ground reads about [`ZERO_DIFF_CODE`].
344    ///
345    /// # Errors
346    ///
347    /// Returns [`Error::ChannelNotConfigured`] if the channel was not configured in
348    /// [`AdcConfig`].
349    pub fn read_blocking<PIN, CS>(&mut self, pin: &mut AdcPin<PIN, ADCX, CS>) -> Result<u16, Error>
350    where
351        PIN: AdcChannel,
352        CS: AdcCalScheme<ADCX>,
353    {
354        let channel = pin.pin.adc_channel();
355        check_channel_configured(&self.configured, channel)?;
356
357        Ok(ADCX::sample(channel))
358    }
359
360    /// Requests that the ADC begin a conversion on the specified pin.
361    ///
362    /// Takes an [`AdcPin`](super::AdcPin) reference, as it is
363    /// expected that the ADC will be able to sample whatever channel
364    /// underlies the pin.
365    ///
366    /// The result is in the range 0..=[`FULL_SCALE`], and an input tied to
367    /// ground reads about [`ZERO_DIFF_CODE`].
368    ///
369    /// # Errors
370    ///
371    /// Returns [`Error::ChannelNotConfigured`] if the channel was not configured in
372    /// [`AdcConfig`].
373    pub fn read_oneshot<PIN, CS>(
374        &mut self,
375        pin: &mut super::AdcPin<PIN, ADCX, CS>,
376    ) -> nb::Result<u16, Error>
377    where
378        PIN: super::AdcChannel,
379        CS: super::AdcCalScheme<ADCX>,
380    {
381        let channel = pin.pin.adc_channel();
382        check_channel_configured(&self.configured, channel).map_err(nb::Error::Other)?;
383
384        if let Some(active_channel) = self.active_channel {
385            // There is conversion in progress:
386            // - if it's for a different channel try again later
387            // - if it's for the given channel, go ahead and check progress
388            if active_channel != channel {
389                return Err(nb::Error::WouldBlock);
390            }
391        } else {
392            // If no conversions are in progress, start a new one for given channel
393            self.active_channel = Some(channel);
394
395            ADCX::start(channel);
396        }
397
398        if !ADCX::is_done() {
399            return Err(nb::Error::WouldBlock);
400        }
401
402        let converted_value = ADCX::read_data();
403        ADCX::reset();
404
405        // Mark that no conversions are currently in progress
406        self.active_channel = None;
407
408        Ok(converted_value)
409    }
410}
411
412impl<ADCX> crate::private::Sealed for Adc<'_, ADCX, Blocking> {}
413
414impl<ADCX> InterruptConfigurable for Adc<'_, ADCX, Blocking> {
415    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
416        for core in crate::system::Cpu::other() {
417            crate::interrupt::disable(core, InterruptSource);
418        }
419        crate::interrupt::bind_handler(InterruptSource, handler);
420    }
421}
422
423impl<'d, ADCX> Adc<'d, ADCX, Async>
424where
425    ADCX: RegisterAccess + 'd,
426{
427    /// Reconfigures the ADC driver to operate in [`Blocking`] mode.
428    pub fn into_blocking(self) -> Adc<'d, ADCX, Blocking> {
429        if release_async_adc() {
430            // Disable the ADC interrupt on all cores once the last async instance goes away.
431            for cpu in crate::system::Cpu::all() {
432                crate::interrupt::disable(cpu, InterruptSource);
433            }
434        }
435        Adc {
436            _adc: self._adc,
437            configured: self.configured,
438            active_channel: self.active_channel,
439            _guard: self._guard,
440            _phantom: PhantomData,
441        }
442    }
443
444    /// Starts a conversion on the specified pin and waits for the result.
445    ///
446    /// Takes an [`AdcPin`](super::AdcPin) reference, as it is
447    /// expected that the ADC will be able to sample whatever channel
448    /// underlies the pin.
449    ///
450    /// The result is in the range 0..=[`FULL_SCALE`], and an input tied to
451    /// ground reads about [`ZERO_DIFF_CODE`].
452    ///
453    /// # Errors
454    ///
455    /// Returns [`Error::ChannelNotConfigured`] if the channel was not configured in
456    /// [`AdcConfig`].
457    pub async fn read_oneshot<PIN, CS>(
458        &mut self,
459        pin: &mut super::AdcPin<PIN, ADCX, CS>,
460    ) -> Result<u16, Error>
461    where
462        ADCX: Instance,
463        PIN: super::AdcChannel,
464        CS: super::AdcCalScheme<ADCX>,
465    {
466        let channel = pin.pin.adc_channel();
467        check_channel_configured(&self.configured, channel)?;
468
469        ADCX::start(channel);
470
471        AdcFuture::<ADCX>::new(self).await;
472
473        let converted_value = ADCX::read_data();
474        ADCX::reset();
475
476        Ok(converted_value)
477    }
478}
479
480static ASYNC_ADC_COUNT: AtomicU32 = AtomicU32::new(0);
481
482fn acquire_async_adc() {
483    ASYNC_ADC_COUNT.fetch_add(1, Ordering::Relaxed);
484}
485
486fn release_async_adc() -> bool {
487    ASYNC_ADC_COUNT.fetch_sub(1, Ordering::Relaxed) == 1
488}
489
490#[handler]
491pub(crate) fn adc_interrupt_handler() {
492    let interrupt_status = APB_SARADC::regs().int_st().read();
493
494    #[cfg(adc_adc1)]
495    if interrupt_status.sar1_done().bit_is_set() {
496        unsafe { handle_async(crate::peripherals::ADC1::steal()) }
497    }
498
499    #[cfg(adc_adc2)]
500    if interrupt_status.sar2_done().bit_is_set() {
501        unsafe { handle_async(crate::peripherals::ADC2::steal()) }
502    }
503}
504
505fn handle_async<ADCX: Instance>(_instance: ADCX) {
506    ADCX::waker().wake();
507    ADCX::unlisten();
508}
509
510/// Enables asynchronous access.
511pub trait Instance: crate::private::Sealed {
512    /// Enables the ADC interrupt.
513    fn listen();
514
515    /// Disables the ADC interrupt.
516    fn unlisten();
517
518    /// Clears the ADC interrupt.
519    fn clear_interrupt();
520
521    /// Obtains the waker for the ADC interrupt.
522    fn waker() -> &'static AtomicWaker;
523}
524
525#[cfg(adc_adc1)]
526impl Instance for crate::peripherals::ADC1<'_> {
527    fn listen() {
528        APB_SARADC::regs()
529            .int_ena()
530            .modify(|_, w| w.sar1_done().set_bit());
531    }
532
533    fn unlisten() {
534        APB_SARADC::regs()
535            .int_ena()
536            .modify(|_, w| w.sar1_done().clear_bit());
537    }
538
539    fn clear_interrupt() {
540        APB_SARADC::regs()
541            .int_clr()
542            .write(|w| w.sar1_done().clear_bit_by_one());
543    }
544
545    fn waker() -> &'static AtomicWaker {
546        static WAKER: AtomicWaker = AtomicWaker::new();
547
548        &WAKER
549    }
550}
551
552#[cfg(adc_adc2)]
553impl Instance for crate::peripherals::ADC2<'_> {
554    fn listen() {
555        APB_SARADC::regs()
556            .int_ena()
557            .modify(|_, w| w.sar2_done().set_bit());
558    }
559
560    fn unlisten() {
561        APB_SARADC::regs()
562            .int_ena()
563            .modify(|_, w| w.sar2_done().clear_bit());
564    }
565
566    fn clear_interrupt() {
567        APB_SARADC::regs()
568            .int_clr()
569            .write(|w| w.sar2_done().clear_bit_by_one());
570    }
571
572    fn waker() -> &'static AtomicWaker {
573        static WAKER: AtomicWaker = AtomicWaker::new();
574
575        &WAKER
576    }
577}
578
579#[must_use = "futures do nothing unless you `.await` or poll them"]
580pub(crate) struct AdcFuture<ADCX: Instance> {
581    phantom: PhantomData<ADCX>,
582    _wake_lock: WakeLock,
583}
584
585impl<ADCX: Instance> AdcFuture<ADCX> {
586    pub fn new(_self: &super::Adc<'_, ADCX, Async>) -> Self {
587        Self {
588            phantom: PhantomData,
589            _wake_lock: WakeLock::new(),
590        }
591    }
592}
593
594impl<ADCX: Instance + super::RegisterAccess> core::future::Future for AdcFuture<ADCX> {
595    type Output = ();
596
597    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
598        if ADCX::is_done() {
599            ADCX::clear_interrupt();
600            Poll::Ready(())
601        } else {
602            ADCX::waker().register(cx.waker());
603            ADCX::listen();
604            Poll::Pending
605        }
606    }
607}
608
609impl<ADCX: Instance> Drop for AdcFuture<ADCX> {
610    fn drop(&mut self) {
611        ADCX::unlisten();
612    }
613}