embassy_nrf/
saadc.rs

1//! Successive Approximation Analog-to-Digital Converter (SAADC) driver.
2
3#![macro_use]
4
5use core::future::poll_fn;
6use core::marker::PhantomData;
7use core::sync::atomic::{Ordering, compiler_fence};
8use core::task::Poll;
9
10use embassy_hal_internal::drop::OnDrop;
11use embassy_hal_internal::{Peri, impl_peripheral};
12use embassy_sync::waitqueue::AtomicWaker;
13#[cfg(not(feature = "_nrf54l"))]
14pub(crate) use vals::Psel as InputChannel;
15
16use crate::interrupt::InterruptExt;
17use crate::pac::saadc::vals;
18use crate::ppi::{ConfigurableChannel, Event, Ppi, Task};
19use crate::timer::{Frequency, Instance as TimerInstance, Timer};
20use crate::{interrupt, pac, peripherals};
21
22/// SAADC error
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25#[non_exhaustive]
26pub enum Error {}
27
28/// Interrupt handler.
29pub struct InterruptHandler {
30    _private: (),
31}
32
33impl interrupt::typelevel::Handler<interrupt::typelevel::SAADC> for InterruptHandler {
34    unsafe fn on_interrupt() {
35        let r = pac::SAADC;
36
37        if r.events_calibratedone().read() != 0 {
38            r.intenclr().write(|w| w.set_calibratedone(true));
39            WAKER.wake();
40        }
41
42        if r.events_end().read() != 0 {
43            r.intenclr().write(|w| w.set_end(true));
44            WAKER.wake();
45        }
46
47        if r.events_started().read() != 0 {
48            r.intenclr().write(|w| w.set_started(true));
49            WAKER.wake();
50        }
51    }
52}
53
54static WAKER: AtomicWaker = AtomicWaker::new();
55
56/// Used to configure the SAADC peripheral.
57///
58/// See the `Default` impl for suitable default values.
59#[non_exhaustive]
60pub struct Config {
61    /// Output resolution in bits.
62    pub resolution: Resolution,
63    /// Average 2^`oversample` input samples before transferring the result into memory.
64    pub oversample: Oversample,
65}
66
67impl Default for Config {
68    /// Default configuration for single channel sampling.
69    fn default() -> Self {
70        Self {
71            resolution: Resolution::_12BIT,
72            oversample: Oversample::BYPASS,
73        }
74    }
75}
76
77/// Used to configure an individual SAADC peripheral channel.
78///
79/// Construct using the `single_ended` or `differential` methods.  These provide sensible defaults
80/// for the public fields, which can be overridden if required.
81#[non_exhaustive]
82pub struct ChannelConfig<'d> {
83    /// Reference voltage of the SAADC input.
84    pub reference: Reference,
85    /// Gain used to control the effective input range of the SAADC.
86    pub gain: Gain,
87    /// Positive channel resistor control.
88    #[cfg(not(feature = "_nrf54l"))]
89    pub resistor: Resistor,
90    /// Acquisition time in microseconds.
91    pub time: Time,
92    /// Positive channel to sample
93    p_channel: AnyInput<'d>,
94    /// An optional negative channel to sample
95    n_channel: Option<AnyInput<'d>>,
96}
97
98impl<'d> ChannelConfig<'d> {
99    /// Default configuration for single ended channel sampling.
100    pub fn single_ended(input: impl Input + 'd) -> Self {
101        Self {
102            reference: Reference::INTERNAL,
103            #[cfg(not(feature = "_nrf54l"))]
104            gain: Gain::GAIN1_6,
105            #[cfg(feature = "_nrf54l")]
106            gain: Gain::GAIN2_8,
107            #[cfg(not(feature = "_nrf54l"))]
108            resistor: Resistor::BYPASS,
109            time: Time::_10US,
110            p_channel: input.degrade_saadc(),
111            n_channel: None,
112        }
113    }
114    /// Default configuration for differential channel sampling.
115    pub fn differential(p_input: impl Input + 'd, n_input: impl Input + 'd) -> Self {
116        Self {
117            reference: Reference::INTERNAL,
118            #[cfg(not(feature = "_nrf54l"))]
119            gain: Gain::GAIN1_6,
120            #[cfg(feature = "_nrf54l")]
121            gain: Gain::GAIN2_8,
122            #[cfg(not(feature = "_nrf54l"))]
123            resistor: Resistor::BYPASS,
124            time: Time::_10US,
125            p_channel: p_input.degrade_saadc(),
126            n_channel: Some(n_input.degrade_saadc()),
127        }
128    }
129}
130
131const CNT_UNIT: usize = if cfg!(feature = "_nrf54l") { 2 } else { 1 };
132
133/// Value returned by the SAADC callback, deciding what happens next.
134#[derive(PartialEq)]
135pub enum CallbackResult {
136    /// The SAADC should keep sampling and calling the callback.
137    Continue,
138    /// The SAADC should stop sampling, and return.
139    Stop,
140}
141
142/// One-shot and continuous SAADC.
143pub struct Saadc<'d, const N: usize> {
144    _p: Peri<'d, peripherals::SAADC>,
145}
146
147impl<'d, const N: usize> Saadc<'d, N> {
148    /// Create a new SAADC driver.
149    pub fn new(
150        saadc: Peri<'d, peripherals::SAADC>,
151        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SAADC, InterruptHandler> + 'd,
152        config: Config,
153        channel_configs: [ChannelConfig; N],
154    ) -> Self {
155        let r = pac::SAADC;
156
157        let Config { resolution, oversample } = config;
158
159        // Configure channels
160        r.enable().write(|w| w.set_enable(true));
161        r.resolution().write(|w| w.set_val(resolution.into()));
162        r.oversample().write(|w| w.set_oversample(oversample.into()));
163
164        for (i, cc) in channel_configs.iter().enumerate() {
165            #[cfg(not(feature = "_nrf54l"))]
166            r.ch(i).pselp().write(|w| w.set_pselp(cc.p_channel.channel()));
167            #[cfg(feature = "_nrf54l")]
168            r.ch(i).pselp().write(|w| {
169                w.set_port(cc.p_channel.port());
170                w.set_pin(cc.p_channel.pin());
171                w.set_internal(cc.p_channel.internal());
172                w.set_connect(cc.p_channel.connect());
173            });
174            if let Some(n_channel) = &cc.n_channel {
175                #[cfg(not(feature = "_nrf54l"))]
176                r.ch(i).pseln().write(|w| w.set_pseln(n_channel.channel()));
177                #[cfg(feature = "_nrf54l")]
178                r.ch(i).pseln().write(|w| {
179                    w.set_port(n_channel.port());
180                    w.set_pin(n_channel.pin());
181                    w.set_connect(n_channel.connect().to_bits().into());
182                });
183            }
184            r.ch(i).config().write(|w| {
185                w.set_refsel(cc.reference.into());
186                w.set_gain(cc.gain.into());
187                w.set_tacq(cc.time.into());
188                #[cfg(feature = "_nrf54l")]
189                w.set_tconv(7); // 7 is the default from the Nordic C driver
190                w.set_mode(match cc.n_channel {
191                    None => vals::ConfigMode::SE,
192                    Some(_) => vals::ConfigMode::DIFF,
193                });
194                #[cfg(not(feature = "_nrf54l"))]
195                w.set_resp(cc.resistor.into());
196                #[cfg(not(feature = "_nrf54l"))]
197                w.set_resn(vals::Resn::BYPASS);
198                #[cfg(not(feature = "_nrf54l"))]
199                w.set_burst(!matches!(oversample, Oversample::BYPASS));
200            });
201        }
202
203        // Disable all events interrupts
204        r.intenclr().write(|w| w.0 = 0x003F_FFFF);
205
206        interrupt::SAADC.unpend();
207        unsafe { interrupt::SAADC.enable() };
208
209        Self { _p: saadc }
210    }
211
212    fn regs() -> pac::saadc::Saadc {
213        pac::SAADC
214    }
215
216    /// Perform SAADC calibration. Completes when done.
217    pub async fn calibrate(&self) {
218        let r = Self::regs();
219
220        // Reset and enable the end event
221        r.events_calibratedone().write_value(0);
222        r.intenset().write(|w| w.set_calibratedone(true));
223
224        // Order is important
225        compiler_fence(Ordering::SeqCst);
226
227        r.tasks_calibrateoffset().write_value(1);
228
229        // Wait for 'calibratedone' event.
230        poll_fn(|cx| {
231            let r = Self::regs();
232
233            WAKER.register(cx.waker());
234
235            if r.events_calibratedone().read() != 0 {
236                r.events_calibratedone().write_value(0);
237                return Poll::Ready(());
238            }
239
240            Poll::Pending
241        })
242        .await;
243    }
244
245    /// One shot sampling. The buffer must be the same size as the number of channels configured.
246    /// The sampling is stopped prior to returning in order to reduce power consumption (power
247    /// consumption remains higher if sampling is not stopped explicitly). Cancellation will
248    /// also cause the sampling to be stopped.
249    pub async fn sample(&mut self, buf: &mut [i16; N]) {
250        // In case the future is dropped, stop the task and wait for it to end.
251        let on_drop = OnDrop::new(Self::stop_sampling_immediately);
252
253        let r = Self::regs();
254
255        // Set up the DMA
256        r.result().ptr().write_value(buf.as_mut_ptr() as u32);
257        r.result().maxcnt().write(|w| w.set_maxcnt((N * CNT_UNIT) as _));
258
259        // Reset and enable the end event
260        r.events_end().write_value(0);
261        r.intenset().write(|w| w.set_end(true));
262
263        // Don't reorder the ADC start event before the previous writes. Hopefully self
264        // wouldn't happen anyway.
265        compiler_fence(Ordering::SeqCst);
266
267        r.tasks_start().write_value(1);
268        r.tasks_sample().write_value(1);
269
270        // Wait for 'end' event.
271        poll_fn(|cx| {
272            let r = Self::regs();
273
274            WAKER.register(cx.waker());
275
276            if r.events_end().read() != 0 {
277                r.events_end().write_value(0);
278                return Poll::Ready(());
279            }
280
281            Poll::Pending
282        })
283        .await;
284
285        drop(on_drop);
286    }
287
288    /// Continuous sampling with double buffers.
289    ///
290    /// A TIMER and two PPI peripherals are passed in so that precise sampling
291    /// can be attained. The sampling interval is expressed by selecting a
292    /// timer clock frequency to use along with a counter threshold to be reached.
293    /// For example, 1KHz can be achieved using a frequency of 1MHz and a counter
294    /// threshold of 1000.
295    ///
296    /// A sampler closure is provided that receives the buffer of samples, noting
297    /// that the size of this buffer can be less than the original buffer's size.
298    /// A command is return from the closure that indicates whether the sampling
299    /// should continue or stop.
300    ///
301    /// NOTE: The time spent within the callback supplied should not exceed the time
302    /// taken to acquire the samples into a single buffer. You should measure the
303    /// time taken by the callback and set the sample buffer size accordingly.
304    /// Exceeding this time can lead to samples becoming dropped.
305    ///
306    /// The sampling is stopped prior to returning in order to reduce power consumption (power
307    /// consumption remains higher if sampling is not stopped explicitly), and to
308    /// free the buffers from being used by the peripheral. Cancellation will
309    /// also cause the sampling to be stopped.
310
311    pub async fn run_task_sampler<F, T: TimerInstance, const N0: usize>(
312        &mut self,
313        timer: Peri<'_, T>,
314        ppi_ch1: Peri<'_, impl ConfigurableChannel>,
315        ppi_ch2: Peri<'_, impl ConfigurableChannel>,
316        frequency: Frequency,
317        sample_counter: u32,
318        bufs: &mut [[[i16; N]; N0]; 2],
319        callback: F,
320    ) where
321        F: FnMut(&[[i16; N]]) -> CallbackResult,
322    {
323        let r = Self::regs();
324
325        // We want the task start to effectively short with the last one ending so
326        // we don't miss any samples. It'd be great for the SAADC to offer a SHORTS
327        // register instead, but it doesn't, so we must use PPI.
328        let mut start_ppi = Ppi::new_one_to_one(
329            ppi_ch1,
330            Event::from_reg(r.events_end()),
331            Task::from_reg(r.tasks_start()),
332        );
333        start_ppi.enable();
334
335        let timer = Timer::new(timer);
336        timer.set_frequency(frequency);
337        timer.cc(0).write(sample_counter);
338        timer.cc(0).short_compare_clear();
339
340        let timer_cc = timer.cc(0);
341
342        let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(r.tasks_sample()));
343
344        timer.start();
345
346        self.run_sampler(
347            bufs,
348            None,
349            || {
350                sample_ppi.enable();
351            },
352            callback,
353        )
354        .await;
355    }
356
357    async fn run_sampler<I, F, const N0: usize>(
358        &mut self,
359        bufs: &mut [[[i16; N]; N0]; 2],
360        sample_rate_divisor: Option<u16>,
361        mut init: I,
362        mut callback: F,
363    ) where
364        I: FnMut(),
365        F: FnMut(&[[i16; N]]) -> CallbackResult,
366    {
367        // In case the future is dropped, stop the task and wait for it to end.
368        let on_drop = OnDrop::new(Self::stop_sampling_immediately);
369
370        let r = Self::regs();
371
372        // Establish mode and sample rate
373        match sample_rate_divisor {
374            Some(sr) => {
375                r.samplerate().write(|w| {
376                    w.set_cc(sr);
377                    w.set_mode(vals::SamplerateMode::TIMERS);
378                });
379                r.tasks_sample().write_value(1); // Need to kick-start the internal timer
380            }
381            None => r.samplerate().write(|w| {
382                w.set_cc(0);
383                w.set_mode(vals::SamplerateMode::TASK);
384            }),
385        }
386
387        // Set up the initial DMA
388        r.result().ptr().write_value(bufs[0].as_mut_ptr() as u32);
389        r.result().maxcnt().write(|w| w.set_maxcnt((N0 * N * CNT_UNIT) as _));
390
391        // Reset and enable the events
392        r.events_end().write_value(0);
393        r.events_started().write_value(0);
394        r.intenset().write(|w| {
395            w.set_end(true);
396            w.set_started(true);
397        });
398
399        // Don't reorder the ADC start event before the previous writes. Hopefully self
400        // wouldn't happen anyway.
401        compiler_fence(Ordering::SeqCst);
402
403        r.tasks_start().write_value(1);
404
405        let mut inited = false;
406        let mut current_buffer = 0;
407
408        // Wait for events and complete when the sampler indicates it has had enough.
409        let r = poll_fn(|cx| {
410            let r = Self::regs();
411
412            WAKER.register(cx.waker());
413
414            if r.events_end().read() != 0 {
415                compiler_fence(Ordering::SeqCst);
416
417                r.events_end().write_value(0);
418                r.intenset().write(|w| w.set_end(true));
419
420                match callback(&bufs[current_buffer]) {
421                    CallbackResult::Continue => {
422                        let next_buffer = 1 - current_buffer;
423                        current_buffer = next_buffer;
424                    }
425                    CallbackResult::Stop => {
426                        return Poll::Ready(());
427                    }
428                }
429            }
430
431            if r.events_started().read() != 0 {
432                r.events_started().write_value(0);
433                r.intenset().write(|w| w.set_started(true));
434
435                if !inited {
436                    init();
437                    inited = true;
438                }
439
440                let next_buffer = 1 - current_buffer;
441                r.result().ptr().write_value(bufs[next_buffer].as_mut_ptr() as u32);
442            }
443
444            Poll::Pending
445        })
446        .await;
447
448        drop(on_drop);
449
450        r
451    }
452
453    // Stop sampling and wait for it to stop in a blocking fashion
454    fn stop_sampling_immediately() {
455        let r = Self::regs();
456
457        compiler_fence(Ordering::SeqCst);
458
459        r.events_stopped().write_value(0);
460        r.tasks_stop().write_value(1);
461
462        while r.events_stopped().read() == 0 {}
463        r.events_stopped().write_value(0);
464    }
465}
466
467impl<'d> Saadc<'d, 1> {
468    /// Continuous sampling on a single channel with double buffers.
469    ///
470    /// The internal clock is to be used with a sample rate expressed as a divisor of
471    /// 16MHz, ranging from 80..2047. For example, 1600 represents a sample rate of 10KHz
472    /// given 16_000_000 / 10_000_000 = 1600.
473    ///
474    /// A sampler closure is provided that receives the buffer of samples, noting
475    /// that the size of this buffer can be less than the original buffer's size.
476    /// A command is return from the closure that indicates whether the sampling
477    /// should continue or stop.
478    pub async fn run_timer_sampler<I, S, const N0: usize>(
479        &mut self,
480        bufs: &mut [[[i16; 1]; N0]; 2],
481        sample_rate_divisor: u16,
482        sampler: S,
483    ) where
484        S: FnMut(&[[i16; 1]]) -> CallbackResult,
485    {
486        self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await;
487    }
488}
489
490impl<'d, const N: usize> Drop for Saadc<'d, N> {
491    fn drop(&mut self) {
492        // Reset of SAADC.
493        //
494        // This is needed when more than one pin is sampled to avoid needless power consumption.
495        // More information can be found in [nrf52 Anomaly 241](https://docs.nordicsemi.com/bundle/errata_nRF52810_Rev1/page/ERR/nRF52810/Rev1/latest/anomaly_810_241.html).
496        // The workaround seems like it copies the configuration before reset and reapplies it after.
497        // The instance is dropped, forcing a reconfiguration at compile time, hence we only
498        // call what is the reset portion of the workaround.
499        #[cfg(feature = "_nrf52")]
500        {
501            unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 0) }
502            unsafe { core::ptr::read_volatile(0x40007FFC as *const ()) }
503            unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 1) }
504        }
505        let r = Self::regs();
506        r.enable().write(|w| w.set_enable(false));
507        for i in 0..N {
508            #[cfg(not(feature = "_nrf54l"))]
509            {
510                r.ch(i).pselp().write(|w| w.set_pselp(InputChannel::NC));
511                r.ch(i).pseln().write(|w| w.set_pseln(InputChannel::NC));
512            }
513            #[cfg(feature = "_nrf54l")]
514            {
515                r.ch(i).pselp().write(|w| w.set_connect(vals::PselpConnect::NC));
516                r.ch(i).pseln().write(|w| w.set_connect(vals::PselnConnect::NC));
517            }
518        }
519    }
520}
521
522#[cfg(not(feature = "_nrf54l"))]
523impl From<Gain> for vals::Gain {
524    fn from(gain: Gain) -> Self {
525        match gain {
526            Gain::GAIN1_6 => vals::Gain::GAIN1_6,
527            Gain::GAIN1_5 => vals::Gain::GAIN1_5,
528            Gain::GAIN1_4 => vals::Gain::GAIN1_4,
529            Gain::GAIN1_3 => vals::Gain::GAIN1_3,
530            Gain::GAIN1_2 => vals::Gain::GAIN1_2,
531            Gain::GAIN1 => vals::Gain::GAIN1,
532            Gain::GAIN2 => vals::Gain::GAIN2,
533            Gain::GAIN4 => vals::Gain::GAIN4,
534        }
535    }
536}
537
538#[cfg(feature = "_nrf54l")]
539impl From<Gain> for vals::Gain {
540    fn from(gain: Gain) -> Self {
541        match gain {
542            Gain::GAIN2_8 => vals::Gain::GAIN2_8,
543            Gain::GAIN2_7 => vals::Gain::GAIN2_7,
544            Gain::GAIN2_6 => vals::Gain::GAIN2_6,
545            Gain::GAIN2_5 => vals::Gain::GAIN2_5,
546            Gain::GAIN2_4 => vals::Gain::GAIN2_4,
547            Gain::GAIN2_3 => vals::Gain::GAIN2_3,
548            Gain::GAIN1 => vals::Gain::GAIN1,
549            Gain::GAIN2 => vals::Gain::GAIN2,
550        }
551    }
552}
553
554/// Gain control
555#[cfg(not(feature = "_nrf54l"))]
556#[non_exhaustive]
557#[derive(Clone, Copy)]
558pub enum Gain {
559    /// 1/6
560    GAIN1_6 = 0,
561    /// 1/5
562    GAIN1_5 = 1,
563    /// 1/4
564    GAIN1_4 = 2,
565    /// 1/3
566    GAIN1_3 = 3,
567    /// 1/2
568    GAIN1_2 = 4,
569    /// 1
570    GAIN1 = 5,
571    /// 2
572    GAIN2 = 6,
573    /// 4
574    GAIN4 = 7,
575}
576
577/// Gain control
578#[cfg(feature = "_nrf54l")]
579#[non_exhaustive]
580#[derive(Clone, Copy)]
581pub enum Gain {
582    /// 2/8
583    GAIN2_8 = 0,
584    /// 2/7
585    GAIN2_7 = 1,
586    /// 2/6
587    GAIN2_6 = 2,
588    /// 2/5
589    GAIN2_5 = 3,
590    /// 2/4
591    GAIN2_4 = 4,
592    /// 2/3
593    GAIN2_3 = 5,
594    /// 1
595    GAIN1 = 6,
596    /// 2
597    GAIN2 = 7,
598}
599
600impl From<Reference> for vals::Refsel {
601    fn from(reference: Reference) -> Self {
602        match reference {
603            Reference::INTERNAL => vals::Refsel::INTERNAL,
604            #[cfg(not(feature = "_nrf54l"))]
605            Reference::VDD1_4 => vals::Refsel::VDD1_4,
606            #[cfg(feature = "_nrf54l")]
607            Reference::EXTERNAL => vals::Refsel::EXTERNAL,
608        }
609    }
610}
611
612/// Reference control
613#[non_exhaustive]
614#[derive(Clone, Copy)]
615pub enum Reference {
616    /// Internal reference (0.6 V)
617    INTERNAL = 0,
618    #[cfg(not(feature = "_nrf54l"))]
619    /// VDD/4 as reference
620    VDD1_4 = 1,
621    /// PADC_EXT_REF_1V2 as reference
622    #[cfg(feature = "_nrf54l")]
623    EXTERNAL = 1,
624}
625
626#[cfg(not(feature = "_nrf54l"))]
627impl From<Resistor> for vals::Resp {
628    fn from(resistor: Resistor) -> Self {
629        match resistor {
630            Resistor::BYPASS => vals::Resp::BYPASS,
631            Resistor::PULLDOWN => vals::Resp::PULLDOWN,
632            Resistor::PULLUP => vals::Resp::PULLUP,
633            Resistor::VDD1_2 => vals::Resp::VDD1_2,
634        }
635    }
636}
637
638/// Positive channel resistor control
639#[non_exhaustive]
640#[derive(Clone, Copy)]
641#[cfg(not(feature = "_nrf54l"))]
642pub enum Resistor {
643    /// Bypass resistor ladder
644    BYPASS = 0,
645    /// Pull-down to GND
646    PULLDOWN = 1,
647    /// Pull-up to VDD
648    PULLUP = 2,
649    /// Set input at VDD/2
650    VDD1_2 = 3,
651}
652
653#[cfg(not(feature = "_nrf54l"))]
654impl From<Time> for vals::Tacq {
655    fn from(time: Time) -> Self {
656        match time {
657            Time::_3US => vals::Tacq::_3US,
658            Time::_5US => vals::Tacq::_5US,
659            Time::_10US => vals::Tacq::_10US,
660            Time::_15US => vals::Tacq::_15US,
661            Time::_20US => vals::Tacq::_20US,
662            Time::_40US => vals::Tacq::_40US,
663        }
664    }
665}
666
667#[cfg(feature = "_nrf54l")]
668impl From<Time> for u16 {
669    fn from(time: Time) -> Self {
670        match time {
671            Time::_3US => (3000 / 125) - 1,
672            Time::_5US => (5000 / 125) - 1,
673            Time::_10US => (10000 / 125) - 1,
674            Time::_15US => (15000 / 125) - 1,
675            Time::_20US => (20000 / 125) - 1,
676            Time::_40US => (40000 / 125) - 1,
677        }
678    }
679}
680
681/// Acquisition time, the time the SAADC uses to sample the input voltage
682#[non_exhaustive]
683#[derive(Clone, Copy)]
684pub enum Time {
685    /// 3 us
686    _3US = 0,
687    ///  5 us
688    _5US = 1,
689    /// 10 us
690    _10US = 2,
691    /// 15 us
692    _15US = 3,
693    /// 20 us
694    _20US = 4,
695    /// 40 us
696    _40US = 5,
697}
698
699impl From<Oversample> for vals::Oversample {
700    fn from(oversample: Oversample) -> Self {
701        match oversample {
702            Oversample::BYPASS => vals::Oversample::BYPASS,
703            Oversample::OVER2X => vals::Oversample::OVER2X,
704            Oversample::OVER4X => vals::Oversample::OVER4X,
705            Oversample::OVER8X => vals::Oversample::OVER8X,
706            Oversample::OVER16X => vals::Oversample::OVER16X,
707            Oversample::OVER32X => vals::Oversample::OVER32X,
708            Oversample::OVER64X => vals::Oversample::OVER64X,
709            Oversample::OVER128X => vals::Oversample::OVER128X,
710            Oversample::OVER256X => vals::Oversample::OVER256X,
711        }
712    }
713}
714
715/// Oversample control
716#[non_exhaustive]
717#[derive(Clone, Copy)]
718pub enum Oversample {
719    /// Bypass oversampling
720    BYPASS = 0,
721    /// Oversample 2x
722    OVER2X = 1,
723    /// Oversample 4x
724    OVER4X = 2,
725    /// Oversample 8x
726    OVER8X = 3,
727    /// Oversample 16x
728    OVER16X = 4,
729    /// Oversample 32x
730    OVER32X = 5,
731    /// Oversample 64x
732    OVER64X = 6,
733    /// Oversample 128x
734    OVER128X = 7,
735    /// Oversample 256x
736    OVER256X = 8,
737}
738
739impl From<Resolution> for vals::Val {
740    fn from(resolution: Resolution) -> Self {
741        match resolution {
742            Resolution::_8BIT => vals::Val::_8BIT,
743            Resolution::_10BIT => vals::Val::_10BIT,
744            Resolution::_12BIT => vals::Val::_12BIT,
745            Resolution::_14BIT => vals::Val::_14BIT,
746        }
747    }
748}
749
750/// Set the resolution
751#[non_exhaustive]
752#[derive(Clone, Copy)]
753pub enum Resolution {
754    /// 8 bits
755    _8BIT = 0,
756    /// 10 bits
757    _10BIT = 1,
758    /// 12 bits
759    _12BIT = 2,
760    /// 14 bits
761    _14BIT = 3,
762}
763
764pub(crate) trait SealedInput {
765    #[cfg(not(feature = "_nrf54l"))]
766    fn channel(&self) -> InputChannel;
767
768    #[cfg(feature = "_nrf54l")]
769    fn pin(&self) -> u8;
770
771    #[cfg(feature = "_nrf54l")]
772    fn port(&self) -> u8;
773
774    #[cfg(feature = "_nrf54l")]
775    fn internal(&self) -> vals::PselpInternal;
776
777    #[cfg(feature = "_nrf54l")]
778    fn connect(&self) -> vals::PselpConnect;
779}
780
781/// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal.
782#[allow(private_bounds)]
783pub trait Input: SealedInput + Sized {
784    /// Convert this SAADC input to a type-erased `AnyInput`.
785    ///
786    /// This allows using several inputs  in situations that might require
787    /// them to be the same type, like putting them in an array.
788    #[cfg(not(feature = "_nrf54l"))]
789    fn degrade_saadc<'a>(self) -> AnyInput<'a>
790    where
791        Self: 'a,
792    {
793        AnyInput {
794            channel: self.channel(),
795            _phantom: core::marker::PhantomData,
796        }
797    }
798
799    /// Convert this SAADC input to a type-erased `AnyInput`.
800    ///
801    /// This allows using several inputs  in situations that might require
802    /// them to be the same type, like putting them in an array.
803    #[cfg(feature = "_nrf54l")]
804    fn degrade_saadc<'a>(self) -> AnyInput<'a>
805    where
806        Self: 'a,
807    {
808        AnyInput {
809            pin: self.pin(),
810            port: self.port(),
811            internal: self.internal(),
812            connect: self.connect(),
813            _phantom: core::marker::PhantomData,
814        }
815    }
816}
817
818/// A type-erased SAADC input.
819///
820/// This allows using several inputs  in situations that might require
821/// them to be the same type, like putting them in an array.
822#[cfg(not(feature = "_nrf54l"))]
823pub struct AnyInput<'a> {
824    channel: InputChannel,
825    _phantom: PhantomData<&'a ()>,
826}
827
828/// A type-erased SAADC input.
829///
830/// This allows using several inputs  in situations that might require
831/// them to be the same type, like putting them in an array.
832#[cfg(feature = "_nrf54l")]
833pub struct AnyInput<'a> {
834    pin: u8,
835    port: u8,
836    internal: vals::PselpInternal,
837    connect: vals::PselpConnect,
838    _phantom: PhantomData<&'a ()>,
839}
840
841impl<'a> AnyInput<'a> {
842    /// Reborrow into a "child" AnyInput.
843    ///
844    /// `self` will stay borrowed until the child AnyInput is dropped.
845    pub fn reborrow(&mut self) -> AnyInput<'_> {
846        // safety: we're returning the clone inside a new Peripheral that borrows
847        // self, so user code can't use both at the same time.
848        #[cfg(not(feature = "_nrf54l"))]
849        {
850            Self {
851                channel: self.channel,
852                _phantom: PhantomData,
853            }
854        }
855        #[cfg(feature = "_nrf54l")]
856        {
857            Self {
858                pin: self.pin,
859                port: self.port,
860                internal: self.internal,
861                connect: self.connect,
862                _phantom: PhantomData,
863            }
864        }
865    }
866}
867
868impl SealedInput for AnyInput<'_> {
869    #[cfg(not(feature = "_nrf54l"))]
870    fn channel(&self) -> InputChannel {
871        self.channel
872    }
873
874    #[cfg(feature = "_nrf54l")]
875    fn pin(&self) -> u8 {
876        self.pin
877    }
878
879    #[cfg(feature = "_nrf54l")]
880    fn port(&self) -> u8 {
881        self.port
882    }
883
884    #[cfg(feature = "_nrf54l")]
885    fn internal(&self) -> vals::PselpInternal {
886        self.internal
887    }
888
889    #[cfg(feature = "_nrf54l")]
890    fn connect(&self) -> vals::PselpConnect {
891        self.connect
892    }
893}
894
895impl Input for AnyInput<'_> {}
896
897#[cfg(not(feature = "_nrf54l"))]
898macro_rules! impl_saadc_input {
899    ($pin:ident, $ch:ident) => {
900        impl_saadc_input!(@local, crate::Peri<'_, crate::peripherals::$pin>, $ch);
901    };
902    (@local, $pin:ty, $ch:ident) => {
903        impl crate::saadc::SealedInput for $pin {
904            fn channel(&self) -> crate::saadc::InputChannel {
905                crate::saadc::InputChannel::$ch
906            }
907        }
908        impl crate::saadc::Input for $pin {}
909    };
910}
911
912#[cfg(feature = "_nrf54l")]
913macro_rules! impl_saadc_input {
914    ($pin:ident, $port:expr, $ain:expr) => {
915        impl_saadc_input!(@local, crate::Peri<'_, crate::peripherals::$pin>, $port, $ain, AVDD, ANALOG_INPUT);
916    };
917    (@local, $pin:ty, $port:expr, $ain:expr, $internal:ident, $connect:ident) => {
918        impl crate::saadc::SealedInput for $pin {
919            fn pin(&self) -> u8 {
920                $ain
921            }
922
923            fn port(&self) -> u8 {
924                $port
925            }
926
927            fn internal(&self) -> crate::pac::saadc::vals::PselpInternal {
928                crate::pac::saadc::vals::PselpInternal::$internal
929            }
930
931            fn connect(&self) -> crate::pac::saadc::vals::PselpConnect {
932                crate::pac::saadc::vals::PselpConnect::$connect
933            }
934        }
935        impl crate::saadc::Input for $pin {}
936    };
937}
938
939/// A dummy `Input` pin implementation for SAADC peripheral sampling from the
940/// internal voltage.
941pub struct VddInput;
942
943impl_peripheral!(VddInput);
944#[cfg(not(feature = "_nrf54l"))]
945#[cfg(not(feature = "_nrf91"))]
946impl_saadc_input!(@local, VddInput, VDD);
947#[cfg(feature = "_nrf91")]
948impl_saadc_input!(@local, VddInput, VDD_GPIO);
949#[cfg(feature = "_nrf54l")]
950impl_saadc_input!(@local, VddInput, 0, 0, VDD, INTERNAL);
951
952/// A dummy `Input` pin implementation for SAADC peripheral sampling from the
953/// VDDH / 5 voltage.
954#[cfg(any(feature = "_nrf5340-app", feature = "nrf52833", feature = "nrf52840"))]
955pub struct VddhDiv5Input;
956
957#[cfg(any(feature = "_nrf5340-app", feature = "nrf52833", feature = "nrf52840"))]
958impl_peripheral!(VddhDiv5Input);
959
960#[cfg(any(feature = "_nrf5340-app", feature = "nrf52833", feature = "nrf52840"))]
961impl_saadc_input!(@local, VddhDiv5Input, VDDHDIV5);
962
963/// A dummy `Input` pin implementation for SAADC peripheral sampling from the
964/// AVDD internal voltage of the nrf54l chip family.
965#[cfg(feature = "_nrf54l")]
966pub struct AVddInput;
967#[cfg(feature = "_nrf54l")]
968embassy_hal_internal::impl_peripheral!(AVddInput);
969#[cfg(feature = "_nrf54l")]
970impl_saadc_input!(@local, AVddInput, 0, 0, AVDD, INTERNAL);
971
972/// A dummy `Input` pin implementation for SAADC peripheral sampling from the
973/// DVDD internal voltage of the nrf54l chip family.
974#[cfg(feature = "_nrf54l")]
975pub struct DVddInput;
976#[cfg(feature = "_nrf54l")]
977embassy_hal_internal::impl_peripheral!(DVddInput);
978#[cfg(feature = "_nrf54l")]
979impl_saadc_input!(@local, DVddInput, 0, 0, DVDD, INTERNAL);