esp_hal/analog/adc/
riscv.rs

1use core::marker::PhantomData;
2
3cfg_if::cfg_if! {
4    if #[cfg(esp32c6)] {
5        use Interrupt::APB_SARADC as InterruptSource;
6    } else {
7        use Interrupt::APB_ADC as InterruptSource;
8    }
9}
10
11use core::{
12    pin::Pin,
13    task::{Context, Poll},
14};
15
16// We only have to count on devices that have multiple ADCs sharing the same interrupt
17#[cfg(all(adc_adc1, adc_adc2))]
18use portable_atomic::{AtomicU32, Ordering};
19use procmacros::handler;
20
21pub use self::calibration::*;
22use super::{AdcCalSource, AdcConfig, Attenuation};
23#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
24use crate::efuse::{AdcCalibUnit, Efuse};
25use crate::{
26    Async,
27    Blocking,
28    asynch::AtomicWaker,
29    interrupt::{InterruptConfigurable, InterruptHandler},
30    peripherals::{APB_SARADC, Interrupt},
31    soc::regi2c,
32    system::{GenericPeripheralGuard, Peripheral},
33};
34
35mod calibration;
36
37// Constants taken from:
38// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32c2/include/soc/regi2c_saradc.h
39// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32c3/include/soc/regi2c_saradc.h
40// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32c6/include/soc/regi2c_saradc.h
41// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32h2/include/soc/regi2c_saradc.h
42cfg_if::cfg_if! {
43    if #[cfg(adc_adc1)] {
44        const ADC_VAL_MASK: u16 = 0xfff;
45        const ADC_CAL_CNT_MAX: u16 = 32;
46        const ADC_CAL_CHANNEL: u16 = 15;
47    }
48}
49
50// The number of analog IO pins, and in turn the number of attentuations,
51// depends on which chip is being used
52cfg_if::cfg_if! {
53    if #[cfg(esp32c6)] {
54        pub(super) const NUM_ATTENS: usize = 7;
55    } else {
56        pub(super) const NUM_ATTENS: usize = 5;
57    }
58}
59
60impl<ADCI> AdcConfig<ADCI>
61where
62    ADCI: RegisterAccess,
63{
64    /// Calibrate ADC with specified attenuation and voltage source
65    pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16
66    where
67        ADCI: super::CalibrationAccess,
68    {
69        let mut adc_max: u16 = 0;
70        let mut adc_min: u16 = u16::MAX;
71        let mut adc_sum: u32 = 0;
72
73        ADCI::enable_vdef(true);
74
75        // Start sampling
76        ADCI::config_onetime_sample(ADC_CAL_CHANNEL as u8, atten as u8);
77
78        // Connect calibration source
79        ADCI::connect_cal(source, true);
80
81        ADCI::calibration_init();
82        for _ in 0..ADC_CAL_CNT_MAX {
83            ADCI::set_init_code(0);
84
85            // Trigger ADC sampling
86            ADCI::start_onetime_sample();
87
88            // Wait until ADC1 sampling is done
89            while !ADCI::is_done() {}
90
91            let adc = ADCI::read_data() & ADC_VAL_MASK;
92
93            ADCI::reset();
94
95            adc_sum += adc as u32;
96            adc_max = adc.max(adc_max);
97            adc_min = adc.min(adc_min);
98        }
99
100        let cal_val = (adc_sum - adc_max as u32 - adc_min as u32) as u16 / (ADC_CAL_CNT_MAX - 2);
101
102        // Disconnect calibration source
103        ADCI::connect_cal(source, false);
104
105        cal_val
106    }
107}
108
109#[doc(hidden)]
110pub trait RegisterAccess {
111    /// Configure onetime sampling parameters
112    fn config_onetime_sample(channel: u8, attenuation: u8);
113
114    /// Start onetime sampling
115    fn start_onetime_sample();
116
117    /// Check if sampling is done
118    fn is_done() -> bool;
119
120    /// Read sample data
121    fn read_data() -> u16;
122
123    /// Reset flags
124    fn reset();
125
126    /// Set up ADC hardware for calibration
127    fn calibration_init();
128
129    /// Set calibration parameter to ADC hardware
130    fn set_init_code(data: u16);
131}
132
133#[cfg(adc_adc1)]
134impl RegisterAccess for crate::peripherals::ADC1<'_> {
135    fn config_onetime_sample(channel: u8, attenuation: u8) {
136        APB_SARADC::regs().onetime_sample().modify(|_, w| unsafe {
137            w.saradc1_onetime_sample().set_bit();
138            w.onetime_channel().bits(channel);
139            w.onetime_atten().bits(attenuation)
140        });
141    }
142
143    fn start_onetime_sample() {
144        APB_SARADC::regs()
145            .onetime_sample()
146            .modify(|_, w| w.onetime_start().set_bit());
147    }
148
149    fn is_done() -> bool {
150        APB_SARADC::regs().int_raw().read().adc1_done().bit()
151    }
152
153    fn read_data() -> u16 {
154        APB_SARADC::regs()
155            .sar1data_status()
156            .read()
157            .saradc1_data()
158            .bits() as u16
159            & 0xfff
160    }
161
162    fn reset() {
163        // Clear ADC1 sampling done interrupt bit
164        APB_SARADC::regs()
165            .int_clr()
166            .write(|w| w.adc1_done().clear_bit_by_one());
167
168        // Disable ADC sampling
169        APB_SARADC::regs()
170            .onetime_sample()
171            .modify(|_, w| w.onetime_start().clear_bit());
172    }
173
174    // Currently #[cfg] covers all supported RISC-V devices,
175    // but, for example, esp32p4 uses the value 4 instead of 1,
176    // so it is not standard across all RISC-V devices.
177    #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
178    fn calibration_init() {
179        // e.g.
180        // https://github.com/espressif/esp-idf/blob/800f141f94c0f880c162de476512e183df671307/components/hal/esp32c3/include/hal/adc_ll.h#L702
181        regi2c::ADC_SAR1_DREF.write_field(1);
182    }
183
184    fn set_init_code(data: u16) {
185        let [msb, lsb] = data.to_be_bytes();
186
187        regi2c::ADC_SAR1_INITIAL_CODE_HIGH.write_field(msb);
188        regi2c::ADC_SAR1_INITIAL_CODE_LOW.write_field(lsb);
189    }
190}
191
192#[cfg(adc_adc1)]
193impl super::CalibrationAccess for crate::peripherals::ADC1<'_> {
194    const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX;
195    const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL;
196    const ADC_VAL_MASK: u16 = ADC_VAL_MASK;
197
198    fn enable_vdef(enable: bool) {
199        regi2c::ADC_SAR1_DREF.write_field(enable as _);
200    }
201
202    fn connect_cal(source: AdcCalSource, enable: bool) {
203        match source {
204            AdcCalSource::Gnd => regi2c::ADC_SAR1_ENCAL_GND.write_field(enable as _),
205            #[cfg(not(esp32h2))]
206            AdcCalSource::Ref => regi2c::ADC_SAR1_ENCAL_REF.write_field(enable as _),
207            // For the ESP32-H2 ground and internal reference voltage are mutually exclusive and
208            // you can toggle between them.
209            //
210            // See: <https://github.com/espressif/esp-idf/blob/5c51472e82a58098dda8d40a1c4f250c374fc900/components/hal/esp32h2/include/hal/adc_ll.h#L645>
211            #[cfg(esp32h2)]
212            AdcCalSource::Ref => regi2c::ADC_SAR1_ENCAL_GND.write_field(!enable as _),
213        }
214    }
215}
216
217#[cfg(adc_adc2)]
218impl RegisterAccess for crate::peripherals::ADC2<'_> {
219    fn config_onetime_sample(channel: u8, attenuation: u8) {
220        APB_SARADC::regs().onetime_sample().modify(|_, w| unsafe {
221            w.saradc2_onetime_sample().set_bit();
222            w.onetime_channel().bits(channel);
223            w.onetime_atten().bits(attenuation)
224        });
225    }
226
227    fn start_onetime_sample() {
228        APB_SARADC::regs()
229            .onetime_sample()
230            .modify(|_, w| w.onetime_start().set_bit());
231    }
232
233    fn is_done() -> bool {
234        APB_SARADC::regs().int_raw().read().adc2_done().bit()
235    }
236
237    fn read_data() -> u16 {
238        APB_SARADC::regs()
239            .sar2data_status()
240            .read()
241            .saradc2_data()
242            .bits() as u16
243            & 0xfff
244    }
245
246    fn reset() {
247        APB_SARADC::regs()
248            .int_clr()
249            .write(|w| w.adc2_done().clear_bit_by_one());
250
251        APB_SARADC::regs()
252            .onetime_sample()
253            .modify(|_, w| w.onetime_start().clear_bit());
254    }
255
256    #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
257    fn calibration_init() {
258        regi2c::ADC_SAR2_DREF.write_field(1);
259    }
260
261    fn set_init_code(data: u16) {
262        let [msb, lsb] = data.to_be_bytes();
263
264        regi2c::ADC_SAR2_INITIAL_CODE_HIGH.write_field(msb as _);
265        regi2c::ADC_SAR2_INITIAL_CODE_LOW.write_field(lsb as _);
266    }
267}
268
269#[cfg(adc_adc2)]
270impl super::CalibrationAccess for crate::peripherals::ADC2<'_> {
271    const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX;
272    const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL;
273    const ADC_VAL_MASK: u16 = ADC_VAL_MASK;
274
275    fn enable_vdef(enable: bool) {
276        regi2c::ADC_SAR2_DREF.write_field(enable as _);
277    }
278
279    fn connect_cal(source: AdcCalSource, enable: bool) {
280        match source {
281            AdcCalSource::Gnd => regi2c::ADC_SAR2_ENCAL_GND.write_field(enable as _),
282            AdcCalSource::Ref => regi2c::ADC_SAR2_ENCAL_REF.write_field(enable as _),
283        }
284    }
285}
286
287/// Analog-to-Digital Converter peripheral driver.
288pub struct Adc<'d, ADCI, Dm: crate::DriverMode> {
289    _adc: ADCI,
290    attenuations: [Option<Attenuation>; NUM_ATTENS],
291    active_channel: Option<u8>,
292    _guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>,
293    _phantom: PhantomData<(Dm, &'d mut ())>,
294}
295
296impl<'d, ADCI> Adc<'d, ADCI, Blocking>
297where
298    ADCI: RegisterAccess + 'd,
299{
300    /// Configure a given ADC instance using the provided configuration, and
301    /// initialize the ADC for use
302    pub fn new(adc_instance: ADCI, config: AdcConfig<ADCI>) -> Self {
303        let guard = GenericPeripheralGuard::new();
304
305        APB_SARADC::regs().ctrl().modify(|_, w| unsafe {
306            w.start_force().set_bit();
307            w.start().set_bit();
308            w.sar_clk_gated().set_bit();
309            w.xpd_sar_force().bits(0b11)
310        });
311
312        Adc {
313            _adc: adc_instance,
314            attenuations: config.attenuations,
315            active_channel: None,
316            _guard: guard,
317            _phantom: PhantomData,
318        }
319    }
320
321    /// Reconfigures the ADC driver to operate in asynchronous mode.
322    pub fn into_async(mut self) -> Adc<'d, ADCI, Async> {
323        acquire_async_adc();
324        self.set_interrupt_handler(adc_interrupt_handler);
325
326        // Reset interrupt flags and disable oneshot reading to normalize state before
327        // entering async mode, otherwise there can be '0' readings, happening initially
328        // using ADC2
329        ADCI::reset();
330
331        Adc {
332            _adc: self._adc,
333            attenuations: self.attenuations,
334            active_channel: self.active_channel,
335            _guard: self._guard,
336            _phantom: PhantomData,
337        }
338    }
339
340    /// Request that the ADC begin a conversion on the specified pin
341    ///
342    /// This method takes an [AdcPin](super::AdcPin) reference, as it is
343    /// expected that the ADC will be able to sample whatever channel
344    /// underlies the pin.
345    pub fn read_oneshot<PIN, CS>(
346        &mut self,
347        pin: &mut super::AdcPin<PIN, ADCI, CS>,
348    ) -> nb::Result<u16, ()>
349    where
350        PIN: super::AdcChannel,
351        CS: super::AdcCalScheme<ADCI>,
352    {
353        if self.attenuations[pin.pin.adc_channel() as usize].is_none() {
354            panic!(
355                "Channel {} is not configured reading!",
356                pin.pin.adc_channel()
357            );
358        }
359
360        if let Some(active_channel) = self.active_channel {
361            // There is conversion in progress:
362            // - if it's for a different channel try again later
363            // - if it's for the given channel, go ahead and check progress
364            if active_channel != pin.pin.adc_channel() {
365                return Err(nb::Error::WouldBlock);
366            }
367        } else {
368            // If no conversions are in progress, start a new one for given channel
369            self.active_channel = Some(pin.pin.adc_channel());
370
371            // Set ADC unit calibration according used scheme for pin
372            ADCI::calibration_init();
373            ADCI::set_init_code(pin.cal_scheme.adc_cal());
374
375            let channel = self.active_channel.unwrap();
376            let attenuation = self.attenuations[channel as usize].unwrap() as u8;
377            ADCI::config_onetime_sample(channel, attenuation);
378            ADCI::start_onetime_sample();
379
380            // see https://github.com/espressif/esp-idf/blob/b4268c874a4cf8fcf7c0c4153cffb76ad2ddda4e/components/hal/adc_oneshot_hal.c#L105-L107
381            // the delay might be a bit generous but longer delay seem to not cause problems
382            #[cfg(esp32c6)]
383            {
384                crate::rom::ets_delay_us(40);
385                ADCI::start_onetime_sample();
386            }
387        }
388
389        // Wait for ADC to finish conversion
390        let conversion_finished = ADCI::is_done();
391        if !conversion_finished {
392            return Err(nb::Error::WouldBlock);
393        }
394
395        // Get converted value
396        let converted_value = ADCI::read_data();
397        ADCI::reset();
398
399        // Postprocess converted value according to calibration scheme used for pin
400        let converted_value = pin.cal_scheme.adc_val(converted_value);
401
402        // There is a hardware limitation. If the APB clock frequency is high, the step
403        // of this reg signal: ``onetime_start`` may not be captured by the
404        // ADC digital controller (when its clock frequency is too slow). A rough
405        // estimate for this step should be at least 3 ADC digital controller
406        // clock cycle.
407        //
408        // This limitation will be removed in hardware future versions.
409        // We reset ``onetime_start`` in `reset` and assume enough time has passed until
410        // the next sample is requested.
411
412        // Mark that no conversions are currently in progress
413        self.active_channel = None;
414
415        Ok(converted_value)
416    }
417}
418
419impl<ADCI> crate::private::Sealed for Adc<'_, ADCI, Blocking> {}
420
421impl<ADCI> InterruptConfigurable for Adc<'_, ADCI, Blocking> {
422    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
423        for core in crate::system::Cpu::other() {
424            crate::interrupt::disable(core, InterruptSource);
425        }
426        unsafe { crate::interrupt::bind_interrupt(InterruptSource, handler.handler()) };
427        unwrap!(crate::interrupt::enable(
428            InterruptSource,
429            handler.priority()
430        ));
431    }
432}
433
434#[cfg(adc_adc1)]
435impl super::AdcCalEfuse for crate::peripherals::ADC1<'_> {
436    fn init_code(atten: Attenuation) -> Option<u16> {
437        Efuse::rtc_calib_init_code(AdcCalibUnit::ADC1, atten)
438    }
439
440    fn cal_mv(atten: Attenuation) -> u16 {
441        Efuse::rtc_calib_cal_mv(AdcCalibUnit::ADC1, atten)
442    }
443
444    fn cal_code(atten: Attenuation) -> Option<u16> {
445        Efuse::rtc_calib_cal_code(AdcCalibUnit::ADC1, atten)
446    }
447}
448
449#[cfg(adc_adc2)]
450impl super::AdcCalEfuse for crate::peripherals::ADC2<'_> {
451    fn init_code(atten: Attenuation) -> Option<u16> {
452        Efuse::rtc_calib_init_code(AdcCalibUnit::ADC2, atten)
453    }
454
455    fn cal_mv(atten: Attenuation) -> u16 {
456        Efuse::rtc_calib_cal_mv(AdcCalibUnit::ADC2, atten)
457    }
458
459    fn cal_code(atten: Attenuation) -> Option<u16> {
460        Efuse::rtc_calib_cal_code(AdcCalibUnit::ADC2, atten)
461    }
462}
463
464impl<'d, ADCI> Adc<'d, ADCI, Async>
465where
466    ADCI: RegisterAccess + 'd,
467{
468    /// Create a new instance in [crate::Blocking] mode.
469    pub fn into_blocking(self) -> Adc<'d, ADCI, Blocking> {
470        if release_async_adc() {
471            // Disable ADC interrupt on all cores if the last async ADC instance is disabled
472            for cpu in crate::system::Cpu::all() {
473                crate::interrupt::disable(cpu, InterruptSource);
474            }
475        }
476        Adc {
477            _adc: self._adc,
478            attenuations: self.attenuations,
479            active_channel: self.active_channel,
480            _guard: self._guard,
481            _phantom: PhantomData,
482        }
483    }
484
485    /// Request that the ADC begin a conversion on the specified pin
486    ///
487    /// This method takes an [AdcPin](super::AdcPin) reference, as it is
488    /// expected that the ADC will be able to sample whatever channel
489    /// underlies the pin.
490    pub async fn read_oneshot<PIN, CS>(&mut self, pin: &mut super::AdcPin<PIN, ADCI, CS>) -> u16
491    where
492        ADCI: Instance,
493        PIN: super::AdcChannel,
494        CS: super::AdcCalScheme<ADCI>,
495    {
496        let channel = pin.pin.adc_channel();
497        if self.attenuations[channel as usize].is_none() {
498            panic!("Channel {} is not configured reading!", channel);
499        }
500
501        // Set ADC unit calibration according used scheme for pin
502        ADCI::calibration_init();
503        ADCI::set_init_code(pin.cal_scheme.adc_cal());
504
505        let attenuation = self.attenuations[channel as usize].unwrap() as u8;
506        ADCI::config_onetime_sample(channel, attenuation);
507        ADCI::start_onetime_sample();
508
509        // Wait for ADC to finish conversion and get value
510        let adc_ready_future = AdcFuture::new(self);
511        adc_ready_future.await;
512        let converted_value = ADCI::read_data();
513
514        // There is a hardware limitation. If the APB clock frequency is high, the step
515        // of this reg signal: ``onetime_start`` may not be captured by the
516        // ADC digital controller (when its clock frequency is too slow). A rough
517        // estimate for this step should be at least 3 ADC digital controller
518        // clock cycle.
519        //
520        // This limitation will be removed in hardware future versions.
521        // We reset ``onetime_start`` in `reset` and assume enough time has passed until
522        // the next sample is requested.
523
524        ADCI::reset();
525
526        // Postprocess converted value according to calibration scheme used for pin
527        pin.cal_scheme.adc_val(converted_value)
528    }
529}
530
531#[cfg(all(adc_adc1, adc_adc2))]
532static ASYNC_ADC_COUNT: AtomicU32 = AtomicU32::new(0);
533
534pub(super) fn acquire_async_adc() {
535    #[cfg(all(adc_adc1, adc_adc2))]
536    ASYNC_ADC_COUNT.fetch_add(1, Ordering::Relaxed);
537}
538
539pub(super) fn release_async_adc() -> bool {
540    cfg_if::cfg_if! {
541        if #[cfg(all(adc_adc1, adc_adc2))] {
542            ASYNC_ADC_COUNT.fetch_sub(1, Ordering::Relaxed) == 1
543        } else {
544            true
545        }
546    }
547}
548
549#[handler]
550pub(crate) fn adc_interrupt_handler() {
551    let saradc = APB_SARADC::regs();
552    let interrupt_status = saradc.int_st().read();
553
554    #[cfg(adc_adc1)]
555    if interrupt_status.adc1_done().bit_is_set() {
556        unsafe { handle_async(crate::peripherals::ADC1::steal()) }
557    }
558
559    #[cfg(adc_adc2)]
560    if interrupt_status.adc2_done().bit_is_set() {
561        unsafe { handle_async(crate::peripherals::ADC2::steal()) }
562    }
563}
564
565fn handle_async<ADCI: Instance>(_instance: ADCI) {
566    ADCI::waker().wake();
567    ADCI::unlisten();
568}
569
570/// Enable asynchronous access.
571pub trait Instance: crate::private::Sealed {
572    /// Enable the ADC interrupt
573    fn listen();
574
575    /// Disable the ADC interrupt
576    fn unlisten();
577
578    /// Clear the ADC interrupt
579    fn clear_interrupt();
580
581    /// Obtain the waker for the ADC interrupt
582    fn waker() -> &'static AtomicWaker;
583}
584
585#[cfg(adc_adc1)]
586impl Instance for crate::peripherals::ADC1<'_> {
587    fn listen() {
588        APB_SARADC::regs()
589            .int_ena()
590            .modify(|_, w| w.adc1_done().set_bit());
591    }
592
593    fn unlisten() {
594        APB_SARADC::regs()
595            .int_ena()
596            .modify(|_, w| w.adc1_done().clear_bit());
597    }
598
599    fn clear_interrupt() {
600        APB_SARADC::regs()
601            .int_clr()
602            .write(|w| w.adc1_done().clear_bit_by_one());
603    }
604
605    fn waker() -> &'static AtomicWaker {
606        static WAKER: AtomicWaker = AtomicWaker::new();
607
608        &WAKER
609    }
610}
611
612#[cfg(adc_adc2)]
613impl Instance for crate::peripherals::ADC2<'_> {
614    fn listen() {
615        APB_SARADC::regs()
616            .int_ena()
617            .modify(|_, w| w.adc2_done().set_bit());
618    }
619
620    fn unlisten() {
621        APB_SARADC::regs()
622            .int_ena()
623            .modify(|_, w| w.adc2_done().clear_bit());
624    }
625
626    fn clear_interrupt() {
627        APB_SARADC::regs()
628            .int_clr()
629            .write(|w| w.adc2_done().clear_bit_by_one());
630    }
631
632    fn waker() -> &'static AtomicWaker {
633        static WAKER: AtomicWaker = AtomicWaker::new();
634
635        &WAKER
636    }
637}
638
639#[must_use = "futures do nothing unless you `.await` or poll them"]
640pub(crate) struct AdcFuture<ADCI: Instance> {
641    phantom: PhantomData<ADCI>,
642}
643
644impl<ADCI: Instance> AdcFuture<ADCI> {
645    pub fn new(_self: &super::Adc<'_, ADCI, Async>) -> Self {
646        Self {
647            phantom: PhantomData,
648        }
649    }
650}
651
652impl<ADCI: Instance + super::RegisterAccess> core::future::Future for AdcFuture<ADCI> {
653    type Output = ();
654
655    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
656        if ADCI::is_done() {
657            ADCI::clear_interrupt();
658            Poll::Ready(())
659        } else {
660            ADCI::waker().register(cx.waker());
661            ADCI::listen();
662            Poll::Pending
663        }
664    }
665}
666
667impl<ADCI: Instance> Drop for AdcFuture<ADCI> {
668    fn drop(&mut self) {
669        ADCI::unlisten();
670    }
671}