kea_hal/
adc.rs

1//! The ADC Interface
2//!
3//! The ADC is disabled at startup and must be enabled (by calling
4//! [Adc<Disabled>::enable]) before any of its registers can be accessed
5//! (read or write). Attempts to access these registers will trigger a hardware
6//! generated HardFault, which by default resets the microcontroller.
7//!
8//! The ADC can be polled for conversion completion with [Adc::is_done].
9//! Completion will trigger an ADC Interrupt if enabled. See
10//! [Adc::into_interrupt]
11//!
12//! ## Input Modes
13//!
14//! The Adc peripheral can operate in either single input or FIFO modes. Single
15//! input mode is the mode most commonly thought of when using an ADC. A
16//! multiplexer (via Adc::set_channel) is used to connect a single channel to
17//! the ADC, and when the conversion is complete the hardware makes the results
18//! available in the results register. The software must call
19//! [Adc::set_channel] again to either select a new channel or to restart the
20//! conversion on the same channel.
21//!
22//! The FIFO mode sets up a hardware buffer of selectable depth (2-8 channels).
23//! Once the buffer is filled the Adc peripheral shoves the buffer contents
24//! into the multiplexer channel by channel. Likewise, as each conversion is
25//! completed the results are buffered into the result register in the same
26//! order as the channel select buffer.
27//!
28//! Note: FIFO mode is not yet implemented in this HAL
29//!
30//! ## Conversion Modes
31//!
32//! The Adc peripheral offers 2 conversion modes, OneShot and Continuous. In
33//! OneShot mode, the conversion is started when the channel is selected (or
34//! when the channel select buffer is filled in FIFO mode). After completion no
35//! new conversion is started until the channel is set again, even if the same
36//! channel is used.
37//!
38//! In Continuous mode a new conversion is started immediately
39//! after the previous one is completed. Changing the channel interrupts the
40//! conversion and immediately begins conversion on the new channel (unless the
41//! new channel is [DummyDisable], then the conversion is allowed to complete,
42//! but no new conversion is started). In FIFO mode the input FIFO is reloaded
43//! after completion, in other words the same N values are converted on a loop.
44//!
45//! Note: Continuous mode is not yet implemented in this HAL
46//!
47//! ## Comparison Mode
48//!
49//! Note: Comparison mode is not yet implemented in this HAL
50//!
51//! Comparison mode is a hardware feature of the Adc Peripheral. If set, the
52//! conversion result is compared to the comparison value. If the result
53//! is greater than or less than (depending on configuration) the comparison
54//! value the result is moved into the result register. Otherwise, the result
55//! is discarded \[Note: Unsure if the conversion is restarted in OneShot
56//! mode\].
57//!
58//! A common use case for comparison mode is to enter a low power state with
59//! the Adc configured to use the asynchronous clock source and to generate an
60//! interrupt on completion. When the input channel crosses the comparison
61//! threshold the interrupt is triggered, waking the MCU.
62//!
63//! ## Clocking
64//!
65//! The ADC requires a clock signal (ADCK), which is generated from the bus
66//! clock, the bus clock divided by 2, the output of the OSC peripheral
67//! (OSC_OUT), or an internal asynchronous clock, which, when selected,
68//! operates in wait and stop modes. With any of these clock sources a
69//! multi-value divider is provided to further divide the incoming clock by 1
70//! (i.e. 1:1), 2, 4, or 8.
71//!
72//! The clock frequency must fall within 400kHz to 8MHz (4MHz in low power
73//! mode), This is the same for all KEA MCUs. Ideally, the HAL will only
74//! present valid options, but that is not yet implemented (pending clocks
75//! improvements to output frequencies). For now you are trusted to input the
76//! correct frequency.
77//!
78//! *Note:* When using the FIFO mode with FIFO scan mode disabled, the bus
79//! clock must be faster than half the ADC clock (ADCK). Bus clock >= ADCK / 2.
80//!
81//! ## Pin Control
82//!
83//! This functionality is implemented in the GPIO module. See [Analog]
84//! for details.
85//!
86//! ## Conversion Width
87//!
88//! The ADC can be run in 8, 10, or 12 bit modes. These modes are enumerated in
89//! [AdcResolution].
90//!
91//! ## Hardware Trigger
92//!
93//! The ADC conversions can be started by a hardware trigger. This is not
94//! implemented in all KEA chips, so implementation here will be Delayed. Use
95//! the PAC. Enable is ADC_SC2\[ADTRG\] = 1, and trigger is the ADHWT source.
96//!
97//! ## Usage
98//!
99//! ### AdcConfig struct
100//!
101//! [AdcConfig] offers public fields to allow for creation in-place. The
102//! [AdcConfig::calculate_divisor] method allows the user to specify the
103//! desired Adc Clock frequency (given the clock source frequency). The clock
104//! divider which gets the closest to that frequency is chosen.
105//!
106//! The AdcConfig structure also implements the [Default] trait.
107//!
108//! ```rust
109//! let config: AdcConfig = Default::default();
110//!
111//! config.calculate_divisor(20_u32.MHz(), 2_u32.MHz());
112//! assert!(matches!(config.clock_divisor, ClockDivisor::_8));
113//! ```
114
115use crate::hal::adc::{Channel, OneShot};
116use crate::{pac::ADC, HALExt};
117use core::{convert::Infallible, marker::PhantomData};
118use embedded_time::rate::*;
119
120/// Error Enumeration for this module
121#[derive(Debug)]
122pub enum Error {
123    /// The Channel has already been moved
124    Moved,
125}
126
127/// Analog type state for a GPIO pin.
128///
129/// This mode "gives" the pin to the ADC hardware peripheral.
130/// The ADC Peripheral can take the GPIO pins in any state. The Peripheral will
131/// reconfigure the pin to turn off any output drivers, disable input buffers
132/// (reading the pin after configuring as analog will return a zero), and
133/// disable the pullup. Electrically, an Analog pin that is not currently under
134/// conversion is effectively HighImpedence.
135///
136/// Once a pin is released from the ADC, it will return to its previous state.
137/// The previous state includes output enabled, input enabled, pullup enabled,
138/// and level (for outputs). Note to accomplish this the pin implements the
139/// outof_analog method, which is semantically different from the other type
140/// states.
141///
142/// For example, [crate::gpio::gpioa::PTA0] is configured to be a Output that is set high is
143/// converted into the analog mode with the [crate::gpio::gpioa::PTA0::into_analog] method.
144/// Once measurements from that pin are completed it will be returned to an
145/// Output that is set high by calling the [Analog::outof_analog] method.
146///
147/// ```rust
148/// let pta0 = gpioa.pta0.into_push_pull_output();
149/// pta0.set_high();
150/// let mut pta0 = pta0.into_analog(); // pta0 is hi-Z
151/// let value = adc.read(&mut pta0).unwrap_or(0);
152/// let pta0 = pta0.outof_analog();  // pta0 is push-pull output, set high.
153/// ```
154///
155/// Note: This is a hardware feature that requires effectively no clock cycles
156/// to complete. "Manually" reconfiguring the pins to HighImpedence before
157/// calling into_analog() is discouraged, but it would not hurt anything.
158pub struct Analog<Pin> {
159    pin: Pin,
160}
161
162/// Interface for ADC Peripheral.
163///
164/// Returned by calling [HALExt::split] on the pac [ADC] structure. Holds state
165/// of peripheral.
166pub struct Adc<State> {
167    peripheral: ADC,
168    _state: PhantomData<State>,
169    /// Contains the On-Chip ADC Channels, like the MCU's temperature sensor.
170    pub onchip_channels: OnChipChannels,
171}
172
173impl HALExt for ADC {
174    type T = Adc<Disabled>;
175    fn split(self) -> Adc<Disabled> {
176        Adc {
177            peripheral: self,
178            _state: PhantomData,
179            onchip_channels: OnChipChannels {
180                vss: Some(Analog {
181                    pin: Vss::<Input> { _mode: PhantomData },
182                }),
183                temp_sense: Some(Analog {
184                    pin: TempSense::<Input> { _mode: PhantomData },
185                }),
186                bandgap: Some(Analog {
187                    pin: Bandgap::<Input> { _mode: PhantomData },
188                }),
189                vref_h: Some(Analog {
190                    pin: VrefH::<Input> { _mode: PhantomData },
191                }),
192                vref_l: Some(Analog {
193                    pin: VrefL::<Input> { _mode: PhantomData },
194                }),
195            },
196        }
197    }
198}
199
200/// Configuration struct for Adc peripheral.
201pub struct AdcConfig {
202    /// Determines the clock source for the ADC peripheral
203    ///
204    /// Default is [AdcClocks::Bus]
205    pub clock_source: AdcClocks,
206    /// Divides the clock source to get the ADC clock into it's usable range of
207    /// 400kHz - 8MHz (4MHz in low power mode).
208    ///
209    /// Default is [ClockDivisor::_1] (no divison)
210    pub clock_divisor: ClockDivisor,
211    /// Set the resolution of ADC conversion
212    ///
213    /// Default is [AdcResolution::_8bit]
214    pub resolution: AdcResolution,
215
216    /// Set ADC sample time.
217    ///
218    /// Default is [AdcSampleTime::Short]
219    pub sample_time: AdcSampleTime,
220
221    /// Set low power mode
222    ///
223    /// Default is false.
224    pub low_power: bool,
225}
226
227impl AdcConfig {
228    /// Calculate the ADC clock divisor
229    ///
230    /// Uses the current clock source and clock frequency to determine
231    /// the best divisor to use in order to have minimal error between
232    /// the ADC clock rate and the desired ADC clock rate.
233    ///
234    /// Note: This relies on trustworthy values for source_freq and valid
235    /// values for req_adc_freq. In the future this should know or
236    /// determine what the current clock frequency is instead of relying
237    /// on the user to provide it.
238    pub fn calculate_divisor(&mut self, source_freq: Hertz, req_adc_freq: Hertz) {
239        let denom: u8 = (source_freq.integer() / req_adc_freq.integer()) as u8;
240
241        let mut output: u8 = 1;
242        let mut err: i8 = (denom - output) as i8;
243        let mut err_old: i8 = err;
244        let max_divisor = match self.clock_source {
245            AdcClocks::Bus => 16,
246            _ => 8,
247        };
248        while output < max_divisor {
249            err = (denom - (output << 1)) as i8;
250            if err.is_negative() {
251                err = err.abs();
252            }
253            if err <= err_old {
254                output <<= 1;
255                err_old = err;
256            } else {
257                break;
258            }
259        }
260
261        // I am of the mind that this assert is okay, at least until the input
262        // clock can be known at compile time.
263        let ad_clock = source_freq.integer() / output as u32;
264        assert!(400_000 <= ad_clock);
265        assert!(
266            ad_clock
267                <= match self.low_power {
268                    false => 8_000_000,
269                    true => 4_000_000,
270                }
271        );
272
273        self.clock_divisor = match output {
274            1 => ClockDivisor::_1,
275            2 => ClockDivisor::_2,
276            4 => ClockDivisor::_4,
277            8 => ClockDivisor::_8,
278            _ => ClockDivisor::_16,
279        }
280    }
281
282    /// Set the divisor directly. panics if divisor isn't supported by the
283    /// clock source.
284    ///
285    /// TODO: Refactor to remove assert. Add Clock Source as a type state
286    pub fn set_divisor(&mut self, divisor: ClockDivisor) {
287        // divisor can't be 16 unless using the Bus clock
288        assert!(
289            !(!matches!(self.clock_source, AdcClocks::Bus) && matches!(divisor, ClockDivisor::_16))
290        );
291        self.clock_divisor = divisor;
292    }
293
294    /// Sets the clock source, panics if divisor isn't supported
295    ///
296    /// TODO: Refactor to remove assert. Add Clock Source as a type state
297    pub fn set_clock_source(&mut self, clock: AdcClocks) {
298        // Panic if setting the clock to anything other than Bus if the divisor
299        // is set to 16
300        assert!(
301            !matches!(clock, AdcClocks::Bus) && matches!(self.clock_divisor, ClockDivisor::_16)
302        );
303        self.clock_source = clock;
304    }
305}
306
307impl Default for AdcConfig {
308    fn default() -> AdcConfig {
309        AdcConfig {
310            clock_source: AdcClocks::Bus,
311            clock_divisor: ClockDivisor::_1,
312            resolution: AdcResolution::_12bit,
313            sample_time: AdcSampleTime::Short,
314            low_power: false,
315        }
316    }
317}
318
319/// Clock types available to the Adc peripheral
320///
321/// Dividers will be chosen appropriately to suit requested clock rate.
322pub enum AdcClocks {
323    /// Use the incoming Bus Clock
324    Bus,
325    /// jkl
326    External,
327    /// Available in Wait AND Stop Mode
328    Async,
329}
330
331/// This enum represents the availabe ADC resolutions
332///
333/// Regardless of resolution chosen, results are always right justified
334#[repr(u8)]
335pub enum AdcResolution {
336    /// 8 bit AD conversion mode
337    _8bit = 0,
338    /// 10 bit AD conversion mode
339    _10bit = 1,
340    /// 12 bit AD conversion mode
341    _12bit = 2,
342}
343
344/// Adc sample time
345pub enum AdcSampleTime {
346    /// Sample for 3.5 ADC clock (ADCK) cycles.
347    Short = 0,
348
349    /// Sample for 23.5 ADC clock (ADCK) cycles.
350    ///
351    /// Required for high impedence (>2k @ADCK > 4MHz, >5k @ ADCK < 4MHz)
352    /// inputs.
353    Long = 1,
354}
355
356/// Adc Clock Divisors
357///
358/// Note 1/16 divisor is only usable for the Bus clock
359pub enum ClockDivisor {
360    /// Source / 1, No divison
361    _1 = 0,
362    /// Source / 2
363    _2 = 1,
364    /// Source / 4
365    _4 = 2,
366    /// Source / 8
367    _8 = 3,
368    /// Source / 16
369    _16 = 4,
370}
371
372/// Enabled state
373pub struct Enabled;
374
375/// Disabled state
376pub struct Disabled;
377
378impl Adc<Enabled> {
379    /// Poll to determine if ADC conversion is complete.
380    ///
381    /// Note: This flag is cleared when the sampling mode is changed,
382    /// interrupts are enabled, [Adc::set_channel] is called, and when [Adc::result] is
383    /// called (including [Adc::try_result])
384    pub fn is_done(&self) -> bool {
385        self.peripheral.sc1.read().coco().bit()
386    }
387
388    /// Poll to determine if ADC conversion is underway
389    pub fn is_converting(&self) -> bool {
390        self.peripheral.sc2.read().adact().bit()
391    }
392
393    /// Grab the last ADC conversion result.
394    pub fn result(&self) -> u16 {
395        self.peripheral.r.read().adr().bits()
396    }
397
398    /// Poll for conversion completion, if done return the result.
399    pub fn try_result(&self) -> Option<u16> {
400        if self.is_done() {
401            Some(self.result())
402        } else {
403            None
404        }
405    }
406
407    /// Set ADC target channel.
408    ///
409    /// In Single conversion mode (OneShot), setting the channel begins the conversion. In FIFO mode
410    /// the channel is added to the FIFO buffer.
411    ///
412    /// Note: If the channel is changed while a conversion is in progress the
413    /// current conversion will be cancelled. If in FIFO mode, conversion will
414    /// resume once the FIFO channels are refilled.
415    pub fn set_channel<T: Channel<Adc<Enabled>, ID = u8>>(&self, _pin: &T) {
416        self.peripheral
417            .sc1
418            .modify(|_, w| unsafe { w.adch().bits(T::channel()) });
419    }
420
421    /// Set the ADC's configuration
422    pub fn configure(self, config: AdcConfig) -> Adc<Enabled> {
423        self.peripheral.sc3.modify(|_, w| {
424            use pac::adc::sc3::{ADICLK_A, ADIV_A, ADLSMP_A, MODE_A};
425            w.adiclk()
426                .variant(match config.clock_source {
427                    AdcClocks::Bus =>
428                    // If divisor is 16, use the Bus / 2 clock source, else use
429                    // the 1:1 Bus clock source
430                    {
431                        match config.clock_divisor {
432                            ClockDivisor::_16 => ADICLK_A::_01,
433                            _ => ADICLK_A::_00,
434                        }
435                    }
436                    AdcClocks::External => ADICLK_A::_10,
437                    AdcClocks::Async => ADICLK_A::_11,
438                })
439                .mode()
440                .variant(match config.resolution {
441                    AdcResolution::_8bit => MODE_A::_00,
442                    AdcResolution::_10bit => MODE_A::_01,
443                    AdcResolution::_12bit => MODE_A::_10,
444                })
445                .adlsmp()
446                .variant(match config.sample_time {
447                    AdcSampleTime::Short => ADLSMP_A::_0,
448                    AdcSampleTime::Long => ADLSMP_A::_1,
449                })
450                .adiv()
451                .variant(match config.clock_divisor {
452                    ClockDivisor::_1 => ADIV_A::_00,
453                    ClockDivisor::_2 => ADIV_A::_01,
454                    ClockDivisor::_4 => ADIV_A::_10,
455                    _ => ADIV_A::_11,
456                })
457                .adlpc()
458                .bit(config.low_power)
459        });
460
461        // It looks like SCGC has to be set before touching the peripheral
462        // at all, else hardfault. Go back later to confirm that if using external clock
463        // scgc can be cleared.
464        // w.adc().variant(match config.clock_source {
465        //     AdcClocks::Bus => ADC_A::_1,
466        //     _ => ADC_A::_0,
467        // })
468
469        Adc {
470            peripheral: self.peripheral,
471            _state: PhantomData,
472            onchip_channels: self.onchip_channels,
473        }
474    }
475}
476
477impl Adc<Disabled> {
478    /// Connects the bus clock to the adc via the SIM peripheral, allowing
479    /// read and write access to ADC registers.
480    ///
481    /// Any attempt to access ADC registers while disabled results in a
482    /// HardFault, generated by hardware.
483    ///
484    /// This also enables the bandgap voltage reference.
485    pub fn enable(self) -> Adc<Enabled> {
486        cortex_m::interrupt::free(|_| {
487            unsafe { &(*pac::SIM::ptr()) }.scgc.modify(|_, w| {
488                use pac::sim::scgc::ADC_A;
489                w.adc().variant(ADC_A::_1)
490            });
491
492            // Don't start a conversion (set channel to DummyDisable)
493            self.peripheral.sc1.modify(|_, w| w.adch()._11111());
494
495            // Bandgap. Grab directly, Currently the bandgap isn't implemented
496            // in [system::PMC]. We will eventually have to pass in the pmc
497            // peripheral handle as a variable.
498            unsafe { &(*pac::PMC::ptr()) }
499                .spmsc1
500                .modify(|_, w| w.bgbe()._1());
501        });
502
503        Adc {
504            peripheral: self.peripheral,
505            _state: PhantomData,
506            onchip_channels: self.onchip_channels,
507        }
508    }
509
510    /// Set the ADC's configuration
511    ///
512    /// This is a sugar method for calling [Adc<Disabled>::enable] followed by
513    /// [Adc<Enabled>::configure]
514    pub fn configure(self, config: AdcConfig) -> Adc<Enabled> {
515        self.enable().configure(config)
516    }
517}
518
519impl<Mode> Adc<Mode> {
520    /// Not Implemented
521    pub fn into_interrupt(self) -> Adc<Mode> {
522        unimplemented!("Interrupt is not yet implemented");
523        // Adc::<Mode> {
524        //     peripheral: self.peripheral,
525        //     _state: PhantomData,
526        //     onchip_channels: self.onchip_channels,
527        // }
528    }
529
530    /// Not Implemented
531    pub fn into_fifo(self, _depth: u8) -> Adc<Mode> {
532        // self.peripheral
533        //     .sc4
534        //     .modify(|_r, w| w.afdep().bits(depth & 0x7));
535        // Adc::<Mode> {
536        //     peripheral: self.peripheral,
537        //     _state: PhantomData,
538        //     onchip_channels: self.onchip_channels,
539        // }
540        unimplemented!("FIFO is not yet implemented");
541    }
542
543    /// Not Implemented
544    pub fn into_continuous(self) -> Adc<Mode> {
545        unimplemented!("Continuous Conversion mode not yet implemented");
546    }
547}
548
549impl OnChipChannels {
550    /// Request an instance of an on-chip [Vss] channel.
551    pub fn vss(&mut self) -> Result<Analog<Vss<Input>>, Error> {
552        self.vss.take().ok_or(Error::Moved)
553    }
554
555    /// Return the instance of [Vss]
556    pub fn return_vss(&mut self, inst: Analog<Vss<Input>>) {
557        self.vss.replace(inst);
558    }
559
560    /// Try to grab an instance of the onchip [TempSense] channel.
561    pub fn tempsense(&mut self) -> Result<Analog<TempSense<Input>>, Error> {
562        self.temp_sense.take().ok_or(Error::Moved)
563    }
564
565    /// Return the instance of [TempSense]
566    pub fn return_tempsense(&mut self, inst: Analog<TempSense<Input>>) {
567        self.temp_sense.replace(inst);
568    }
569
570    /// Try to grab an instance of the onchip [Bandgap] channel.
571    ///
572    /// The bandgap reference is a fixed 1.16V (nom, Factory trimmed to +/-
573    /// 0.02V at Vdd=5.0 at 125C) signal that is available to the ADC Module.
574    /// It can be used as a voltage reference for the ACMP and as an [Analog]
575    /// channel that can be used to (roughly) check the VDD voltage
576    pub fn bandgap(&mut self) -> Result<Analog<Bandgap<Input>>, Error> {
577        self.bandgap.take().ok_or(Error::Moved)
578    }
579
580    /// Return the instance of [Bandgap]
581    pub fn return_bandgap(&mut self, inst: Analog<Bandgap<Input>>) {
582        self.bandgap.replace(inst);
583    }
584
585    /// Try to grab an instance of the onchip Voltage Reference High ([VrefH]) channel.
586    pub fn vref_h(&mut self) -> Result<Analog<VrefH<Input>>, Error> {
587        self.vref_h.take().ok_or(Error::Moved)
588    }
589
590    /// Return the instance of [VrefH]
591    pub fn return_vref_h(&mut self, inst: Analog<VrefH<Input>>) {
592        self.vref_h.replace(inst);
593    }
594
595    /// Try to grab an instance of the onchip Voltage Reference Low ([VrefL]) channel.
596    pub fn vref_l(&mut self) -> Result<Analog<VrefL<Input>>, Error> {
597        self.vref_l.take().ok_or(Error::Moved)
598    }
599
600    /// Return the instance of [VrefL]
601    pub fn return_vref_l(&mut self, inst: Analog<VrefL<Input>>) {
602        self.vref_l.replace(inst);
603    }
604
605    /// Grab a [DummyDisable] instance. Multiple Instances possible.
606    pub fn dummy_disable(&self) -> Analog<DummyDisable<Input>> {
607        Analog {
608            pin: DummyDisable::<Input> { _mode: PhantomData },
609        }
610    }
611}
612
613/// Holds On-Chip ADC Channel inputs and provides an interface to grab and return them.
614// These have to have the Input dummy type to allow them to have the Channel
615// trait.
616pub struct OnChipChannels {
617    vss: Option<Analog<Vss<Input>>>,
618    temp_sense: Option<Analog<TempSense<Input>>>,
619    bandgap: Option<Analog<Bandgap<Input>>>,
620    vref_h: Option<Analog<VrefH<Input>>>,
621    vref_l: Option<Analog<VrefL<Input>>>,
622}
623
624/// Dummy type state for on-chip ADC input channels
625pub struct Input;
626
627/// Adc Input Channel, measures ground (should be 0?)
628pub struct Vss<Input> {
629    _mode: PhantomData<Input>,
630}
631
632/// Adc Input Channel, measures internal temperature sensor
633pub struct TempSense<Input> {
634    _mode: PhantomData<Input>,
635}
636
637/// Adc Input Channel, Bandgap internal voltage reference
638pub struct Bandgap<Input> {
639    _mode: PhantomData<Input>,
640}
641
642/// Adc Input Channel, Voltage Reference, High
643pub struct VrefH<Input> {
644    _mode: PhantomData<Input>,
645}
646
647/// Adc Input Channel, Voltage Reference, Low
648pub struct VrefL<Input> {
649    _mode: PhantomData<Input>,
650}
651
652/// Dummy Channel that temporarily disables the Adc Module.
653pub struct DummyDisable<Input> {
654    _mode: PhantomData<Input>,
655}
656macro_rules! adc_input_channels {
657    ( $($Chan:expr => $Pin:ident),+ $(,)*) => {
658        $(
659            impl<OldMode> Channel<Adc<Enabled>> for Analog<$Pin<OldMode>> {
660                type ID = u8;
661                fn channel() -> u8 { $Chan }
662            }
663        )+
664    };
665}
666
667use crate::gpio::{gpioa::*, gpiob::*};
668adc_input_channels! (
669    0_u8 => PTA0,
670    1_u8 => PTA1,
671    2_u8 => PTA6,
672    3_u8 => PTA7,
673    4_u8 => PTB0,
674    5_u8 => PTB1,
675    6_u8 => PTB2,
676    7_u8 => PTB3,
677    8_u8 => PTC0,
678    9_u8 => PTC1,
679    10_u8 => PTC2,
680    11_u8 => PTC3,
681    12_u8 => PTF4,
682    13_u8 => PTF5,
683    14_u8 => PTF6,
684    15_u8 => PTF7,
685    16_u8 => Vss,
686    22_u8 => TempSense,
687    23_u8 => Bandgap,
688    24_u8 => VrefH,
689    25_u8 => VrefL,
690    0x1F_u8 => DummyDisable,
691);
692
693macro_rules! impl_analog_pin {
694    ( $($Chan:expr => $Pin:ident),+ $(,)*) => {
695        $(
696            impl<OldMode> $Pin<OldMode> {
697                /// Convert Pin into the [Analog] state for use by the ADC.
698                ///
699                /// This implementation provides the GPIO interface a method to
700                /// give an eligible pin to the ADC peripheral for conversion
701                /// into an Analog pin. This method is only implemented in
702                /// eligible pins. The ADC peripheral disables the GPIO and
703                /// PORT control over the pin and connects it to the ADC mux
704                /// (controlled by [Adc::set_channel].
705                ///
706                /// Note: The [Analog::outof_analog] method must be used to
707                /// return the pin to a normal Input/Output typestate. The pin
708                /// will be returned in the same typestate as it was received.
709                pub fn into_analog(self) -> Analog<$Pin<OldMode>> {
710                    unsafe {
711                        (*ADC::ptr())
712                            .apctl1
713                            .modify(|r, w| w.adpc().bits(r.adpc().bits() | (1 << $Chan)));
714                    }
715                    Analog { pin: self }
716                }
717            }
718
719            impl<OldMode> Analog<$Pin<OldMode>> {
720                /// Return Analog state Pin to normal GPIO-state interface.
721                ///
722                /// The Pin will be in the same state that it was when it
723                /// entered the Analog type state.
724                pub fn outof_analog(self) -> $Pin<OldMode> {
725                    let adc = unsafe { &(*ADC::ptr()) };
726                    adc.apctl1
727                        .modify(|r, w| unsafe { w.adpc().bits(r.adpc().bits() & !(1 << $Chan)) });
728                    self.pin
729                }
730            }
731        )+
732    };
733}
734
735impl_analog_pin!(
736    0_u8 => PTA0,
737    1_u8 => PTA1,
738    2_u8 => PTA6,
739    3_u8 => PTA7,
740    4_u8 => PTB0,
741    5_u8 => PTB1,
742    6_u8 => PTB2,
743    7_u8 => PTB3,
744    8_u8 => PTC0,
745    9_u8 => PTC1,
746    10_u8 => PTC2,
747    11_u8 => PTC3,
748    12_u8 => PTF4,
749    13_u8 => PTF5,
750    14_u8 => PTF6,
751    15_u8 => PTF7,
752);
753
754impl<Pin> OneShot<Adc<Enabled>, u16, Pin> for Adc<Enabled>
755where
756    Pin: Channel<Adc<Enabled>, ID = u8>,
757{
758    type Error = Infallible;
759
760    fn read(&mut self, pin: &mut Pin) -> nb::Result<u16, Self::Error> {
761        self.set_channel(pin);
762        while !self.is_done() {}
763        let ret_val = Ok(self.result());
764        let disable = self.onchip_channels.dummy_disable();
765        self.set_channel(&disable);
766        ret_val
767    }
768}