Skip to main content

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