Skip to main content

esp_hal/analog/adc/
dig_ctrl.rs

1use core::marker::PhantomData;
2
3cfg_select! {
4    any(esp32c6, esp32c61) => {
5        use Interrupt::APB_SARADC as InterruptSource;
6    }
7    _ => {
8        use Interrupt::APB_ADC as InterruptSource;
9    }
10}
11
12use core::{
13    pin::Pin,
14    task::{Context, Poll},
15};
16
17use procmacros::handler;
18
19pub use self::calibration::*;
20use super::{AdcConfig, Attenuation, Error};
21use crate::{
22    Async,
23    Blocking,
24    asynch::AtomicWaker,
25    efuse::AdcCalibUnit,
26    interrupt::{InterruptConfigurable, InterruptHandler},
27    peripherals::{APB_SARADC, Interrupt},
28    rtc_cntl::WakeLock,
29    soc::regi2c,
30    system::{GenericPeripheralGuard, Peripheral},
31};
32
33mod calibration;
34
35// Constants taken from:
36// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32c2/include/soc/regi2c_saradc.h
37// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32c3/include/soc/regi2c_saradc.h
38// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32c6/include/soc/regi2c_saradc.h
39// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32h2/include/soc/regi2c_saradc.h
40const ADC_VAL_MASK: u16 = 0xfff;
41
42pub(super) const NUM_ATTENS: usize = property!("adc.channel_count");
43
44impl<ADCX> AdcConfig<ADCX>
45where
46    ADCX: RegisterAccess,
47{
48    /// Measures the initial code that cancels the unit's zero-voltage offset at `atten`.
49    pub fn adc_calibrate(atten: Attenuation) -> u16
50    where
51        ADCX: super::CalibrationAccess,
52    {
53        // This can be called without an `Adc`, so it cannot rely on `Adc::new` having clocked
54        // and powered the unit.
55        let _guard = GenericPeripheralGuard::<{ Peripheral::ApbSarAdc as u8 }>::new();
56        let _clock = FunctionClockGuard::new();
57        let sar_power = SarPowerGuard::new();
58        init_hardware();
59        sar_power.force_on();
60
61        ADCX::enable_vdef(true);
62
63        // Start sampling
64        ADCX::setup_calibration(atten);
65
66        // Connect calibration source
67        ADCX::connect_gnd(true);
68
69        ADCX::calibration_init();
70        let cal_val = super::search_init_code::<ADCX>(read_cal_channel::<ADCX>);
71        ADCX::reset();
72
73        // Disconnect calibration source
74        ADCX::connect_gnd(false);
75
76        cal_val
77    }
78}
79
80// Converts the connected calibration source once.
81//
82// See `read_cal_channel` in
83// <https://github.com/espressif/esp-idf/blob/v6.1/components/esp_hal_ana_conv/adc_hal_common.c>
84fn read_cal_channel<ADCX: RegisterAccess>() -> u16 {
85    // Only the done flag - `reset` would disarm the unit `setup_calibration` armed.
86    ADCX::clear_done();
87
88    ADCX::set_onetime_start(false);
89    crate::rom::ets_delay_us(5);
90    ADCX::set_onetime_start(true);
91
92    while !ADCX::is_done() {}
93    settle_after_done();
94
95    ADCX::read_data() & ADC_VAL_MASK
96}
97
98// Configures clocks and powers up the SAR ADC.
99//
100// See `adc_oneshot_hal_setup` in
101// <https://github.com/espressif/esp-idf/blob/v6.1/components/esp_hal_ana_conv/adc_oneshot_hal.c>
102fn init_hardware() {
103    // controller_clk = source / (DIV_NUM + DIV_A / DIV_B + 1)
104    // The ESP32-H2 divides its faster source further, landing at the same 5 MHz as the other chips.
105    #[cfg(esp32h2)]
106    const DIV: (u8, u8, u8) = (18, 5, 1);
107    #[cfg(not(esp32h2))]
108    const DIV: (u8, u8, u8) = (15, 1, 0);
109
110    let (div_num, div_b, div_a) = DIV;
111
112    // The ESP32-C2 and ESP32-C3 keep the divider in the ADC itself; the other chips configure
113    // it in PCR. See `adc_ll_digi_controller_clk_div`.
114    #[cfg(not(adc_pcr_clock))]
115    APB_SARADC::regs().clkm_conf().modify(|_, w| unsafe {
116        w.clkm_div_num().bits(div_num);
117        w.clkm_div_b().bits(div_b);
118        w.clkm_div_a().bits(div_a)
119    });
120
121    #[cfg(adc_pcr_clock)]
122    crate::peripherals::PCR::regs()
123        .saradc_clkm_conf()
124        .modify(|_, w| unsafe {
125            w.saradc_clkm_div_num().bits(div_num);
126            w.saradc_clkm_div_b().bits(div_b);
127            w.saradc_clkm_div_a().bits(div_a)
128        });
129
130    APB_SARADC::regs().ctrl().modify(|_, w| unsafe {
131        // Conversions are triggered through `onetime_start`, so the software start of the
132        // digital controller has to stay off - a concurrently running controller disturbs the
133        // sampling and biases the results low.
134        w.start_force().clear_bit();
135        w.start().clear_bit();
136        w.sar_clk_gated().set_bit();
137        #[cfg(not(adc_pcr_sar_clk_div))]
138        w.sar_clk_div().bits(1);
139        w.xpd_sar_force().bits(0b11)
140    });
141
142    // The ESP32-C5, ESP32-C61, and ESP32-H2 configure the divider in PCR.
143    // See `adc_ll_digi_set_clk_div`.
144    #[cfg(adc_pcr_sar_clk_div)]
145    crate::peripherals::PCR::regs()
146        .sar_clk_div()
147        .modify(|_, w| unsafe { w.sar1_clk_div_num().bits(1) });
148}
149
150struct FunctionClockGuard;
151
152impl FunctionClockGuard {
153    fn new() -> Self {
154        crate::clock::ll::ClockTree::with(|clocks| {
155            #[cfg(soc_clock_node_adc_function_clock_is_configurable)]
156            crate::clock::ll::AdcInstance::ApbSaradc
157                .configure_function_clock(clocks, Default::default());
158            crate::clock::ll::AdcInstance::ApbSaradc.request_function_clock(clocks);
159        });
160        Self
161    }
162}
163
164impl Drop for FunctionClockGuard {
165    fn drop(&mut self) {
166        crate::clock::ll::ClockTree::with(|clocks| {
167            crate::clock::ll::AdcInstance::ApbSaradc.release_function_clock(clocks);
168        });
169    }
170}
171
172/// Keeps the SAR powered while a radio is running.
173///
174/// Radio initialization hands SAR power to the PHY's power detector (PWDET), which powers the SAR
175/// down between its own measurements - conversions then saturate. Like ESP-IDF, the driver
176/// overrides that through `PWDET_CONF` while the ADC is in use, and hands control back to the
177/// power detector when the last guard drops. On the ESP32-C2 and ESP32-C3 the power detector does
178/// not control the SAR, so this does nothing there.
179///
180/// See `s_sar_power_acquire` in
181/// <https://github.com/espressif/esp-idf/blob/v6.1/components/esp_hw_support/port/esp32c6/sar_periph_ctrl.c>
182struct SarPowerGuard;
183
184cfg_select! {
185    adc_sar_power_override => {
186        static SAR_POWER_REFS: esp_sync::NonReentrantMutex<u8> =
187            esp_sync::NonReentrantMutex::new(0);
188
189        impl SarPowerGuard {
190            fn new() -> Self {
191                SAR_POWER_REFS.with(|refs| *refs += 1);
192                Self
193            }
194
195            /// Forces the SAR on.
196            ///
197            /// Radio initialization rewrites `PWDET_CONF`, so this has to run before every
198            /// conversion rather than once when the guard is created.
199            fn force_on(&self) {
200                SAR_POWER_REFS.with(|_| force_sar_power(true));
201            }
202        }
203
204        impl Drop for SarPowerGuard {
205            fn drop(&mut self) {
206                SAR_POWER_REFS.with(|refs| {
207                    *refs -= 1;
208                    if *refs == 0 {
209                        force_sar_power(false);
210                    }
211                });
212            }
213        }
214
215        /// Sets or releases the power detector's override of SAR power.
216        ///
217        /// `PWDET_CONF` is clocked by the modem front-end APB clock, which the radio gates while
218        /// its PHY is off, and writes are dropped while it is gated. The clock is enabled only
219        /// around the write, so the radio finds it the way it left it. Callers hold
220        /// `SAR_POWER_REFS`, a critical section, which keeps the radio from reconfiguring the
221        /// clock in between.
222        ///
223        /// See `sar_ctrl_ll_set_power_mode_from_pwdet` in
224        /// <https://github.com/espressif/esp-idf/blob/v6.1/components/hal/esp32c6/include/hal/sar_ctrl_ll.h>
225        fn force_sar_power(force: bool) {
226            // The PACs do not describe the power detector.
227            const PWDET_CONF: *mut u32 = cfg_select! {
228                esp32c61 => 0x600A_0808 as *mut u32,
229                _ => 0x600A_0810 as *mut u32,
230            };
231            const SAR_POWER_FORCE: u32 = 1 << 24;
232            const SAR_POWER_CNTL: u32 = 1 << 23;
233
234            let clk_conf1 = crate::peripherals::MODEM_SYSCON::regs().clk_conf1();
235            let fe_apb_clock_enabled = clk_conf1.read().clk_fe_apb_en().bit();
236            clk_conf1.modify(|_, w| w.clk_fe_apb_en().set_bit());
237
238            unsafe {
239                let conf = PWDET_CONF.read_volatile();
240                let conf = if force {
241                    conf | SAR_POWER_FORCE | SAR_POWER_CNTL
242                } else {
243                    conf & !SAR_POWER_FORCE
244                };
245                PWDET_CONF.write_volatile(conf);
246            }
247
248            clk_conf1.modify(|_, w| w.clk_fe_apb_en().bit(fe_apb_clock_enabled));
249        }
250    }
251    _ => {
252        impl SarPowerGuard {
253            fn new() -> Self {
254                Self
255            }
256
257            fn force_on(&self) {}
258        }
259    }
260}
261
262// Triggers a single conversion.
263//
264// See `adc_hal_onetime_start` in
265// <https://github.com/espressif/esp-idf/blob/v6.1/components/esp_hal_ana_conv/adc_oneshot_hal.c>
266fn start_onetime_sample<ADCX: RegisterAccess>(sar_power: &SarPowerGuard) {
267    sar_power.force_on();
268    ADCX::set_onetime_start(true);
269}
270
271/// Waits for the conversion result to become readable after the done flag has come up.
272///
273/// The ESP32-H2 needs five SAR clock cycles here or it reports zero. `init_hardware` leaves the SAR
274/// at the 5 MHz digital controller clock, which makes that one microsecond per cycle.
275///
276/// See `ADC_LL_DELAY_CYCLE_AFTER_DONE_SIGNAL`, which is zero on every other chip here.
277#[inline]
278fn settle_after_done() {
279    #[cfg(esp32h2)]
280    crate::rom::ets_delay_us(5);
281}
282
283#[doc(hidden)]
284pub trait RegisterAccess {
285    /// Configures one-time sampling parameters.
286    fn config_onetime_sample(channel: u8, attenuation: u8);
287
288    /// Drives the `onetime_start` trigger level.
289    ///
290    /// Callers go through [`start_onetime_sample`], which raises it once per conversion; `reset`
291    /// lowers it again after the read.
292    fn set_onetime_start(enable: bool);
293
294    /// Returns whether sampling is done.
295    fn is_done() -> bool;
296
297    /// Reads sample data.
298    fn read_data() -> u16;
299
300    /// Clears the conversion-done flag.
301    fn clear_done();
302
303    /// Clears the done flag and ends the one-shot session.
304    fn reset();
305
306    /// Sets up ADC hardware for calibration.
307    fn calibration_init();
308
309    /// Sets calibration parameter to ADC hardware.
310    fn set_init_code(data: u16);
311}
312
313impl RegisterAccess for crate::peripherals::ADC1<'_> {
314    fn config_onetime_sample(channel: u8, attenuation: u8) {
315        #[cfg(esp32c6)]
316        let was_armed = APB_SARADC::regs()
317            .onetime_sample()
318            .read()
319            .saradc1_onetime_sample()
320            .bit();
321
322        APB_SARADC::regs().onetime_sample().modify(|_, w| unsafe {
323            // Disarm ADC2 before configuring ADC1.
324            // See `adc_oneshot_ll_disable_all_unit`.
325            w.saradc2_onetime_sample().clear_bit();
326            w.saradc1_onetime_sample().set_bit();
327            w.onetime_channel().bits(channel);
328            w.onetime_atten().bits(attenuation)
329        });
330
331        // The ESP32-C6 channel selection needs 50 µs to settle after arming ADC1, which is why
332        // `reset` leaves it armed there. See `adc_oneshot_ll_enable`.
333        #[cfg(esp32c6)]
334        if !was_armed {
335            crate::rom::ets_delay_us(50);
336        }
337    }
338
339    fn set_onetime_start(enable: bool) {
340        APB_SARADC::regs()
341            .onetime_sample()
342            .modify(|_, w| w.onetime_start().bit(enable));
343    }
344
345    fn is_done() -> bool {
346        APB_SARADC::regs().int_raw().read().adc1_done().bit()
347    }
348
349    fn read_data() -> u16 {
350        APB_SARADC::regs()
351            .sar1data_status()
352            .read()
353            .saradc1_data()
354            .bits() as u16
355            & 0xfff
356    }
357
358    fn clear_done() {
359        APB_SARADC::regs()
360            .int_clr()
361            .write(|w| w.adc1_done().clear_bit_by_one());
362    }
363
364    fn reset() {
365        Self::clear_done();
366
367        // Disarm both units along with the trigger - except on the ESP32-C6, which keeps
368        // ADC1 armed for the whole one-shot session.
369        // See `adc_oneshot_ll_disable_all_unit`.
370        APB_SARADC::regs().onetime_sample().modify(|_, w| {
371            #[cfg(not(esp32c6))]
372            {
373                w.saradc2_onetime_sample().clear_bit();
374                w.saradc1_onetime_sample().clear_bit();
375            }
376            w.onetime_start().clear_bit()
377        });
378    }
379
380    fn calibration_init() {
381        // e.g.
382        // https://github.com/espressif/esp-idf/blob/800f141f94c0f880c162de476512e183df671307/components/hal/esp32c3/include/hal/adc_ll.h#L702
383        regi2c::ADC_SAR1_DREF.write_field(1);
384    }
385
386    fn set_init_code(data: u16) {
387        let [msb, lsb] = data.to_be_bytes();
388
389        regi2c::ADC_SAR1_INITIAL_CODE_HIGH.write_field(msb);
390        regi2c::ADC_SAR1_INITIAL_CODE_LOW.write_field(lsb);
391    }
392}
393
394impl super::CalibrationAccess for crate::peripherals::ADC1<'_> {
395    const ADC_VAL_MASK: u16 = ADC_VAL_MASK;
396    const SUPPORTS_SELF_CALIBRATION: bool = !cfg!(esp32c5);
397
398    fn enable_vdef(enable: bool) {
399        regi2c::ADC_SAR1_DREF.write_field(enable as _);
400    }
401
402    fn connect_gnd(enable: bool) {
403        regi2c::ADC_SAR1_ENCAL_GND.write_field(enable as _);
404    }
405
406    fn setup_calibration(atten: Attenuation) {
407        // `adc_oneshot_ll_disable_channel` deselects every pad by writing
408        // `(unit << 3) | 0xf` to the channel field. The attenuation is shared by all
409        // channels on these chips, so programming it is enough.
410        Self::config_onetime_sample(0xf, atten as u8);
411    }
412}
413
414/// Analog-to-Digital Converter peripheral driver.
415pub struct Adc<'d, ADCX, Dm: crate::DriverMode> {
416    _adc: ADCX,
417    attenuations: [Option<Attenuation>; NUM_ATTENS],
418    /// Hardware calibration code per attenuation, indexed by [`Attenuation`].
419    init_codes: [u16; super::ATTENUATION_COUNT],
420    active_channel: Option<u8>,
421    _clock: FunctionClockGuard,
422    _guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>,
423    sar_power: SarPowerGuard,
424    _phantom: PhantomData<(Dm, &'d mut ())>,
425}
426
427impl<'d, ADCX, Dm> Adc<'d, ADCX, Dm>
428where
429    ADCX: RegisterAccess + 'd,
430    Dm: crate::DriverMode,
431{
432    // Validate the channel before modifying ADC registers.
433    // The calibration init code depends on the channel's attenuation.
434    fn start_sample<PIN, CS>(&mut self, pin: &mut super::AdcPin<PIN, ADCX, CS>) -> Result<(), Error>
435    where
436        PIN: super::AdcChannel,
437        CS: super::AdcCalScheme<ADCX>,
438    {
439        let channel = pin.pin.adc_channel();
440        let attenuation = super::channel_attenuation(&self.attenuations, channel)?;
441
442        ADCX::calibration_init();
443        ADCX::set_init_code(self.init_codes[attenuation as usize]);
444
445        ADCX::config_onetime_sample(channel, attenuation as u8);
446        start_onetime_sample::<ADCX>(&self.sar_power);
447
448        Ok(())
449    }
450}
451
452impl<'d, ADCX> Adc<'d, ADCX, Blocking>
453where
454    ADCX: RegisterAccess + 'd,
455{
456    /// Creates a new ADC instance with the given configuration.
457    pub fn new(adc_instance: ADCX, config: AdcConfig<ADCX>) -> Result<Self, Error>
458    where
459        ADCX: super::AdcCalEfuse + super::CalibrationAccess,
460    {
461        let guard = GenericPeripheralGuard::new();
462        let clock = FunctionClockGuard::new();
463        let sar_power = SarPowerGuard::new();
464
465        init_hardware();
466
467        let init_codes = super::hw_init_codes::<ADCX>(&config.attenuations);
468
469        // The bootloader runs conversions of its own to seed the RNG, so the done flag can
470        // already be set here and would make the first read return a stale result.
471        ADCX::reset();
472
473        Ok(Adc {
474            _adc: adc_instance,
475            attenuations: config.attenuations,
476            init_codes,
477            active_channel: None,
478            _guard: guard,
479            _clock: clock,
480            sar_power,
481            _phantom: PhantomData,
482        })
483    }
484
485    /// Reconfigures the ADC driver to operate in asynchronous mode.
486    pub fn into_async(mut self) -> Adc<'d, ADCX, Async> {
487        self.set_interrupt_handler(adc_interrupt_handler);
488
489        // Reset interrupt flags and disable oneshot reading to normalize state before
490        // entering async mode, otherwise there can be '0' readings, happening initially
491        // using ADC2
492        ADCX::reset();
493
494        Adc {
495            _adc: self._adc,
496            attenuations: self.attenuations,
497            init_codes: self.init_codes,
498            active_channel: self.active_channel,
499            _guard: self._guard,
500            _clock: self._clock,
501            sar_power: self.sar_power,
502            _phantom: PhantomData,
503        }
504    }
505
506    /// Requests that the ADC begin a conversion on the specified pin.
507    ///
508    /// Takes an [`AdcPin`](super::AdcPin) reference, as it is
509    /// expected that the ADC will be able to sample whatever channel
510    /// underlies the pin.
511    ///
512    /// # Errors
513    ///
514    /// Returns [`Error::ChannelNotConfigured`] if the channel was not configured in
515    /// [`AdcConfig`].
516    pub fn read_oneshot<PIN, CS>(
517        &mut self,
518        pin: &mut super::AdcPin<PIN, ADCX, CS>,
519    ) -> nb::Result<u16, Error>
520    where
521        PIN: super::AdcChannel,
522        CS: super::AdcCalScheme<ADCX>,
523    {
524        let channel = pin.pin.adc_channel();
525
526        if let Some(active_channel) = self.active_channel {
527            // There is conversion in progress:
528            // - if it's for a different channel try again later
529            // - if it's for the given channel, go ahead and check progress
530            if active_channel != channel {
531                return Err(nb::Error::WouldBlock);
532            }
533        } else {
534            // If no conversions are in progress, start a new one for given channel
535            self.start_sample(pin).map_err(nb::Error::Other)?;
536            self.active_channel = Some(channel);
537        }
538
539        // Wait for ADC to finish conversion
540        let conversion_finished = ADCX::is_done();
541        if !conversion_finished {
542            return Err(nb::Error::WouldBlock);
543        }
544        settle_after_done();
545
546        // Get converted value
547        let converted_value = ADCX::read_data();
548        ADCX::reset();
549
550        // Postprocess converted value according to calibration scheme used for pin
551        let converted_value = pin.cal_scheme.adc_val(converted_value);
552
553        // Mark that no conversions are currently in progress
554        self.active_channel = None;
555
556        Ok(converted_value)
557    }
558}
559
560impl<ADCX> crate::private::Sealed for Adc<'_, ADCX, Blocking> {}
561
562impl<ADCX> InterruptConfigurable for Adc<'_, ADCX, Blocking> {
563    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
564        for core in crate::system::Cpu::other() {
565            crate::interrupt::disable(core, InterruptSource);
566        }
567        crate::interrupt::bind_handler(InterruptSource, handler);
568    }
569}
570
571impl super::AdcCalEfuse for crate::peripherals::ADC1<'_> {
572    fn init_code(atten: Attenuation) -> Option<u16> {
573        crate::efuse::rtc_calib_init_code(AdcCalibUnit::ADC1, atten)
574    }
575
576    fn cal_mv(atten: Attenuation) -> Option<u16> {
577        crate::efuse::rtc_calib_cal_mv(AdcCalibUnit::ADC1, atten)
578    }
579
580    fn cal_code(atten: Attenuation) -> Option<u16> {
581        crate::efuse::rtc_calib_cal_code(AdcCalibUnit::ADC1, atten)
582    }
583
584    #[cfg(adc_channel_compensation)]
585    fn cal_chan_compens(atten: Attenuation, channel: u8) -> Option<i32> {
586        crate::efuse::rtc_calib_get_chan_compens(AdcCalibUnit::ADC1, channel, atten)
587    }
588}
589
590impl<'d, ADCX> Adc<'d, ADCX, Async>
591where
592    ADCX: RegisterAccess + 'd,
593{
594    /// Reconfigures the ADC driver to operate in [`Blocking`] mode.
595    pub fn into_blocking(self) -> Adc<'d, ADCX, Blocking> {
596        // Every chip handled by this driver has a single ADC unit, so no other driver can be
597        // sharing the interrupt and it is always safe to disable it here.
598        for cpu in crate::system::Cpu::all() {
599            crate::interrupt::disable(cpu, InterruptSource);
600        }
601        Adc {
602            _adc: self._adc,
603            attenuations: self.attenuations,
604            init_codes: self.init_codes,
605            active_channel: self.active_channel,
606            _guard: self._guard,
607            _clock: self._clock,
608            sar_power: self.sar_power,
609            _phantom: PhantomData,
610        }
611    }
612
613    /// Requests that the ADC begin a conversion on the specified pin.
614    ///
615    /// Takes an [`AdcPin`](super::AdcPin) reference, as it is
616    /// expected that the ADC will be able to sample whatever channel
617    /// underlies the pin.
618    ///
619    /// # Errors
620    ///
621    /// Returns [`Error::ChannelNotConfigured`] if the channel was not configured in
622    /// [`AdcConfig`].
623    pub async fn read_oneshot<PIN, CS>(
624        &mut self,
625        pin: &mut super::AdcPin<PIN, ADCX, CS>,
626    ) -> Result<u16, Error>
627    where
628        ADCX: Instance,
629        PIN: super::AdcChannel,
630        CS: super::AdcCalScheme<ADCX>,
631    {
632        self.start_sample(pin)?;
633
634        // Wait for ADC to finish conversion and get value
635        let adc_ready_future = AdcFuture::new(self);
636        adc_ready_future.await;
637        settle_after_done();
638
639        let converted_value = ADCX::read_data();
640
641        ADCX::reset();
642
643        // Postprocess converted value according to calibration scheme used for pin
644        Ok(pin.cal_scheme.adc_val(converted_value))
645    }
646}
647
648#[handler]
649pub(crate) fn adc_interrupt_handler() {
650    let saradc = APB_SARADC::regs();
651    let interrupt_status = saradc.int_st().read();
652
653    if interrupt_status.adc1_done().bit_is_set() {
654        unsafe { handle_async(crate::peripherals::ADC1::steal()) }
655    }
656}
657
658fn handle_async<ADCX: Instance>(_instance: ADCX) {
659    ADCX::waker().wake();
660    ADCX::unlisten();
661}
662
663/// Enables asynchronous access.
664pub trait Instance: crate::private::Sealed {
665    /// Enables the ADC interrupt.
666    fn listen();
667
668    /// Disables the ADC interrupt.
669    fn unlisten();
670
671    /// Clears the ADC interrupt.
672    fn clear_interrupt();
673
674    /// Obtains the waker for the ADC interrupt.
675    fn waker() -> &'static AtomicWaker;
676}
677
678impl Instance for crate::peripherals::ADC1<'_> {
679    fn listen() {
680        APB_SARADC::regs()
681            .int_ena()
682            .modify(|_, w| w.adc1_done().set_bit());
683    }
684
685    fn unlisten() {
686        APB_SARADC::regs()
687            .int_ena()
688            .modify(|_, w| w.adc1_done().clear_bit());
689    }
690
691    fn clear_interrupt() {
692        APB_SARADC::regs()
693            .int_clr()
694            .write(|w| w.adc1_done().clear_bit_by_one());
695    }
696
697    fn waker() -> &'static AtomicWaker {
698        static WAKER: AtomicWaker = AtomicWaker::new();
699
700        &WAKER
701    }
702}
703
704#[must_use = "futures do nothing unless you `.await` or poll them"]
705pub(crate) struct AdcFuture<ADCX: Instance> {
706    phantom: PhantomData<ADCX>,
707    _wake_lock: WakeLock,
708}
709
710impl<ADCX: Instance> AdcFuture<ADCX> {
711    pub fn new(_self: &super::Adc<'_, ADCX, Async>) -> Self {
712        Self {
713            phantom: PhantomData,
714            _wake_lock: WakeLock::new(),
715        }
716    }
717}
718
719impl<ADCX: Instance + super::RegisterAccess> core::future::Future for AdcFuture<ADCX> {
720    type Output = ();
721
722    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
723        if ADCX::is_done() {
724            ADCX::clear_interrupt();
725            Poll::Ready(())
726        } else {
727            ADCX::waker().register(cx.waker());
728            ADCX::listen();
729            Poll::Pending
730        }
731    }
732}
733
734impl<ADCX: Instance> Drop for AdcFuture<ADCX> {
735    fn drop(&mut self) {
736        ADCX::unlisten();
737    }
738}