Skip to main content

ra_hal/
adc.rs

1//!  Analog-to-Digital Converter (`ADC12`, `ADC14`, `ADC16`).
2//!
3//! # Supported Hardware
4//! * `ADC12`: supports 12-bit precision (`RA4L1`) or 8-, 10-, 12-bit precision (`RA6M5`, soon).
5//! * `ADC14`: supports 12- and 14-bit precision (`RA4M1`).
6//! * `ADC16`: supports 16-bit precision (`RA2A1`).
7//!
8//! # Notes
9//! * The driver will turn the module off when it is dropped.
10
11use core::{marker::PhantomData, task::Poll};
12
13use cortex_m::asm;
14use embassy_hal_internal::{Peri, PeripheralType};
15
16use crate::{
17    adc::channel::AdcChannel,
18    interrupt::typelevel::{Binding, Interrupt as InterruptType},
19    module_stop::ModuleStop,
20    pac,
21};
22
23#[cfg(adc12)]
24use pac::adc12::{self as adc_pac, Adc12 as AdcPac};
25#[cfg(adc14)]
26use pac::adc14::{self as adc_pac, Adc14 as AdcPac};
27#[cfg(adc16)]
28use pac::adc16::{self as adc_pac, Adc16 as AdcPac};
29
30use adc_pac::{
31    regs::Adans,
32    vals::{AdcCountSelect, Adcs},
33};
34
35#[cfg(adc12)]
36use crate::peripherals::ADC12_0;
37
38#[cfg(adc14)]
39use crate::{pac::adc14::vals::Adprc, peripherals::ADC14_0};
40
41#[cfg(adc16)]
42use crate::peripherals::ADC16_0;
43
44pub use channel::{AdcInputPin, AdcPin, AdcSequence, Temperature, Vref};
45
46#[cfg(dmac)]
47use crate::dmac::{Channel as DmacChannel, DmacInterruptHandler};
48
49pub(crate) mod channel;
50
51/// `ADC` driver.
52#[allow(private_bounds)]
53pub struct Adc<'d, I: Instance> {
54    _phantom: PhantomData<&'d I>,
55    average_mode: AverageMode,
56    sample_time: u8,
57}
58
59/// Addition/Average method.
60///
61/// For a single reading:
62/// * `ADC12` and `ADC14` can report the single sample or the sum or average of multiple samples.
63/// * `ADC16` can report the single sample or the average of multiple samples.
64#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65#[derive(Copy, Clone, Default, PartialEq)]
66pub enum AverageMode {
67    /// Sample the channel once.
68    #[default]
69    Off,
70
71    /// Sample the channel 2x and place the average in the output register.
72    Average2,
73
74    /// Sample the channel 4x and place the average in the output register.
75    Average4,
76
77    /// Place the sum of 2 samples in the output register.
78    #[cfg(any(adc12, adc14))]
79    Add2,
80
81    /// Place the sum of 3 samples in the output register.
82    #[cfg(any(adc12, adc14))]
83    Add3,
84
85    /// Place the sum of 4 samples in the output register.
86    #[cfg(any(adc12, adc14))]
87    Add4,
88
89    /// Place the sum of 16 samples in the output register.
90    #[cfg(any(adc12, adc14))]
91    Add16,
92}
93
94/// ADC resolution.
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96#[derive(Copy, Clone, Default)]
97pub enum Resolution {
98    /// 12-bit resolution
99    #[cfg(any(adc12, adc14))]
100    #[default]
101    _12bit,
102
103    /// 14-bit resolution
104    #[cfg(adc14)]
105    _14bit,
106
107    /// 16-bit resolution
108    #[cfg(adc16)]
109    #[default]
110    _16bit,
111}
112
113/// Alignment of readings within a 16-bit variable.
114#[cfg_attr(feature = "defmt", derive(defmt::Format))]
115#[derive(Copy, Clone, Default)]
116pub enum Alignment {
117    /// Pad the value with trailing zeros.
118    FlushLeft,
119
120    /// Pad the value with leading zeros.
121    #[default]
122    FlushRight,
123}
124
125/// `ADC` configuration.
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127#[derive(Copy, Clone)]
128pub struct AdcConfig {
129    #[allow(missing_docs)]
130    pub resolution: Resolution,
131
132    /// If true the data registers are zeroed after being read (whether by CPU or DMA).
133    pub clear_on_read: bool,
134
135    #[allow(missing_docs)]
136    #[cfg(not(adc16))]
137    pub alignment: Alignment,
138
139    #[allow(missing_docs)]
140    pub average_mode: AverageMode,
141
142    /// Sample time in number of `ADCLK` cycles.
143    ///
144    /// # Notes
145    /// Sample time can *never* be lower than 5 states.
146    /// If the ratio of `PCLKB` to `PCLKC` is 1:2 or 1:4 the minimum is 6 states.
147    pub sample_time: u8,
148}
149
150#[derive(Copy, Clone)]
151struct AdcChannelConfig {
152    pub average_mode: AverageMode,
153    pub sample_time: u8,
154}
155
156/// `ADC` peripheral instance.
157#[allow(private_bounds)]
158pub trait Instance: SealedInstance + ModuleStop + PeripheralType + 'static + Send {
159    /// The interrupt event that is triggered when the ADC scan is complete.
160    const RX_EVENT: crate::event_link::InterruptEvent;
161}
162
163impl Default for AdcConfig {
164    fn default() -> Self {
165        Self {
166            resolution: Default::default(),
167            clear_on_read: Default::default(),
168            #[cfg(not(adc16))]
169            alignment: Default::default(),
170            average_mode: Default::default(),
171            sample_time: 13,
172        }
173    }
174}
175
176trait SealedInstance: PeripheralType {
177    fn regs() -> AdcPac;
178}
179
180#[cfg(adc12)]
181impl SealedInstance for ADC12_0 {
182    fn regs() -> AdcPac {
183        pac::ADC12_0
184    }
185}
186
187#[cfg(adc14)]
188impl SealedInstance for ADC14_0 {
189    fn regs() -> AdcPac {
190        pac::ADC14_0
191    }
192}
193
194#[cfg(adc16)]
195impl SealedInstance for ADC16_0 {
196    fn regs() -> AdcPac {
197        pac::ADC16_0
198    }
199}
200
201#[cfg(adc12)]
202impl Instance for ADC12_0 {
203    const RX_EVENT: crate::event_link::InterruptEvent =
204        crate::event_link::InterruptEvent::Adc0ScanEnd;
205}
206
207#[cfg(adc14)]
208impl Instance for ADC14_0 {
209    const RX_EVENT: crate::event_link::InterruptEvent =
210        crate::event_link::InterruptEvent::Adc0ScanEnd;
211}
212
213#[cfg(adc16)]
214impl Instance for ADC16_0 {
215    const RX_EVENT: crate::event_link::InterruptEvent =
216        crate::event_link::InterruptEvent::Adc0ScanEnd;
217}
218
219impl<'d, I: Instance> Adc<'d, I> {
220    /// Creates a new `ADC` driver.
221    pub fn new(_adc: Peri<'d, I>, config: AdcConfig) -> Self {
222        #[cfg(feature = "strict-assert")]
223        assert!(config.sample_time >= 5);
224
225        I::start_module();
226
227        #[cfg(adc14)]
228        let resolution = match config.resolution {
229            Resolution::_12bit => Adprc::_12bit,
230            Resolution::_14bit => Adprc::_14bit,
231        };
232
233        let clear_on_read = config.clear_on_read;
234
235        #[cfg(not(adc16))]
236        let alignment = match config.alignment {
237            Alignment::FlushLeft => true,
238            Alignment::FlushRight => false,
239        };
240
241        let adc = I::regs();
242
243        // Turn everything off.
244        adc.adansa(0).write_value(Adans(0));
245        adc.adansa(1).write_value(Adans(0));
246
247        adc.adexicr().modify(|w| {
248            w.set_ocsa(false);
249            w.set_tssa(false);
250        });
251
252        adc.adcer().write(|w| {
253            #[cfg(adc14)]
254            {
255                w.set_adprc(resolution);
256            }
257
258            #[cfg(any(adc12, adc14))]
259            {
260                w.set_adrfmt(alignment);
261            }
262
263            w.set_ace(clear_on_read);
264        });
265
266        adc.adadc().write(|w| match config.average_mode {
267            AverageMode::Off => {}
268            AverageMode::Average2 => {
269                #[cfg(any(adc12, adc14))]
270                w.set_avee(true);
271                w.set_adc(AdcCountSelect::Convert2);
272            }
273            AverageMode::Average4 => {
274                #[cfg(any(adc12, adc14))]
275                w.set_avee(true);
276                w.set_adc(AdcCountSelect::Convert4);
277            }
278            #[cfg(any(adc12, adc14))]
279            AverageMode::Add2 => {
280                w.set_avee(false);
281                w.set_adc(AdcCountSelect::Convert2);
282            }
283            #[cfg(any(adc12, adc14))]
284            AverageMode::Add3 => {
285                w.set_avee(false);
286                w.set_adc(AdcCountSelect::Convert3);
287            }
288            #[cfg(any(adc12, adc14))]
289            AverageMode::Add4 => {
290                w.set_avee(false);
291                w.set_adc(AdcCountSelect::Convert4);
292            }
293            #[cfg(any(adc12, adc14))]
294            AverageMode::Add16 => {
295                w.set_avee(false);
296                w.set_adc(AdcCountSelect::Convert16);
297            }
298        });
299
300        Self {
301            _phantom: PhantomData,
302            average_mode: config.average_mode,
303            sample_time: config.sample_time,
304        }
305    }
306
307    /// Returns the currently configured resolution (12 or 14-bits).
308    pub fn resolution(&self) -> Resolution {
309        #[cfg(adc12)]
310        {
311            Resolution::_12bit
312        }
313
314        #[cfg(adc14)]
315        {
316            let adc = I::regs();
317
318            match adc.adcer().read().adprc() {
319                Adprc::_12bit => Resolution::_12bit,
320                Adprc::_RESERVED_1 => unimplemented!(),
321                Adprc::_RESERVED_2 => unimplemented!(),
322                Adprc::_14bit => Resolution::_14bit,
323            }
324        }
325
326        #[cfg(adc16)]
327        {
328            Resolution::_16bit
329        }
330    }
331
332    /// Returns the currently configured alignment. §35.2.1, §35.2.11
333    #[cfg(adc14)]
334    pub fn alignment(&self) -> Alignment {
335        let adc = I::regs();
336
337        match adc.adcer().read().adrfmt() {
338            true => Alignment::FlushLeft,
339            false => Alignment::FlushRight,
340        }
341    }
342
343    /// Returns the on-die [temperature measurement pseudo-channel](Temperature) without performing configuration.
344    pub fn temperature_channel(&self) -> Temperature {
345        Temperature {}
346    }
347
348    /// Returns the [`Vref`] pseudo channel.
349    pub fn vref_channel(&self) -> Vref {
350        Vref {}
351    }
352
353    /// Returns a single reading from an ADC channel.
354    /// Generic over `R` so we can return individual values or sequences as needed.
355    pub fn blocking_read<R, C: AdcChannel<R>>(&self, channel: &C) -> R {
356        let adc = I::regs();
357
358        channel.enable::<I>(AdcChannelConfig {
359            average_mode: self.average_mode,
360            sample_time: self.sample_time,
361        });
362
363        // TODO: read ADST first to ensure we're stopped?
364
365        adc.adcsr().modify(|w| w.set_adcs(Adcs::Single));
366
367        adc.adcsr().modify(|w| w.set_adst(true));
368
369        // When the conversion is finished an interrupt is fired (without modifying the registers) and the ADST bit is cleared
370        while adc.adcsr().read().adst() {
371            asm::nop()
372        }
373
374        channel.disable::<I>();
375
376        channel.read_one::<I>()
377    }
378
379    /// Returns a single reading from an ADC channel via DMA.
380    ///
381    /// The channel selection registers are re-programmed on every call. Use
382    /// [`configured_sequence`](Adc::configured_sequence) if you need repeated reads
383    /// from the same channel without that per-call overhead.
384    #[cfg(dmac)]
385    pub async fn read<
386        'a,
387        R: crate::dmac::Word,
388        RxDmaInstance: crate::dmac::Instance,
389        C: AdcChannel<R>,
390        DmaInt: InterruptType,
391    >(
392        &'a self,
393        rx_dma: Peri<'a, RxDmaInstance>,
394        channel: &C,
395        reading: &mut R,
396        irqs: impl Binding<DmaInt, DmacInterruptHandler<RxDmaInstance>>,
397    ) {
398        let adc = I::regs();
399
400        let mut rx_dma = DmacChannel::new(rx_dma, irqs);
401
402        channel.enable::<I>(AdcChannelConfig {
403            average_mode: self.average_mode,
404            sample_time: self.sample_time,
405        });
406
407        adc.adcsr().modify(|w| w.set_adcs(Adcs::Single));
408
409        let rx = rx_dma.read::<R>(
410            channel.address::<I>(),
411            core::slice::from_mut(reading),
412            I::RX_EVENT,
413        );
414
415        adc.adcsr().modify(|w| w.set_adst(true));
416        rx.await;
417
418        channel.disable::<I>();
419    }
420
421    /// Configure an ADC channel once and return a [`ConfiguredSequence`] for repeated DMA reads.
422    ///
423    /// Unlike [`read`](Adc::read), the channel selection registers are programmed only at
424    /// construction. Each call to [`ConfiguredSequence::read`] skips that setup, which is useful
425    /// when sampling the same channel at high rates.
426    #[cfg(dmac)]
427    pub fn configured_sequence<
428        'a,
429        R: crate::dmac::Word,
430        RxDmaInstance: crate::dmac::Instance,
431        C: AdcChannel<R>,
432        DmaInt: InterruptType,
433    >(
434        &'a self,
435        rx_dma: Peri<'a, RxDmaInstance>,
436        channel: &C,
437        irqs: impl Binding<DmaInt, DmacInterruptHandler<RxDmaInstance>> + 'a,
438    ) -> ConfiguredSequence<'a, I, R> {
439        let adc = I::regs();
440
441        channel.enable::<I>(AdcChannelConfig {
442            average_mode: self.average_mode,
443            sample_time: self.sample_time,
444        });
445
446        adc.adcsr().modify(|w| w.set_adcs(Adcs::Single));
447
448        ConfiguredSequence {
449            dma: DmacChannel::new(rx_dma, irqs),
450            source: channel.address::<I>(),
451            _phantom: PhantomData,
452        }
453    }
454
455    /// Configure an ADC channel for continuous DMA sampling into a ping-pong buffer.
456    ///
457    /// The ADC is placed in continuous scan mode: it runs without software re-triggering
458    /// and each scan-end event fires the DMA to write one sample.
459    ///
460    /// Because the RA4M1 DMAC has no half-transfer interrupt, the driver implements a
461    /// software ping-pong: the DMA fills half of `dma_buf` at a time. When that half is
462    /// full, [`RingBufferedAdc::read`] re-arms the DMA for the other half before returning
463    /// to the caller, keeping the gap where samples might be lost as short as possible.
464    ///
465    /// `dma_buf` must have an even length >= 2. Each call to [`RingBufferedAdc::read`]
466    /// waits for `dma_buf.len() / 2` samples and copies them into the provided output.
467    #[cfg(dmac)]
468    pub fn into_ring_buffered<
469        'a,
470        R: crate::dmac::Word,
471        RxDmaInstance: crate::dmac::Instance,
472        C: AdcChannel<u16>,
473        DmaInt: InterruptType,
474    >(
475        self,
476        rx_dma: Peri<'a, RxDmaInstance>,
477        channel: &C,
478        dma_buf: &'a mut [R],
479        irqs: impl Binding<DmaInt, DmacInterruptHandler<RxDmaInstance>> + 'a,
480    ) -> RingBufferedAdc<'a, I, R>
481    where
482        'd: 'a,
483    {
484        assert!(
485            dma_buf.len() >= 2 && dma_buf.len() % 2 == 0,
486            "DMA buffer must have an even length >= 2"
487        );
488
489        let source = channel.address::<I>();
490
491        channel.enable::<I>(AdcChannelConfig {
492            average_mode: self.average_mode,
493            sample_time: self.sample_time,
494        });
495
496        I::regs().adcsr().modify(|w| w.set_adcs(Adcs::Continuous));
497
498        let mut dma = DmacChannel::new(rx_dma, irqs);
499        dma.configure_peripheral_read::<R>(source as _, I::RX_EVENT);
500
501        // Prevent the Adc drop from stopping the module; RingBufferedAdc::drop handles it.
502        core::mem::forget(self);
503
504        RingBufferedAdc {
505            dma,
506            source: source as _,
507            buf: dma_buf.as_mut_ptr(),
508            half_len: dma_buf.len() / 2,
509            fill_half: false,
510            started: false,
511            _phantom: PhantomData,
512        }
513    }
514}
515
516/// An ADC continuously sampling one channel into a ping-pong DMA buffer.
517///
518/// Because the RA DMAC has no half-transfer interrupt, this driver implements a software
519/// ping-pong: the DMA fills one half of the internal buffer (`dma_buf.len() / 2` samples),
520/// signals completion via DTIF, and [`read`](RingBufferedAdc::read) immediately re-arms the
521/// DMA for the other half before copying the completed half to the caller. The window where
522/// incoming scan-end events are not forwarded to memory is kept to a few register writes.
523///
524/// Obtain via [`Adc::into_ring_buffered`].
525#[cfg(dmac)]
526#[allow(private_bounds)]
527pub struct RingBufferedAdc<'d, I: Instance, R: crate::dmac::Word> {
528    dma: DmacChannel<'d>,
529    #[allow(dead_code)]
530    source: *const R,
531    buf: *mut R,
532    half_len: usize,
533    /// The half the DMA is currently filling (false = first, true = second).
534    /// This is also the half that will be ready to read on the next DTIF.
535    fill_half: bool,
536    started: bool,
537    _phantom: PhantomData<(&'d I, *mut R)>,
538}
539
540#[cfg(dmac)]
541impl<'d, I: Instance, R: crate::dmac::Word> RingBufferedAdc<'d, I, R> {
542    /// Arms the DMA to fill the half indicated by `self.fill_half`.
543    #[inline]
544    fn arm(&mut self) {
545        let dest = unsafe {
546            if self.fill_half {
547                self.buf.add(self.half_len)
548            } else {
549                self.buf
550            }
551        };
552        self.dma.rearm::<R>(dest, self.half_len);
553    }
554
555    /// Start continuous ADC sampling. Called automatically by [`read`](Self::read).
556    pub fn start(&mut self) {
557        if self.started {
558            return;
559        }
560        self.started = true;
561        self.fill_half = false;
562        self.arm();
563        I::regs().adcsr().modify(|w| w.set_adst(true));
564    }
565
566    /// Stop continuous ADC sampling.
567    pub fn stop(&mut self) {
568        I::regs().adcsr().modify(|w| w.set_adst(false));
569        self.dma.disable_dte();
570        self.started = false;
571    }
572
573    /// Read one half-buffer of ADC samples.
574    ///
575    /// Waits until the DMA has finished filling the current half, then immediately re-arms
576    /// the DMA for the other half and copies the completed data into `buf`.
577    ///
578    /// `buf.len()` must equal `dma_buf.len() / 2` (i.e. exactly one half of the buffer
579    /// passed to [`into_ring_buffered`](Adc::into_ring_buffered)).
580    pub async fn read(&mut self, buf: &mut [R]) {
581        #[cfg(feature = "strict-assert")]
582        assert_eq!(
583            buf.len(),
584            self.half_len,
585            "Buffer must be half the DMA buffer length"
586        );
587
588        if !self.started {
589            self.start();
590        }
591
592        let fill_half = self.fill_half;
593
594        core::future::poll_fn(|cx| {
595            self.dma.register_waker(cx.waker());
596
597            if self.dma.take_dtif() {
598                // Switch halves and immediately re-arm so the gap is minimal.
599                self.fill_half = !fill_half;
600                self.arm();
601                Poll::Ready(())
602            } else {
603                Poll::Pending
604            }
605        })
606        .await;
607
608        // Copy the completed half (the one DMA just finished) to the caller's buffer.
609        let src_offset = if fill_half { self.half_len } else { 0 };
610        let src = unsafe { core::slice::from_raw_parts(self.buf.add(src_offset), self.half_len) };
611        buf.copy_from_slice(src);
612    }
613}
614
615#[cfg(dmac)]
616impl<I: Instance, R: crate::dmac::Word> Drop for RingBufferedAdc<'_, I, R> {
617    fn drop(&mut self) {
618        if self.started {
619            self.stop();
620        }
621        let adc = I::regs();
622        adc.adansa(0).write_value(Adans(0));
623        adc.adansa(1).write_value(Adans(0));
624        adc.adexicr().modify(|w| {
625            w.set_ocsa(false);
626            w.set_tssa(false);
627        });
628        I::stop_module();
629    }
630}
631
632/// An ADC with a pre-configured channel for repeated DMA reads.
633///
634/// Unlike [`Adc::read`], this type programs the ADC channel selection registers only once at
635/// construction. Each call to [`read`](ConfiguredSequence::read) reuses the existing hardware
636/// configuration, skipping the per-call overhead of reprogramming those registers.
637///
638/// Obtain via [`Adc::configured_sequence`].
639#[cfg(dmac)]
640#[allow(private_bounds)]
641pub struct ConfiguredSequence<'d, I: Instance, R: crate::dmac::Word> {
642    dma: DmacChannel<'d>,
643    source: *const R,
644    _phantom: PhantomData<(&'d I, R)>,
645}
646
647#[cfg(dmac)]
648impl<'d, I: Instance, R: crate::dmac::Word> ConfiguredSequence<'d, I, R> {
649    /// Trigger one DMA conversion of the pre-configured channel and wait for it to complete.
650    pub async fn read(&mut self, reading: &mut R) {
651        let adc = I::regs();
652        let rx = self
653            .dma
654            .read::<R>(self.source, core::slice::from_mut(reading), I::RX_EVENT);
655        adc.adcsr().modify(|w| w.set_adst(true));
656        rx.await;
657    }
658}
659
660#[cfg(dmac)]
661impl<I: Instance, R: crate::dmac::Word> Drop for ConfiguredSequence<'_, I, R> {
662    fn drop(&mut self) {
663        let adc = I::regs();
664        adc.adansa(0).write_value(Adans(0));
665        adc.adansa(1).write_value(Adans(0));
666        adc.adexicr().modify(|w| {
667            w.set_ocsa(false);
668            w.set_tssa(false);
669        });
670    }
671}
672
673impl<'d, I: Instance> Drop for Adc<'d, I> {
674    fn drop(&mut self) {
675        I::stop_module();
676    }
677}