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);