aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/adc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa/src/adc.rs')
-rw-r--r--embassy-mcxa/src/adc.rs849
1 files changed, 849 insertions, 0 deletions
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs
new file mode 100644
index 000000000..f88bb6b37
--- /dev/null
+++ b/embassy-mcxa/src/adc.rs
@@ -0,0 +1,849 @@
1//! ADC driver
2use core::future::Future;
3use core::marker::PhantomData;
4
5use embassy_hal_internal::{Peri, PeripheralType};
6use maitake_sync::WaitCell;
7use paste::paste;
8
9use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4};
10use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset};
11use crate::gpio::{GpioPin, SealedPin};
12use crate::interrupt::typelevel::{Handler, Interrupt};
13use crate::pac;
14use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
15use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
16use crate::pac::adc1::cmdl1::Mode;
17use crate::pac::adc1::ctrl::CalAvgs;
18use crate::pac::adc1::tctrl::{Tcmd, Tpri};
19
20/// Trigger priority policy for ADC conversions.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[repr(u8)]
23pub enum TriggerPriorityPolicy {
24 ConvPreemptImmediatelyNotAutoResumed = 0,
25 ConvPreemptSoftlyNotAutoResumed = 1,
26 ConvPreemptImmediatelyAutoRestarted = 4,
27 ConvPreemptSoftlyAutoRestarted = 5,
28 ConvPreemptImmediatelyAutoResumed = 12,
29 ConvPreemptSoftlyAutoResumed = 13,
30 ConvPreemptSubsequentlyNotAutoResumed = 2,
31 ConvPreemptSubsequentlyAutoRestarted = 6,
32 ConvPreemptSubsequentlyAutoResumed = 14,
33 TriggerPriorityExceptionDisabled = 16,
34}
35
36/// Configuration for the LPADC peripheral.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct LpadcConfig {
39 /// Control system transition to Stop and Wait power modes while ADC is converting.
40 /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed.
41 /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry.
42 pub enable_in_doze_mode: bool,
43 /// Auto-Calibration Averages.
44 pub conversion_average_mode: CalAvgs,
45 /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption).
46 pub enable_analog_preliminary: bool,
47 /// Power-up delay value (in ADC clock cycles)
48 pub power_up_delay: u8,
49 /// Reference voltage source selection
50 pub reference_voltage_source: Refsel,
51 /// Power configuration selection.
52 pub power_level_mode: Pwrsel,
53 /// Trigger priority policy for handling multiple triggers
54 pub trigger_priority_policy: TriggerPriorityPolicy,
55 /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations,
56 /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration.
57 pub enable_conv_pause: bool,
58 /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles.
59 /// Only available when ADC pausing function is enabled. The available value range is in 9-bit.
60 pub conv_pause_delay: u16,
61 /// Power configuration (normal/deep sleep behavior)
62 pub power: PoweredClock,
63 /// ADC clock source selection
64 pub source: AdcClockSel,
65 /// Clock divider for ADC clock
66 pub div: Div4,
67}
68
69impl Default for LpadcConfig {
70 fn default() -> Self {
71 LpadcConfig {
72 enable_in_doze_mode: true,
73 conversion_average_mode: CalAvgs::NoAverage,
74 enable_analog_preliminary: false,
75 power_up_delay: 0x80,
76 reference_voltage_source: Refsel::Option1,
77 power_level_mode: Pwrsel::Lowest,
78 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
79 enable_conv_pause: false,
80 conv_pause_delay: 0,
81 power: PoweredClock::NormalEnabledDeepSleepDisabled,
82 source: AdcClockSel::FroLfDiv,
83 div: Div4::no_div(),
84 }
85 }
86}
87
88/// Configuration for a conversion command.
89///
90/// Defines the parameters for a single ADC conversion operation.
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct ConvCommandConfig {
93 pub chained_next_command_number: Next,
94 pub enable_auto_channel_increment: bool,
95 pub loop_count: u8,
96 pub hardware_average_mode: Avgs,
97 pub sample_time_mode: Sts,
98 pub hardware_compare_mode: Cmpen,
99 pub hardware_compare_value_high: u32,
100 pub hardware_compare_value_low: u32,
101 pub conversion_resolution_mode: Mode,
102 pub enable_wait_trigger: bool,
103}
104
105impl Default for ConvCommandConfig {
106 fn default() -> Self {
107 ConvCommandConfig {
108 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
109 enable_auto_channel_increment: false,
110 loop_count: 0,
111 hardware_average_mode: Avgs::NoAverage,
112 sample_time_mode: Sts::Sample3p5,
113 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
114 hardware_compare_value_high: 0,
115 hardware_compare_value_low: 0,
116 conversion_resolution_mode: Mode::Data12Bits,
117 enable_wait_trigger: false,
118 }
119 }
120}
121
122/// Configuration for a conversion trigger.
123///
124/// Defines how a trigger initiates ADC conversions.
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub struct ConvTriggerConfig {
127 pub target_command_id: Tcmd,
128 pub delay_power: u8,
129 pub priority: Tpri,
130 pub enable_hardware_trigger: bool,
131}
132
133impl Default for ConvTriggerConfig {
134 fn default() -> Self {
135 ConvTriggerConfig {
136 target_command_id: Tcmd::NotValid,
137 delay_power: 0,
138 priority: Tpri::HighestPriority,
139 enable_hardware_trigger: false,
140 }
141 }
142}
143
144/// Shorthand for `Result<T>`.
145pub type Result<T> = core::result::Result<T, Error>;
146
147/// ADC Error types
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149#[cfg_attr(feature = "defmt", derive(defmt::Format))]
150pub enum Error {
151 /// FIFO is empty, no conversion result available
152 FifoEmpty,
153 /// Invalid configuration
154 InvalidConfig,
155 /// Clock configuration error.
156 ClockSetup(ClockError),
157}
158
159/// Result of an ADC conversion.
160///
161/// Contains the conversion value and metadata about the conversion.
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct ConvResult {
164 pub command_id_source: u8,
165 pub loop_count_index: u8,
166 pub trigger_id_source: u8,
167 pub conv_value: u16,
168}
169
170/// ADC interrupt handler.
171pub struct InterruptHandler<I: Instance> {
172 _phantom: PhantomData<I>,
173}
174
175/// ADC driver instance.
176pub struct Adc<'a, M: ModeAdc> {
177 _inst: PhantomData<&'a mut ()>,
178 mode: M,
179
180 // The channel index of the pin used to create our ADC instance
181 channel_idx: u8,
182
183 // The register block of the ADC instance
184 info: &'static pac::adc0::RegisterBlock,
185}
186
187impl<'a> Adc<'a, Blocking> {
188 /// Create a new blocking instance of the ADC driver.
189 /// # Arguments
190 /// * `_inst` - ADC peripheral instance
191 /// * `pin` - GPIO pin to use for ADC
192 /// * `config` - ADC configuration
193 pub fn new_blocking<I: Instance>(
194 _inst: Peri<'a, I>,
195 pin: Peri<'a, impl AdcPin<I>>,
196 config: LpadcConfig,
197 ) -> Result<Self> {
198 Self::new_inner(_inst, pin, config, Blocking)
199 }
200
201 /// Enable ADC interrupts.
202 ///
203 /// Enables the interrupt sources specified in the bitmask.
204 ///
205 /// # Arguments
206 /// * `mask` - Bitmask of interrupt sources to enable
207 pub fn enable_interrupt(&mut self, mask: u32) {
208 self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
209 }
210
211 /// Disable ADC interrupts.
212 ///
213 /// Disables the interrupt sources specified in the bitmask.
214 ///
215 /// # Arguments
216 /// * `mask` - Bitmask of interrupt sources to disable
217 pub fn disable_interrupt(&mut self, mask: u32) {
218 self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
219 }
220
221 pub fn set_fifo_watermark(&mut self, watermark: u8) -> Result<()> {
222 if watermark > 0b111 {
223 return Err(Error::InvalidConfig);
224 }
225 self.info.fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(watermark) });
226 Ok(())
227 }
228
229 /// Trigger ADC conversion(s) via software.
230 ///
231 /// Initiates conversion(s) for the trigger(s) specified in the bitmask.
232 /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.).
233 ///
234 /// # Arguments
235 /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N)
236 ///
237 /// # Returns
238 /// * `Ok(())` if the triger mask was valid
239 /// * `Err(Error::InvalidConfig)` if the mask was greater than `0b1111`
240 pub fn do_software_trigger(&self, trigger_id_mask: u8) -> Result<()> {
241 if trigger_id_mask > 0b1111 {
242 return Err(Error::InvalidConfig);
243 }
244 self.info.swtrig().write(|w| unsafe { w.bits(trigger_id_mask as u32) });
245 Ok(())
246 }
247
248 /// Set conversion command configuration.
249 ///
250 /// Configures a conversion command slot with the specified parameters.
251 /// Commands define how conversions are performed (channel, resolution, etc.).
252 ///
253 /// # Arguments
254 /// * `index` - Command index (Must be in range 1..=7)
255 /// * `config` - Command configuration
256 ///
257 /// # Returns
258 /// * `Ok(())` if the command was configured successfully
259 /// * `Err(Error::InvalidConfig)` if the index is out of range
260 pub fn set_conv_command_config(&self, index: usize, config: &ConvCommandConfig) -> Result<()> {
261 self.set_conv_command_config_inner(index, config)
262 }
263
264 /// Set conversion trigger configuration.
265 ///
266 /// Configures a trigger to initiate conversions. Triggers can be
267 /// activated by software or hardware signals.
268 ///
269 /// # Arguments
270 /// * `trigger_id` - Trigger index (0..=3)
271 /// * `config` - Trigger configuration
272 pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> {
273 self.set_conv_trigger_config_inner(trigger_id, config)
274 }
275
276 /// Reset the FIFO buffer.
277 ///
278 /// Clears all pending conversion results from the FIFO.
279 pub fn do_reset_fifo(&self) {
280 self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
281 }
282
283 /// Get conversion result from FIFO.
284 ///
285 /// Reads and returns the next conversion result from the FIFO.
286 /// Returns `None` if the FIFO is empty.
287 ///
288 /// # Returns
289 /// - `Some(ConvResult)` if a result is available
290 /// - `Err(Error::FifoEmpty)` if the FIFO is empty
291 pub fn get_conv_result(&self) -> Result<ConvResult> {
292 self.get_conv_result_inner()
293 }
294}
295
296impl<'a> Adc<'a, Async> {
297 /// Initialize ADC with interrupt support.
298 ///
299 /// # Arguments
300 /// * `_inst` - ADC peripheral instance
301 /// * `pin` - GPIO pin to use for ADC
302 /// * `_irq` - Interrupt binding for this ADC instance
303 /// * `config` - ADC configuration
304 pub fn new_async<I: Instance>(
305 _inst: Peri<'a, I>,
306 pin: Peri<'a, impl AdcPin<I>>,
307 _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
308 config: LpadcConfig,
309 ) -> Result<Self> {
310 let adc = Self::new_inner(_inst, pin, config, Async { waiter: I::wait_cell() })?;
311
312 I::Interrupt::unpend();
313 unsafe { I::Interrupt::enable() };
314
315 let cfg = ConvCommandConfig {
316 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
317 enable_auto_channel_increment: false,
318 loop_count: 0,
319 hardware_average_mode: Avgs::NoAverage,
320 sample_time_mode: Sts::Sample3p5,
321 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
322 hardware_compare_value_high: 0,
323 hardware_compare_value_low: 0,
324 conversion_resolution_mode: Mode::Data16Bits,
325 enable_wait_trigger: false,
326 };
327
328 // We always use command 1, so this cannot fail
329 _ = adc.set_conv_command_config_inner(1, &cfg);
330
331 let cfg = ConvTriggerConfig {
332 target_command_id: Tcmd::ExecuteCmd1,
333 delay_power: 0,
334 priority: Tpri::HighestPriority,
335 enable_hardware_trigger: false,
336 };
337
338 // We always use trigger 0, so this cannot fail
339 _ = adc.set_conv_trigger_config_inner(0, &cfg);
340
341 // We always set the watermark to 0 (trigger when 1 is available)
342 I::ptr().fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(0) });
343
344 Ok(adc)
345 }
346
347 /// Set the number of averages
348 pub fn set_averages(&mut self, avgs: Avgs) {
349 // TODO: we should probably return a result or wait for idle?
350 // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior."
351 self.info.cmdh1().modify(|_r, w| w.avgs().variant(avgs));
352 }
353
354 /// Set the sample time
355 pub fn set_sample_time(&mut self, st: Sts) {
356 // TODO: we should probably return a result or wait for idle?
357 // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior."
358 self.info.cmdh1().modify(|_r, w| w.sts().variant(st));
359 }
360
361 pub fn set_resolution(&mut self, mode: Mode) {
362 // TODO: we should probably return a result or wait for idle?
363 // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior."
364 self.info.cmdl1().modify(|_r, w| w.mode().variant(mode));
365 }
366
367 fn wait_idle(&mut self) -> impl Future<Output = core::result::Result<(), maitake_sync::Closed>> + use<'_> {
368 self.mode
369 .waiter
370 .wait_for(|| self.info.ie().read().fwmie0().bit_is_clear())
371 }
372
373 /// Read ADC value asynchronously.
374 ///
375 /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered.
376 ///
377 /// The function:
378 /// 1. Enables the FIFO watermark interrupt
379 /// 2. Triggers a software conversion on trigger 0
380 /// 3. Waits for the conversion to complete
381 /// 4. Returns the conversion result
382 ///
383 /// # Returns
384 /// 16-bit ADC conversion value
385 pub async fn read(&mut self) -> Result<u16> {
386 // If we cancelled a previous read, we might still be busy, wait
387 // until the interrupt is cleared (done by the interrupt)
388 _ = self.wait_idle().await;
389
390 // Clear the fifo
391 self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
392
393 // Trigger a new conversion
394 self.info.ie().modify(|_r, w| w.fwmie0().set_bit());
395 self.info.swtrig().write(|w| w.swt0().set_bit());
396
397 // Wait for completion
398 _ = self.wait_idle().await;
399
400 self.get_conv_result_inner().map(|r| r.conv_value)
401 }
402}
403
404impl<'a, M: ModeAdc> Adc<'a, M> {
405 /// Internal initialization function shared by `new_async` and `new_blocking`.
406 fn new_inner<I: Instance, P: AdcPin<I>>(
407 _inst: Peri<'a, I>,
408 pin: Peri<'a, P>,
409 config: LpadcConfig,
410 mode: M,
411 ) -> Result<Self> {
412 let adc = I::ptr();
413
414 _ = unsafe {
415 enable_and_reset::<I>(&AdcConfig {
416 power: config.power,
417 source: config.source,
418 div: config.div,
419 })
420 .map_err(Error::ClockSetup)?
421 };
422
423 pin.mux();
424
425 /* Reset the module. */
426 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
427 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
428
429 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
430
431 /* Disable the module before setting configuration. */
432 adc.ctrl().modify(|_, w| w.adcen().disabled());
433
434 /* Configure the module generally. */
435 adc.ctrl().modify(|_, w| w.dozen().bit(config.enable_in_doze_mode));
436
437 /* Set calibration average mode. */
438 adc.ctrl()
439 .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode));
440
441 adc.cfg().write(|w| unsafe {
442 w.pwren().bit(config.enable_analog_preliminary);
443
444 w.pudly()
445 .bits(config.power_up_delay)
446 .refsel()
447 .variant(config.reference_voltage_source)
448 .pwrsel()
449 .variant(config.power_level_mode)
450 .tprictrl()
451 .variant(match config.trigger_priority_policy {
452 TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed
453 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
454 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority,
455 TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed
456 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
457 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority,
458 _ => Tprictrl::AbortCurrentOnPriority,
459 })
460 .tres()
461 .variant(match config.trigger_priority_policy {
462 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted
463 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
464 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
465 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
466 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
467 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled,
468 _ => Tres::Disabled,
469 })
470 .tcmdres()
471 .variant(match config.trigger_priority_policy {
472 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
473 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
474 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed
475 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled,
476 _ => Tcmdres::Disabled,
477 })
478 .hpt_exdi()
479 .variant(match config.trigger_priority_policy {
480 TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled,
481 _ => HptExdi::Enabled,
482 })
483 });
484
485 if config.enable_conv_pause {
486 adc.pause()
487 .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) });
488 } else {
489 adc.pause().write(|w| unsafe { w.bits(0) });
490 }
491
492 adc.fctrl0().write(|w| unsafe { w.fwmark().bits(0) });
493
494 // Enable ADC
495 adc.ctrl().modify(|_, w| w.adcen().enabled());
496
497 Ok(Self {
498 _inst: PhantomData,
499 mode,
500 channel_idx: P::CHANNEL,
501 info: adc,
502 })
503 }
504
505 /// Perform offset calibration.
506 /// Waits for calibration to complete before returning.
507 pub fn do_offset_calibration(&self) {
508 // Enable calibration mode
509 self.info
510 .ctrl()
511 .modify(|_, w| w.calofs().offset_calibration_request_pending());
512
513 // Wait for calibration to complete (polling status register)
514 while self.info.stat().read().cal_rdy().is_not_set() {}
515 }
516
517 /// Calculate gain conversion result from gain adjustment factor.
518 ///
519 /// # Arguments
520 /// * `gain_adjustment` - Gain adjustment factor
521 ///
522 /// # Returns
523 /// Gain calibration register value
524 pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 {
525 let mut gcra_array = [0u32; 17];
526 let mut gcalr: u32 = 0;
527
528 for i in (1..=17).rev() {
529 let shift = 16 - (i - 1);
530 let step = 1.0 / (1u32 << shift) as f32;
531 let tmp = (gain_adjustment / step) as u32;
532 gcra_array[i - 1] = tmp;
533 gain_adjustment -= tmp as f32 * step;
534 }
535
536 for i in (1..=17).rev() {
537 gcalr += gcra_array[i - 1] << (i - 1);
538 }
539 gcalr
540 }
541
542 /// Perform automatic gain calibration.
543 pub fn do_auto_calibration(&self) {
544 self.info
545 .ctrl()
546 .modify(|_, w| w.cal_req().calibration_request_pending());
547
548 while self.info.gcc0().read().rdy().is_gain_cal_not_valid() {}
549
550 let mut gcca = self.info.gcc0().read().gain_cal().bits() as u32;
551 if gcca & 0x8000 != 0 {
552 gcca |= !0xFFFF;
553 }
554
555 let gcra = 131072.0 / (131072.0 - gcca as f32);
556
557 // Write to GCR0
558 self.info
559 .gcr0()
560 .write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) });
561
562 self.info.gcr0().modify(|_, w| w.rdy().set_bit());
563
564 // Wait for calibration to complete (polling status register)
565 while self.info.stat().read().cal_rdy().is_not_set() {}
566 }
567
568 fn set_conv_command_config_inner(&self, index: usize, config: &ConvCommandConfig) -> Result<()> {
569 let (cmdl, cmdh) = match index {
570 1 => (self.info.cmdl1(), self.info.cmdh1()),
571 2 => (self.info.cmdl2(), self.info.cmdh2()),
572 3 => (self.info.cmdl3(), self.info.cmdh3()),
573 4 => (self.info.cmdl4(), self.info.cmdh4()),
574 5 => (self.info.cmdl5(), self.info.cmdh5()),
575 6 => (self.info.cmdl6(), self.info.cmdh6()),
576 7 => (self.info.cmdl7(), self.info.cmdh7()),
577 _ => return Err(Error::InvalidConfig),
578 };
579
580 cmdl.write(|w| {
581 unsafe {
582 w.adch().bits(self.channel_idx);
583 }
584 w.mode().variant(config.conversion_resolution_mode)
585 });
586
587 cmdh.write(|w| {
588 w.next().variant(config.chained_next_command_number);
589 unsafe {
590 w.loop_().bits(config.loop_count);
591 }
592 w.avgs().variant(config.hardware_average_mode);
593 w.sts().variant(config.sample_time_mode);
594 w.cmpen().variant(config.hardware_compare_mode);
595 w.wait_trig().bit(config.enable_wait_trigger);
596 w.lwi().bit(config.enable_auto_channel_increment);
597 w
598 });
599
600 Ok(())
601 }
602
603 fn set_conv_trigger_config_inner(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> {
604 // 0..4 are valid
605 if trigger_id >= 4 {
606 return Err(Error::InvalidConfig);
607 }
608
609 let tctrl = &self.info.tctrl(trigger_id);
610
611 tctrl.write(|w| {
612 w.tcmd().variant(config.target_command_id);
613 unsafe {
614 w.tdly().bits(config.delay_power);
615 }
616 w.tpri().variant(config.priority);
617 if config.enable_hardware_trigger {
618 w.hten().enabled()
619 } else {
620 w
621 }
622 });
623
624 Ok(())
625 }
626
627 /// Get conversion result from FIFO.
628 ///
629 /// Reads and returns the next conversion result from the FIFO.
630 /// Returns `None` if the FIFO is empty.
631 ///
632 /// # Returns
633 /// - `Some(ConvResult)` if a result is available
634 /// - `Err(Error::FifoEmpty)` if the FIFO is empty
635 fn get_conv_result_inner(&self) -> Result<ConvResult> {
636 let fifo = self.info.resfifo0().read();
637 if !fifo.valid().is_valid() {
638 return Err(Error::FifoEmpty);
639 }
640
641 Ok(ConvResult {
642 command_id_source: fifo.cmdsrc().bits(),
643 loop_count_index: fifo.loopcnt().bits(),
644 trigger_id_source: fifo.tsrc().bits(),
645 conv_value: fifo.d().bits(),
646 })
647 }
648}
649
650impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
651 unsafe fn on_interrupt() {
652 T::ptr().ie().modify(|_r, w| w.fwmie0().clear_bit());
653 T::wait_cell().wake();
654 }
655}
656
657mod sealed {
658 /// Seal a trait
659 pub trait Sealed {}
660}
661
662impl<I: GpioPin> sealed::Sealed for I {}
663
664trait SealedInstance {
665 fn ptr() -> &'static pac::adc0::RegisterBlock;
666 fn wait_cell() -> &'static WaitCell;
667}
668
669/// ADC Instance
670#[allow(private_bounds)]
671pub trait Instance: SealedInstance + PeripheralType + Gate<MrccPeriphConfig = AdcConfig> {
672 /// Interrupt for this ADC instance.
673 type Interrupt: Interrupt;
674}
675
676macro_rules! impl_instance {
677 ($($n:expr),*) => {
678 $(
679 paste!{
680 impl SealedInstance for crate::peripherals::[<ADC $n>] {
681 fn ptr() -> &'static pac::adc0::RegisterBlock {
682 unsafe { &*pac::[<Adc $n>]::ptr() }
683 }
684
685 fn wait_cell() -> &'static WaitCell {
686 static WAIT_CELL: WaitCell = WaitCell::new();
687 &WAIT_CELL
688 }
689
690 }
691
692 impl Instance for crate::peripherals::[<ADC $n>] {
693 type Interrupt = crate::interrupt::typelevel::[<ADC $n>];
694 }
695 }
696 )*
697 };
698}
699
700impl_instance!(0, 1, 2, 3);
701
702pub trait AdcPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
703 const CHANNEL: u8;
704
705 /// Set the given pin to the correct muxing state
706 fn mux(&self);
707}
708
709/// Driver mode.
710#[allow(private_bounds)]
711pub trait ModeAdc: sealed::Sealed {}
712
713/// Blocking mode.
714pub struct Blocking;
715impl sealed::Sealed for Blocking {}
716impl ModeAdc for Blocking {}
717
718/// Async mode.
719pub struct Async {
720 waiter: &'static WaitCell,
721}
722impl sealed::Sealed for Async {}
723impl ModeAdc for Async {}
724
725macro_rules! impl_pin {
726 ($pin:ident, $peri:ident, $func:ident, $channel:literal) => {
727 impl AdcPin<crate::peripherals::$peri> for crate::peripherals::$pin {
728 const CHANNEL: u8 = $channel;
729
730 fn mux(&self) {
731 self.set_pull(crate::gpio::Pull::Disabled);
732 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
733 self.set_drive_strength(crate::gpio::DriveStrength::Normal.into());
734 self.set_function(crate::pac::port0::pcr0::Mux::$func);
735 }
736 }
737 };
738}
739
740impl_pin!(P2_0, ADC0, Mux0, 0);
741impl_pin!(P2_4, ADC0, Mux0, 1);
742impl_pin!(P2_15, ADC0, Mux0, 2);
743impl_pin!(P2_3, ADC0, Mux0, 3);
744impl_pin!(P2_2, ADC0, Mux0, 4);
745impl_pin!(P2_12, ADC0, Mux0, 5);
746impl_pin!(P2_16, ADC0, Mux0, 6);
747impl_pin!(P2_7, ADC0, Mux0, 7);
748impl_pin!(P0_18, ADC0, Mux0, 8);
749impl_pin!(P0_19, ADC0, Mux0, 9);
750impl_pin!(P0_20, ADC0, Mux0, 10);
751impl_pin!(P0_21, ADC0, Mux0, 11);
752impl_pin!(P0_22, ADC0, Mux0, 12);
753impl_pin!(P0_23, ADC0, Mux0, 13);
754impl_pin!(P0_3, ADC0, Mux0, 14);
755impl_pin!(P0_6, ADC0, Mux0, 15);
756impl_pin!(P1_0, ADC0, Mux0, 16);
757impl_pin!(P1_1, ADC0, Mux0, 17);
758impl_pin!(P1_2, ADC0, Mux0, 18);
759impl_pin!(P1_3, ADC0, Mux0, 19);
760impl_pin!(P1_4, ADC0, Mux0, 20);
761impl_pin!(P1_5, ADC0, Mux0, 21);
762impl_pin!(P1_6, ADC0, Mux0, 22);
763impl_pin!(P1_7, ADC0, Mux0, 23);
764
765// ???
766// impl_pin!(P1_10, ADC0, Mux0, 255);
767
768impl_pin!(P2_1, ADC1, Mux0, 0);
769impl_pin!(P2_5, ADC1, Mux0, 1);
770impl_pin!(P2_19, ADC1, Mux0, 2);
771impl_pin!(P2_6, ADC1, Mux0, 3);
772impl_pin!(P2_3, ADC1, Mux0, 4);
773impl_pin!(P2_13, ADC1, Mux0, 5);
774impl_pin!(P2_17, ADC1, Mux0, 6);
775impl_pin!(P2_7, ADC1, Mux0, 7);
776impl_pin!(P1_10, ADC1, Mux0, 8);
777impl_pin!(P1_11, ADC1, Mux0, 9);
778impl_pin!(P1_12, ADC1, Mux0, 10);
779impl_pin!(P1_13, ADC1, Mux0, 11);
780impl_pin!(P1_14, ADC1, Mux0, 12);
781impl_pin!(P1_15, ADC1, Mux0, 13);
782// ???
783// impl_pin!(P1_16, ADC1, Mux0, 255);
784// impl_pin!(P1_17, ADC1, Mux0, 255);
785// impl_pin!(P1_18, ADC1, Mux0, 255);
786// impl_pin!(P1_19, ADC1, Mux0, 255);
787// ???
788impl_pin!(P3_31, ADC1, Mux0, 20);
789impl_pin!(P3_30, ADC1, Mux0, 21);
790impl_pin!(P3_29, ADC1, Mux0, 22);
791
792impl_pin!(P2_4, ADC2, Mux0, 0);
793impl_pin!(P2_10, ADC2, Mux0, 1);
794impl_pin!(P4_4, ADC2, Mux0, 2);
795// impl_pin!(P2_24, ADC2, Mux0, 255); ???
796impl_pin!(P2_16, ADC2, Mux0, 4);
797impl_pin!(P2_12, ADC2, Mux0, 5);
798impl_pin!(P2_20, ADC2, Mux0, 6);
799impl_pin!(P2_7, ADC2, Mux0, 7);
800impl_pin!(P0_2, ADC2, Mux0, 8);
801// ???
802// impl_pin!(P0_4, ADC2, Mux0, 255);
803// impl_pin!(P0_5, ADC2, Mux0, 255);
804// impl_pin!(P0_6, ADC2, Mux0, 255);
805// impl_pin!(P0_7, ADC2, Mux0, 255);
806// impl_pin!(P0_12, ADC2, Mux0, 255);
807// impl_pin!(P0_13, ADC2, Mux0, 255);
808// ???
809impl_pin!(P0_14, ADC2, Mux0, 14);
810impl_pin!(P0_15, ADC2, Mux0, 15);
811// ???
812// impl_pin!(P4_0, ADC2, Mux0, 255);
813// impl_pin!(P4_1, ADC2, Mux0, 255);
814// ???
815impl_pin!(P4_2, ADC2, Mux0, 18);
816impl_pin!(P4_3, ADC2, Mux0, 19);
817//impl_pin!(P4_4, ADC2, Mux0, 20); // Conflit with ADC2_A3 and ADC2_A20 using the same pin
818impl_pin!(P4_5, ADC2, Mux0, 21);
819impl_pin!(P4_6, ADC2, Mux0, 22);
820impl_pin!(P4_7, ADC2, Mux0, 23);
821
822impl_pin!(P2_5, ADC3, Mux0, 0);
823impl_pin!(P2_11, ADC3, Mux0, 1);
824impl_pin!(P2_23, ADC3, Mux0, 2);
825// impl_pin!(P2_25, ADC3, Mux0, 255); // ???
826impl_pin!(P2_17, ADC3, Mux0, 4);
827impl_pin!(P2_13, ADC3, Mux0, 5);
828impl_pin!(P2_21, ADC3, Mux0, 6);
829impl_pin!(P2_7, ADC3, Mux0, 7);
830// ???
831// impl_pin!(P3_2, ADC3, Mux0, 255);
832// impl_pin!(P3_3, ADC3, Mux0, 255);
833// impl_pin!(P3_4, ADC3, Mux0, 255);
834// impl_pin!(P3_5, ADC3, Mux0, 255);
835// ???
836impl_pin!(P3_6, ADC3, Mux0, 12);
837impl_pin!(P3_7, ADC3, Mux0, 13);
838impl_pin!(P3_12, ADC3, Mux0, 14);
839impl_pin!(P3_13, ADC3, Mux0, 15);
840impl_pin!(P3_14, ADC3, Mux0, 16);
841impl_pin!(P3_15, ADC3, Mux0, 17);
842impl_pin!(P3_20, ADC3, Mux0, 18);
843impl_pin!(P3_21, ADC3, Mux0, 19);
844impl_pin!(P3_22, ADC3, Mux0, 20);
845// ???
846// impl_pin!(P3_23, ADC3, Mux0, 255);
847// impl_pin!(P3_24, ADC3, Mux0, 255);
848// impl_pin!(P3_25, ADC3, Mux0, 255);
849// ???