imxrt_hal/chip/imxrt10xx/adc.rs
1//! Analog to digital converters.
2//!
3//! This ADC driver supports the 0.2 `embedded_hal`'s ADC traits.
4//! To enable the traits, activate this package's `"eh02-unproven"
5//! feature.
6//!
7//! # Example
8//!
9//! ```no_run
10//! use imxrt_hal as hal;
11//! use imxrt_ral as ral;
12//! use hal::adc;
13//!
14//! let mut pads = // Handle to all processor pads
15//! # unsafe { imxrt_iomuxc::imxrt1060::Pads::new() };
16//!
17//! # || -> Option<()> {
18//! let adc1 = unsafe { ral::adc::ADC1::instance() };
19//! let mut adc1 = adc::Adc::new(adc1, adc::ClockSelect::ADACK, adc::ClockDivision::Div2);
20//! let mut a1 = adc::AnalogInput::new(pads.gpio_ad_b1.p02);
21//!
22//! let reading: u16 = adc1.read_blocking(&mut a1);
23//!
24//! // Read without constructing an analog pin:
25//! let adc2 = unsafe { ral::adc::ADC2::instance() };
26//! let mut adc2 = adc::Adc::new(adc2, adc::ClockSelect::ADACK, adc::ClockDivision::Div2);
27//!
28//! let reading = adc2.read_blocking_channel(7);
29//! # Some(()) }();
30//! ```
31
32use crate::iomuxc::adc::{prepare, Pin};
33use crate::ral;
34
35#[cfg(feature = "eh02-unproven")]
36use eh02::adc::{Channel, OneShot};
37
38/// The clock input for an ADC
39#[allow(non_camel_case_types)]
40#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
41pub enum ClockSelect {
42 /// IPG clock
43 IPG,
44 /// IPG clock / 2
45 IPG_2,
46 /// ADC Asynchronous clock
47 #[default]
48 ADACK,
49}
50
51/// How much to divide the clock input
52#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
53pub enum ClockDivision {
54 /// Input clock / 1
55 Div1,
56 /// Input clock
57 #[default]
58 Div2,
59 /// Input clock / 4
60 Div4,
61 /// Input clock / 8
62 Div8,
63}
64
65/// Conversion speeds done by clock cycles
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum ConversionSpeed {
68 /// 25 ADC clock cycles (24 on imxrt102x)
69 Slow,
70 /// 17 ADC clock cycles (16 on imxrt102x)
71 Medium,
72 /// 9 ADC clock cycles (8 on imxrt102x)
73 Fast,
74 /// 3 ADC clock cycles (2 on imxrt102x)
75 VeryFast,
76}
77
78/// Denotes how much hardware averaging to do
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum AveragingCount {
81 /// 1 sample average.
82 Avg1,
83 /// 4 sample average.
84 Avg4,
85 /// 8 sample average.
86 Avg8,
87 /// 16 sample average.
88 Avg16,
89 /// 32 sample average.
90 Avg32,
91}
92
93/// Specifies the resolution the ADC
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum ResolutionBits {
96 /// 8 bit resolution.
97 Res8,
98 /// 10 bit resolution.
99 Res10,
100 /// 12 bit resolution.
101 Res12,
102}
103
104/// A pin representing an analog input for a particular ADC
105pub struct AnalogInput<P, const N: u8> {
106 pin: P,
107}
108
109#[cfg(feature = "eh02-unproven")]
110impl<P, const N: u8> Channel<Adc<N>> for AnalogInput<P, N>
111where
112 P: Pin<N>,
113{
114 type ID = u16;
115
116 fn channel() -> Self::ID {
117 <P as Pin<N>>::INPUT as u16
118 }
119}
120
121impl<P, const N: u8> AnalogInput<P, N>
122where
123 P: Pin<N>,
124{
125 /// Creates a new analog input pin
126 pub fn new(mut pin: P) -> Self {
127 prepare(&mut pin);
128 Self { pin }
129 }
130
131 /// Release the ADC input, returning the underlying hardware pin. This pin is in an
132 /// unspecified state
133 pub fn release(self) -> P {
134 self.pin
135 }
136}
137
138/// The ADC driver.
139///
140/// The ADC starts out with a default configuration of 4 hardware samples, a conversion speed of
141/// medium, a resolution of 10 bits, and low power mode disabled. It's also pre-calibrated using
142/// 32 averages and a slow conversion speed.
143pub struct Adc<const N: u8> {
144 reg: ral::adc::Instance<N>,
145}
146
147impl<const N: u8> Adc<N> {
148 /// Constuct an ADC from a RAL ADC instance
149 pub fn new(reg: ral::adc::Instance<N>, clock: ClockSelect, division: ClockDivision) -> Self {
150 // Enable asynchronous clock if applicable
151 ral::modify_reg!(ral::adc, reg, GC, ADACKEN: match clock {
152 ClockSelect::ADACK => ADACKEN_1,
153 _ => ADACKEN_0
154 });
155
156 // Select the clock selection, division, and enable ADHSC if applicable
157 ral::modify_reg!(ral::adc, reg, CFG,
158 ADICLK: match clock {
159 ClockSelect::IPG => ADICLK_0,
160 ClockSelect::IPG_2 => ADICLK_1,
161 ClockSelect::ADACK => ADICLK_3
162 },
163 ADIV: match division {
164 ClockDivision::Div1 => ADIV_0,
165 ClockDivision::Div2 => ADIV_1,
166 ClockDivision::Div4 => ADIV_2,
167 ClockDivision::Div8 => ADIV_3
168 },
169 ADHSC: ADHSC_1
170 );
171
172 let mut inst = Self { reg };
173
174 inst.set_resolution(ResolutionBits::Res10);
175 inst.set_low_power_mode(false);
176
177 // Calibrate w/ slow settings initially
178 inst.set_averaging(AveragingCount::Avg32);
179 inst.set_conversion_speed(ConversionSpeed::Slow);
180 inst.calibrate();
181
182 // Set to default of 4 hardware averages & medium conversion speed
183 inst.set_averaging(AveragingCount::Avg4);
184 inst.set_conversion_speed(ConversionSpeed::Medium);
185
186 inst
187 }
188
189 /// Sets the resolution that analog reads return, in bits
190 pub fn set_resolution(&mut self, bits: ResolutionBits) {
191 ral::modify_reg!(ral::adc, self.reg, CFG, MODE: match bits {
192 ResolutionBits::Res8 => MODE_0,
193 ResolutionBits::Res10 => MODE_1,
194 ResolutionBits::Res12 => MODE_2
195 });
196 }
197
198 /// Sets the number of hardware averages taken by the ADC
199 pub fn set_averaging(&mut self, avg: AveragingCount) {
200 ral::modify_reg!(ral::adc, self.reg, GC, AVGE: match avg {
201 AveragingCount::Avg1 => AVGE_0,
202 _ => AVGE_1
203 });
204 ral::modify_reg!(ral::adc, self.reg, CFG, AVGS: match avg {
205 AveragingCount::Avg32 => AVGS_3,
206 AveragingCount::Avg16 => AVGS_2,
207 AveragingCount::Avg8 => AVGS_1,
208 _ => AVGS_0,
209 });
210 }
211
212 /// Sets the conversion speed for this ADC, see ConversionSpeed for clock cycle counts.
213 pub fn set_conversion_speed(&mut self, conversion_speed: ConversionSpeed) {
214 ral::modify_reg!(ral::adc, self.reg, CFG,
215 ADSTS: match conversion_speed {
216 ConversionSpeed::Slow => ADSTS_3,
217 ConversionSpeed::Medium => ADSTS_1,
218 ConversionSpeed::Fast => ADSTS_3,
219 ConversionSpeed::VeryFast => ADSTS_0
220 },
221 ADLSMP: match conversion_speed {
222 ConversionSpeed::Slow => ADLSMP_1,
223 ConversionSpeed::Medium => ADLSMP_1,
224 ConversionSpeed::Fast => ADLSMP_0,
225 ConversionSpeed::VeryFast => ADLSMP_0
226 }
227 );
228 }
229
230 /// Enables or disables the low power configuration in the ADC. This does limit the
231 /// ADACK clock frequency (<= 20MHz)
232 pub fn set_low_power_mode(&mut self, state: bool) {
233 ral::modify_reg!(ral::adc, self.reg, CFG, ADLPC: if state { ADLPC_1 } else { ADLPC_0 });
234 }
235
236 /// Calibrates the ADC, will wait for finish
237 pub fn calibrate(&mut self) {
238 ral::modify_reg!(ral::adc, self.reg, GC, CAL: 0b1);
239 while (ral::read_reg!(ral::adc, self.reg, CAL, CAL_CODE) != 0) {}
240 }
241
242 /// Perform a blocking read for an ADC sample.
243 pub fn read_blocking<P>(&mut self, _: &mut AnalogInput<P, N>) -> u16
244 where
245 P: Pin<N>,
246 {
247 self.read_blocking_channel(P::INPUT)
248 }
249
250 /// Perform a blocking read using the specified ADC channel.
251 ///
252 /// Unlike [`read_blocking()`](Self::read_blocking), which ensures
253 /// that the pin is configured as an ADC input, you're responsible
254 /// for configuring the pin as an ADC input before using this method.
255 /// Otherwise, this method may not produce a (correct) value.
256 ///
257 /// # Panics
258 ///
259 /// Panics if the ADC channel is greater than 15.
260 pub fn read_blocking_channel(&mut self, channel: u32) -> u16 {
261 // There's only 15 channels on the 1010 (0 through 14).
262 // Nevertheless, the HC0 register documents that you can
263 // pass in channel 15.
264 assert!(channel < 16);
265 ral::modify_reg!(ral::adc, self.reg, HC0, |_| channel);
266 while (ral::read_reg!(ral::adc, self.reg, HS, COCO0) == 0) {}
267
268 ral::read_reg!(ral::adc, self.reg, R0) as u16
269 }
270
271 /// Release the ADC's register block.
272 ///
273 /// You can use this to re-construct the driver with new configurations.
274 pub fn release(self) -> ral::adc::Instance<N> {
275 self.reg
276 }
277}
278
279#[cfg(feature = "eh02-unproven")]
280impl<W, P, const N: u8> OneShot<Adc<N>, W, AnalogInput<P, N>> for Adc<N>
281where
282 W: From<u16>,
283 P: Pin<N>,
284{
285 type Error = core::convert::Infallible;
286
287 /// Read an ADC value from an AnalogInput.
288 fn read(&mut self, _pin: &mut AnalogInput<P, N>) -> nb::Result<W, Self::Error> {
289 Ok(Adc::<N>::read_blocking(self, _pin).into())
290 }
291}
292
293/// Adapter for using an ADC input as a DMA source.
294///
295/// This adapter exposes the lower-level DMA interface. However, you may
296/// find it easier to use the interface available in [`dma`](crate::dma).
297pub struct DmaSource<P, const N: u8> {
298 adc: Adc<N>,
299 channel: u32,
300 _pin: P,
301}
302
303impl<P, const N: u8> DmaSource<AnalogInput<P, N>, N>
304where
305 P: Pin<N>,
306{
307 /// Create a new DMA source object for a DMA transfer.
308 pub fn new(adc: Adc<N>, pin: AnalogInput<P, N>) -> Self {
309 Self {
310 adc,
311 _pin: pin,
312 channel: P::INPUT,
313 }
314 }
315}
316
317impl<const N: u8> DmaSource<(), N> {
318 /// Create an ADC DMA source without a configured ADC input.
319 ///
320 /// You're responsible for configuring the pin as an ADC input.
321 pub fn without_pin(adc: Adc<N>, channel: u32) -> Self {
322 Self {
323 adc,
324 _pin: (),
325 channel,
326 }
327 }
328}
329
330impl<P, const N: u8> DmaSource<P, N> {
331 /// Returns a pointer to the ADC's `R0` register.
332 ///
333 /// You should use this pointer when coordinating a DMA transfer.
334 /// You're not expected to explicitly read from this pointer in software.
335 pub fn r0(&self) -> *const ral::RORegister<u32> {
336 core::ptr::addr_of!(self.adc.reg.R0)
337 }
338
339 /// Enable the ADC's DMA support.
340 ///
341 /// This is necessary to start a transfer. However, this in itself
342 /// does not start a DMA transfer.
343 pub fn enable_dma(&mut self) {
344 ral::modify_reg!(ral::adc, self.adc.reg, GC, ADCO: 1, DMAEN: 1);
345 ral::modify_reg!(ral::adc, self.adc.reg, HC0, |_| self.channel);
346 }
347
348 /// Disable the ADC's DMA support.
349 ///
350 /// See the DMA chapter in the reference manual to understand when this
351 /// should be called in the DMA transfer lifecycle.
352 pub fn disable_dma(&mut self) {
353 ral::modify_reg!(ral::adc, self.adc.reg, GC, ADCO: 0, DMAEN: 0);
354 }
355}
356
357/// ```compile_fail
358/// use imxrt_hal as hal;
359/// use imxrt_ral as ral;
360/// use hal::adc;
361///
362/// let mut pads = unsafe { imxrt_iomuxc::imxrt1060::Pads::new() };
363///
364/// let inst = unsafe { ral::adc::ADC1::instance() };
365/// let mut adc1 = adc::Adc::new(inst, Default::default(), Default::default());
366/// let mut adc2_pin = adc::AnalogInput::new(pads.gpio_ad_b1.p12);
367///
368/// let reading: u16 = adc1.read_blocking(&mut adc2_pin);
369/// ```
370#[cfg(doctest)]
371struct Adc1Pin2Mismatch;
372
373/// ```no_run
374/// use imxrt_hal as hal;
375/// use imxrt_ral as ral;
376/// use hal::adc;
377///
378/// let mut pads = unsafe { imxrt_iomuxc::imxrt1060::Pads::new() };
379///
380/// let inst = unsafe { ral::adc::ADC2::instance() };
381/// let mut adc2 = adc::Adc::new(inst, adc::ClockSelect::default(), adc::ClockDivision::default());
382/// let mut adc2_pin = adc::AnalogInput::new(pads.gpio_ad_b1.p12);
383///
384/// let reading: u16 = adc2.read_blocking(&mut adc2_pin);
385/// ```
386#[cfg(doctest)]
387struct Adc2Pin2;
388
389/// ```compile_fail
390/// use imxrt_hal as hal;
391/// use imxrt_ral as ral;
392/// use hal::adc;
393///
394/// let mut pads = unsafe { imxrt_iomuxc::imxrt1060::Pads::new() };
395///
396/// let inst = unsafe { ral::adc::ADC2::instance() };
397/// let mut adc2 = adc::Adc::new(inst, Default::default(), Default::default());
398/// let mut adc2_pin = adc::AnalogInput::new(pads.gpio_ad_b0.p13);
399///
400/// let reading: u16 = adc2.read_blocking(&mut adc2_pin);
401/// ```
402#[cfg(doctest)]
403struct Adc2Pin1Mismatch;