lpc8xx_hal/
adc.rs

1//! API for ADC
2//!
3//! # Examples
4//!
5//! Read a single value:
6//! ``` no_run
7//! use lpc8xx_hal::prelude::*;
8//! use lpc8xx_hal::Peripherals;
9//! use lpc8xx_hal::syscon::clock_source::AdcClock;
10//!
11//! let mut p = Peripherals::take().unwrap();
12//!
13//! let mut syscon = p.SYSCON.split();
14//! let mut swm    = p.SWM.split();
15//!
16//! #[cfg(feature = "82x")]
17//! let mut swm_handle = swm.handle;
18//! #[cfg(feature = "845")]
19//! let mut swm_handle = swm.handle.enable(&mut syscon.handle);
20//!
21//! let adc_clock = AdcClock::new_default();
22//! let mut adc = p.ADC.enable(&adc_clock, &mut syscon.handle);
23//!
24//! let (mut adc_pin, _) = swm
25//!     .fixed_functions
26//!     .adc_0
27//!     .assign(p.pins.pio0_7.into_swm_pin(), &mut swm_handle);
28//!
29//! // Read a single value
30//! let adc_value = nb::block!(adc.read(&mut adc_pin))
31//!     .expect("Read should never fail");
32//! ```
33//!
34//! Please refer to the [examples in the repository] for more example code.
35//!
36//! [examples in the repository]: https://github.com/lpc-rs/lpc8xx-hal/tree/master/examples
37
38use embedded_hal::adc::{Channel, OneShot};
39
40use crate::{
41    init_state, pac, swm,
42    syscon::{self, clock_source::AdcClock},
43};
44
45/// Interface to the ADC peripheral
46///
47/// Controls the ADC.  Use [`Peripherals`] to gain access to an instance of
48/// this struct.
49///
50/// Please refer to the [module documentation] for more information.
51///
52/// # `embedded-hal` traits
53/// - [`embedded_hal::adc::OneShot`] for measuring the voltage on a pin
54///
55/// [`Peripherals`]: ../struct.Peripherals.html
56/// [module documentation]: index.html
57/// [`embedded_hal::adc::OneShot`]: #impl-OneShot%3CADC%3CEnabled%3C()%3E%3E%2C%20u16%2C%20PIN%3E
58pub struct ADC<State = init_state::Enabled> {
59    adc: pac::ADC0,
60    _state: State,
61}
62
63impl ADC<init_state::Disabled> {
64    pub(crate) fn new(adc: pac::ADC0) -> Self {
65        Self {
66            adc,
67            _state: init_state::Disabled,
68        }
69    }
70    /// Enable the ADC
71    ///
72    /// This method is only available, if `ADC` is in the [`Disabled`] state.
73    /// Code that attempts to call this method when the peripheral is already
74    /// enabled will not compile.
75    ///
76    /// Consumes this instance of `ADC` and returns another instance that has
77    /// its `State` type parameter set to [`Enabled`].
78    ///
79    /// # Examples
80    ///
81    /// Please refer to the [module documentation] for a full example.
82    ///
83    /// [`Disabled`]: ../init_state/struct.Disabled.html
84    /// [`Enabled`]: ../init_state/struct.Enabled.html
85    /// [module documentation]: index.html
86    pub fn enable(self, clock: &AdcClock, syscon: &mut syscon::Handle) -> ADC {
87        syscon.enable_clock(&self.adc);
88        syscon.power_up(&self.adc);
89
90        // Start calibration
91        // The clock needs to be at 500 kHz for this task
92        self.adc.ctrl.write(|w| {
93            unsafe { w.clkdiv().bits(clock.caldiv) };
94            w.calmode().set_bit()
95        });
96
97        // Wait until the calibration is done
98        while self.adc.ctrl.read().calmode().bit_is_set() {}
99
100        self.adc
101            .ctrl
102            .write(|w| unsafe { w.clkdiv().bits(clock.div) });
103
104        ADC {
105            adc: self.adc,
106            _state: init_state::Enabled(()),
107        }
108    }
109}
110
111impl ADC<init_state::Enabled> {
112    /// Disable the ADC
113    ///
114    /// This method is only available, if `ADC` is in the [`Enabled`] state.
115    /// Code that attempts to call this method when the peripheral is already
116    /// disabled will not compile.
117    ///
118    /// Consumes this instance of `ADC` and returns another instance that has
119    /// its `State` type parameter set to [`Disabled`].
120    ///
121    /// [`Enabled`]: ../init_state/struct.Enabled.html
122    /// [`Disabled`]: ../init_state/struct.Disabled.html
123    pub fn disable(
124        self,
125        syscon: &mut syscon::Handle,
126    ) -> ADC<init_state::Disabled> {
127        syscon.disable_clock(&self.adc);
128
129        ADC {
130            adc: self.adc,
131            _state: init_state::Disabled,
132        }
133    }
134}
135
136impl<State> ADC<State> {
137    /// Return the raw peripheral
138    ///
139    /// This method serves as an escape hatch from the HAL API. It returns the
140    /// raw peripheral, allowing you to do whatever you want with it, without
141    /// limitations imposed by the API.
142    ///
143    /// If you are using this method because a feature you need is missing from
144    /// the HAL API, please [open an issue] or, if an issue for your feature
145    /// request already exists, comment on the existing issue, so we can
146    /// prioritize it accordingly.
147    ///
148    /// [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues
149    pub fn free(self) -> pac::ADC0 {
150        self.adc
151    }
152}
153
154impl<PIN> OneShot<ADC, u16, PIN> for ADC
155where
156    PIN: Channel<ADC, ID = u8>,
157{
158    type Error = ();
159
160    /// Request that the ADC begin a conversion on the specified pin
161    fn read(&mut self, _: &mut PIN) -> nb::Result<u16, Self::Error> {
162        // Start the measurement of the given channel
163        // Follows the description in the um
164        self.adc.seq_ctrla.write(|w| {
165            unsafe { w.channels().bits(1 << PIN::channel()) };
166            w.start().set_bit();
167            w.trigpol().set_bit();
168            w.seq_ena().enabled();
169            w.mode().end_of_conversion()
170        });
171
172        let mut read = self.adc.seq_gdata.read();
173
174        // Wait until the conversion is done
175        while read.datavalid().bit_is_clear() {
176            read = self.adc.seq_gdata.read();
177        }
178
179        // Returns the result as a 16 bit value
180        Ok(read.result().bits() << 4)
181    }
182}
183
184macro_rules! adc_channel {
185    ($pin:ident, $num:expr) => {
186        impl<PIN> Channel<ADC>
187            for swm::Function<swm::$pin, swm::state::Assigned<PIN>>
188        {
189            type ID = u8;
190
191            fn channel() -> Self::ID {
192                $num
193            }
194        }
195    };
196}
197
198adc_channel!(ADC_0, 0);
199adc_channel!(ADC_1, 1);
200adc_channel!(ADC_2, 2);
201adc_channel!(ADC_3, 3);
202adc_channel!(ADC_4, 4);
203adc_channel!(ADC_5, 5);
204adc_channel!(ADC_6, 6);
205adc_channel!(ADC_7, 7);
206adc_channel!(ADC_8, 8);
207adc_channel!(ADC_9, 9);
208adc_channel!(ADC_10, 10);
209adc_channel!(ADC_11, 11);