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