stm32f4xx_hal/adc.rs
1//! Analog to digital converter configuration.
2//!
3//! # Status
4//! Most options relating to regular conversions are implemented. One-shot and sequences of conversions
5//! have been tested and work as expected.
6//!
7//! GPIO to channel mapping should be correct for all supported F4 devices. The mappings were taken from
8//! CubeMX. The mappings are feature gated per 4xx device but there are actually sub variants for some
9//! devices and some pins may be missing on some variants. The implementation has been split up and commented
10//! to show which pins are available on certain device variants but currently the library doesn't enforce this.
11//! To fully support the right pins would require 10+ more features for the various variants.
12//! ## Todo
13//! * Injected conversions
14//! * Analog watchdog config
15//! * Discontinuous mode
16//! # Examples
17//! ## One-shot conversion
18//! ```
19//! use stm32f4xx_hal::{
20//! gpio::gpioa,
21//! adc::{
22//! Adc,
23//! config::{AdcConfig, SampleTime},
24//! },
25//! };
26//!
27//! let mut adc = Adc::adc1(device.ADC1, true, AdcConfig::default());
28//! let pa3 = gpioa.pa3.into_analog();
29//! let sample = adc.convert(&pa3, SampleTime::Cycles_480);
30//! let millivolts = adc.sample_to_millivolts(sample);
31//! info!("pa3: {}mV", millivolts);
32//! ```
33//!
34//! ## Sequence conversion
35//! ```
36//! use stm32f4xx_hal::{
37//! gpio::gpioa,
38//! adc::{
39//! Adc,
40//! config::{AdcConfig, SampleTime, Sequence, Eoc, Scan, Clock},
41//! },
42//! };
43//!
44//! let config = AdcConfig::default()
45//! //We'll either need DMA or an interrupt per conversion to convert
46//! //multiple values in a sequence
47//! .end_of_conversion_interrupt(Eoc::Conversion)
48//! //Scan mode is also required to convert a sequence
49//! .scan(Scan::Enabled)
50//! //And since we're looking for one interrupt per conversion the
51//! //clock will need to be fairly slow to avoid overruns breaking
52//! //the sequence. If you are running in debug mode and logging in
53//! //the interrupt, good luck... try setting pclk2 really low.
54//! //(Better yet use DMA)
55//! .clock(Clock::Pclk2_div_8);
56//! let mut adc = Adc::adc1(device.ADC1, true, config);
57//! let pa0 = gpioa.pa0.into_analog();
58//! let pa3 = gpioa.pa3.into_analog();
59//! adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112);
60//! adc.configure_channel(&pa3, Sequence::Two, SampleTime::Cycles_480);
61//! adc.configure_channel(&pa0, Sequence::Three, SampleTime::Cycles_112);
62//! adc.start_conversion();
63//! ```
64//!
65//! ## External trigger
66//!
67//! A common mistake on STM forums is enabling continuous mode but that causes it to start
68//! capturing on the first trigger and capture as fast as possible forever, regardless of
69//! future triggers. Continuous mode is disabled by default but I thought it was worth
70//! highlighting.
71//!
72//! Getting the timer config right to make sure it's sending the event the ADC is listening
73//! to can be a bit of a pain but the key fields are highlighted below. Try hooking a timer
74//! channel up to an external pin with an LED or oscilloscope attached to check it's really
75//! generating pulses if the ADC doesn't seem to be triggering.
76//! ```
77//! use stm32f4xx_hal::{
78//! gpio::gpioa,
79//! adc::{
80//! Adc,
81//! config::{AdcConfig, SampleTime, Sequence, Eoc, Scan, Clock},
82//! },
83//! };
84//!
85//! let config = AdcConfig::default()
86//! //Set the trigger you want
87//! .external_trigger(TriggerMode::RisingEdge, ExternalTrigger::Tim_1_cc_1);
88//! let mut adc = Adc::adc1(device.ADC1, true, config);
89//! let pa0 = gpioa.pa0.into_analog();
90//! adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112);
91//! //Make sure it's enabled but don't start the conversion
92//! adc.enable();
93//!
94//! //Configure the timer
95//! let mut tim = Timer::tim1(device.TIM1, 1.hz(), clocks);
96//! unsafe {
97//! let tim = &(*TIM1::ptr());
98//!
99//! //Channel 1
100//! //Disable the channel before configuring it
101//! tim.ccer().modify(|_, w| w.cc1e().clear_bit());
102//!
103//! tim.ccmr1_output().modify(|_, w| w
104//! //Preload enable for channel
105//! .oc1pe().set_bit()
106//!
107//! //Set mode for channel, the default mode is "frozen" which won't work
108//! .oc1m().pwm_mode1()
109//! );
110//!
111//! //Set the duty cycle, 0 won't work in pwm mode but might be ok in
112//! //toggle mode or match mode
113//! let max_duty = tim.arr.read().arr().bits() as u16;
114//! tim.ccr1.modify(|_, w| w.ccr().bits(max_duty / 2));
115//!
116//! //Enable the channel
117//! tim.ccer.modify(|_, w| w.cc1e().set_bit());
118//!
119//! //Enable the TIM main Output
120//! tim.bdtr.modify(|_, w| w.moe().set_bit());
121//! }
122//! ```
123
124#![deny(missing_docs)]
125
126/*
127 Currently unused but this is the formula for using temperature calibration:
128 Temperature in °C = (110-30) * (adc_sample - VtempCal30::get().read()) / (VtempCal110::get().read()-VtempCal30::get().read()) + 30
129*/
130
131use crate::dma::traits::{DMASet, PeriAddress, SafePeripheralRead};
132use crate::dma::PeripheralToMemory;
133use crate::rcc;
134use crate::{
135 gpio::{self, Analog},
136 pac::{self, RCC},
137 signature::VrefCal,
138 signature::VDDA_CALIB,
139};
140use core::fmt;
141use core::ops::Deref;
142
143pub mod config;
144mod f4;
145
146/// Vref internal signal, used for calibration
147pub struct Vref;
148
149/// Vbat internal signal, used for monitoring the battery (if used)
150pub struct Vbat;
151
152/// Core temperature internal signal
153pub struct Temperature;
154
155/// Marker trait for all ADC peripherals
156pub trait Instance:
157 crate::Sealed + Deref<Target = pac::adc1::RegisterBlock> + rcc::Enable + rcc::Reset
158{
159}
160
161#[doc(hidden)]
162pub trait Calibrate {
163 // Provide a stub implementation for ADCs that do not have a means of sampling VREF.
164 fn calibrate(&mut self) {}
165}
166
167impl Instance for pac::ADC1 {}
168#[cfg(feature = "adc2")]
169impl Instance for pac::ADC2 {}
170#[cfg(feature = "adc3")]
171impl Instance for pac::ADC3 {}
172
173/// Analog to Digital Converter
174#[derive(Clone, Copy)]
175pub struct Adc<ADC: Instance> {
176 /// Current config of the ADC, kept up to date by the various set methods
177 config: config::AdcConfig,
178 /// The adc peripheral
179 adc_reg: ADC,
180 /// VDDA in millivolts calculated from the factory calibration and vrefint
181 calibrated_vdda: u32,
182 /// Exclusive limit for the sample value possible for the configured resolution.
183 max_sample: u32,
184}
185impl<ADC: Instance> fmt::Debug for Adc<ADC> {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 write!(
188 f,
189 "Adc: {{ calibrated_vdda: {:?}, max_sample: {:?}, config: {:?}, ... }}",
190 self.calibrated_vdda, self.max_sample, self.config
191 )
192 }
193}
194
195// Note that only ADC1 supports measurement of VREF, VBAT, and the internal temperature sensor.
196impl Calibrate for Adc<pac::ADC1> {
197 /// Calculates the system VDDA by sampling the internal VREF channel and comparing
198 /// the result with the value stored at the factory.
199 fn calibrate(&mut self) {
200 self.enable();
201
202 let vref_en = self.temperature_and_vref_enabled();
203 if !vref_en {
204 self.enable_temperature_and_vref();
205 }
206
207 let vref_cal = VrefCal::get().read();
208 let vref_samp = self.read(&mut Vref).unwrap(); //This can't actually fail, it's just in a result to satisfy hal trait
209
210 self.calibrated_vdda = (VDDA_CALIB * u32::from(vref_cal)) / u32::from(vref_samp);
211 if !vref_en {
212 self.disable_temperature_and_vref();
213 }
214 }
215}
216
217impl Adc<pac::ADC1> {
218 /// Calibrate ADC
219 pub fn calibrate(&mut self) {
220 Calibrate::calibrate(self);
221 }
222
223 /// Enables the vbat internal channel
224 pub fn enable_vbat(&self) {
225 unsafe {
226 let common = &(*pac::ADC_COMMON::ptr());
227 common.ccr().modify(|_, w| w.vbate().set_bit());
228 }
229 }
230
231 /// Enables the vbat internal channel
232 pub fn disable_vbat(&self) {
233 unsafe {
234 let common = &(*pac::ADC_COMMON::ptr());
235 common.ccr().modify(|_, w| w.vbate().clear_bit());
236 }
237 }
238
239 /// Enables the temp and vref internal channels.
240 /// They can't work while vbat is also enabled so this method also disables vbat.
241 pub fn enable_temperature_and_vref(&mut self) {
242 //VBAT prevents TS and VREF from being sampled
243 self.disable_vbat();
244 unsafe {
245 let common = &(*pac::ADC_COMMON::ptr());
246 common.ccr().modify(|_, w| w.tsvrefe().set_bit());
247 }
248 }
249
250 /// Disables the temp and vref internal channels
251 pub fn disable_temperature_and_vref(&mut self) {
252 unsafe {
253 let common = &(*pac::ADC_COMMON::ptr());
254 common.ccr().modify(|_, w| w.tsvrefe().clear_bit());
255 }
256 }
257
258 /// Returns if the temp and vref internal channels are enabled
259 pub fn temperature_and_vref_enabled(&mut self) -> bool {
260 unsafe {
261 let common = &(*pac::ADC_COMMON::ptr());
262 common.ccr().read().tsvrefe().bit_is_set()
263 }
264 }
265}
266
267impl<ADC: Instance> SafePeripheralRead for Adc<ADC> {}
268
269impl<ADC: Instance> Adc<ADC>
270where
271 Self: Calibrate,
272{
273 /// Enables the ADC clock, resets the peripheral (optionally), runs calibration and applies the supplied config
274 /// # Arguments
275 /// * `reset` - should a reset be performed. This is provided because on some devices multiple ADCs share the same common reset
276 pub fn new(adc: ADC, reset: bool, config: config::AdcConfig, rcc: &mut RCC) -> Adc<ADC> {
277 // All ADCs share the same reset interface.
278
279 //Enable the clock
280 ADC::enable(rcc);
281
282 if reset {
283 //Reset the peripheral(s)
284 ADC::reset(rcc);
285 }
286
287 let mut s = Self {
288 config,
289 adc_reg: adc,
290 calibrated_vdda: VDDA_CALIB,
291 max_sample: 0,
292 };
293
294 //Probably unnecessary to disable the ADC in most cases but it shouldn't do any harm either
295 s.disable();
296 s.apply_config(config);
297
298 s.enable();
299 s.calibrate();
300
301 // If the user specified a VDDA, use that over the internally determined value.
302 if let Some(vdda) = s.config.vdda {
303 s.calibrated_vdda = vdda;
304 }
305
306 s
307 }
308}
309
310impl<ADC: Instance> Adc<ADC> {
311 /// Applies all fields in AdcConfig
312 pub fn apply_config(&mut self, config: config::AdcConfig) {
313 self.set_clock(config.clock);
314 self.set_resolution(config.resolution);
315 self.set_align(config.align);
316 self.set_scan(config.scan);
317 self.set_external_trigger(config.external_trigger);
318 self.set_continuous(config.continuous);
319 self.set_dma(config.dma);
320 self.set_end_of_conversion_interrupt(config.end_of_conversion_interrupt);
321 self.set_default_sample_time(config.default_sample_time);
322
323 if let Some(vdda) = config.vdda {
324 self.calibrated_vdda = vdda;
325 }
326 }
327
328 /// Returns if the adc is enabled
329 pub fn is_enabled(&self) -> bool {
330 self.adc_reg.cr2().read().adon().bit_is_set()
331 }
332
333 /// Enables the adc
334 pub fn enable(&mut self) {
335 self.adc_reg.cr2().modify(|_, w| w.adon().set_bit());
336 }
337
338 /// Disables the adc
339 /// # Note
340 /// The ADC in the f4 has few restrictions on what can be configured while the ADC
341 /// is enabled. If any bugs are found where some settings aren't "sticking" try disabling
342 /// the ADC before changing them. The reference manual for the chip I'm using only states
343 /// that the sequence registers are locked when they are being converted.
344 pub fn disable(&mut self) {
345 self.adc_reg.cr2().modify(|_, w| w.adon().clear_bit());
346 }
347
348 /// Starts conversion sequence. Waits for the hardware to indicate it's actually started.
349 pub fn start_conversion(&mut self) {
350 self.enable();
351 self.clear_end_of_conversion_flag();
352 //Start conversion
353 self.adc_reg.cr2().modify(|_, w| w.swstart().set_bit());
354
355 while !self.adc_reg.sr().read().strt().bit_is_set() {}
356 }
357
358 /// Sets the clock for the adc
359 pub fn set_clock(&mut self, clock: config::Clock) {
360 self.config.clock = clock;
361 unsafe {
362 let common = &(*pac::ADC_COMMON::ptr());
363 common.ccr().modify(|_, w| w.adcpre().bits(clock as _));
364 }
365 }
366
367 /// Sets the sampling resolution
368 pub fn set_resolution(&mut self, resolution: config::Resolution) {
369 self.max_sample = match resolution {
370 config::Resolution::Twelve => 1 << 12,
371 config::Resolution::Ten => 1 << 10,
372 config::Resolution::Eight => 1 << 8,
373 config::Resolution::Six => 1 << 6,
374 };
375 self.config.resolution = resolution;
376 self.adc_reg
377 .cr1()
378 .modify(|_, w| w.res().set(resolution as _));
379 }
380
381 /// Sets the DR register alignment to left or right
382 pub fn set_align(&mut self, align: config::Align) {
383 self.config.align = align;
384 self.adc_reg
385 .cr2()
386 .modify(|_, w| w.align().bit(align.into()));
387 }
388
389 /// Enables and disables scan mode
390 pub fn set_scan(&mut self, scan: config::Scan) {
391 self.config.scan = scan;
392 self.adc_reg.cr1().modify(|_, w| w.scan().bit(scan.into()));
393 }
394
395 /// Sets which external trigger to use and if it is disabled, rising, falling or both
396 pub fn set_external_trigger(
397 &mut self,
398 (edge, extsel): (config::TriggerMode, config::ExternalTrigger),
399 ) {
400 self.config.external_trigger = (edge, extsel);
401 self.adc_reg.cr2().modify(|_, w| {
402 unsafe {
403 w.extsel().bits(extsel as _);
404 }
405 w.exten().set(edge as _)
406 });
407 }
408
409 /// Enables and disables continuous mode
410 pub fn set_continuous(&mut self, continuous: config::Continuous) {
411 self.config.continuous = continuous;
412 self.adc_reg
413 .cr2()
414 .modify(|_, w| w.cont().bit(continuous.into()));
415 }
416
417 /// Sets DMA to disabled, single or continuous
418 pub fn set_dma(&mut self, dma: config::Dma) {
419 self.config.dma = dma;
420 let (dds, en) = match dma {
421 config::Dma::Disabled => (false, false),
422 config::Dma::Single => (false, true),
423 config::Dma::Continuous => (true, true),
424 };
425 self.adc_reg.cr2().modify(|_, w| {
426 //DDS stands for "DMA disable selection"
427 //0 means do one DMA then stop
428 //1 means keep sending DMA requests as long as DMA=1
429 w.dds().bit(dds);
430 w.dma().bit(en)
431 });
432 }
433
434 /// Sets if the end-of-conversion behaviour.
435 /// The end-of-conversion interrupt occur either per conversion or for the whole sequence.
436 pub fn set_end_of_conversion_interrupt(&mut self, eoc: config::Eoc) {
437 self.config.end_of_conversion_interrupt = eoc;
438 let (en, eocs) = match eoc {
439 config::Eoc::Disabled => (false, false),
440 config::Eoc::Conversion => (true, true),
441 config::Eoc::Sequence => (true, false),
442 };
443 self.adc_reg.cr1().modify(|_, w| w.eocie().bit(en));
444 self.adc_reg.cr2().modify(|_, w| w.eocs().bit(eocs));
445 }
446
447 /// Resets the end-of-conversion flag
448 pub fn clear_end_of_conversion_flag(&mut self) {
449 self.adc_reg.sr().modify(|_, w| w.eoc().clear_bit());
450 }
451
452 /// Sets the default sample time that is used for one-shot conversions.
453 /// [configure_channel](#method.configure_channel) and [start_conversion](#method.start_conversion) can be \
454 /// used for configurations where different sampling times are required per channel.
455 pub fn set_default_sample_time(&mut self, sample_time: config::SampleTime) {
456 self.config.default_sample_time = sample_time;
457 }
458
459 /// Returns the current sequence length. Primarily useful for configuring DMA.
460 pub fn sequence_length(&mut self) -> u8 {
461 self.adc_reg.sqr1().read().l().bits() + 1
462 }
463
464 /// Reset the sequence
465 pub fn reset_sequence(&mut self) {
466 //The reset state is One conversion selected
467 self.adc_reg
468 .sqr1()
469 .modify(|_, w| w.l().set(config::Sequence::One.into()));
470 }
471
472 /// Returns the address of the ADC data register. Primarily useful for configuring DMA.
473 pub fn data_register_address(&self) -> u32 {
474 self.adc_reg.dr().as_ptr() as u32
475 }
476
477 /// Configure a channel for sampling.
478 /// It will make sure the sequence is at least as long as the `sequence` provided.
479 /// # Arguments
480 /// * `channel` - channel to configure
481 /// * `sequence` - where in the sequence to sample the channel. Also called rank in some STM docs/code
482 /// * `sample_time` - how long to sample for. See datasheet and ref manual to work out how long you need
483 /// to sample for at a given ADC clock frequency
484 pub fn configure_channel<CHANNEL>(
485 &mut self,
486 _channel: &CHANNEL,
487 sequence: config::Sequence,
488 sample_time: config::SampleTime,
489 ) where
490 CHANNEL: embedded_hal_02::adc::Channel<ADC, ID = u8>,
491 {
492 //Check the sequence is long enough
493 self.adc_reg.sqr1().modify(|r, w| {
494 let prev: config::Sequence = r.l().bits().into();
495 if prev < sequence {
496 w.l().set(sequence.into())
497 } else {
498 w
499 }
500 });
501
502 let channel = CHANNEL::channel();
503
504 //Set the channel in the right sequence field
505 use config::Sequence;
506 match sequence {
507 Sequence::One
508 | Sequence::Two
509 | Sequence::Three
510 | Sequence::Four
511 | Sequence::Five
512 | Sequence::Six => self
513 .adc_reg
514 .sqr3()
515 .modify(|_, w| unsafe { w.sq(sequence as u8).bits(channel) }),
516 Sequence::Seven
517 | Sequence::Eight
518 | Sequence::Nine
519 | Sequence::Ten
520 | Sequence::Eleven
521 | Sequence::Twelve => self
522 .adc_reg
523 .sqr2()
524 .modify(|_, w| unsafe { w.sq(sequence as u8 - 6).bits(channel) }),
525 _ => self
526 .adc_reg
527 .sqr1()
528 .modify(|_, w| unsafe { w.sq(sequence as u8 - 12).bits(channel) }),
529 };
530
531 //Set the sample time for the channel
532 let st = sample_time as u8;
533 match channel {
534 0..=9 => self
535 .adc_reg
536 .smpr2()
537 .modify(|_, w| unsafe { w.smp(channel).bits(st) }),
538 10..=18 => self
539 .adc_reg
540 .smpr1()
541 .modify(|_, w| unsafe { w.smp(channel - 10).bits(st) }),
542 _ => unimplemented!(),
543 };
544 }
545
546 /// Returns the current sample stored in the ADC data register
547 pub fn current_sample(&self) -> u16 {
548 self.adc_reg.dr().read().data().bits()
549 }
550
551 /// Converts a sample value to millivolts using calibrated VDDA and configured resolution.
552 /// Due to the ADC characteristics VDDA will never be reached as described in #362 and
553 /// [AN2834-How to get the best ADC accuracy in STM32 microcontrollers](https://www.st.com/resource/en/application_note/cd00211314-how-to-get-the-best-adc-accuracy-in-stm32-microcontrollers-stmicroelectronics.pdf) in section 3.1.2.
554 pub fn sample_to_millivolts(&self, sample: u16) -> u16 {
555 ((u32::from(sample) * self.calibrated_vdda) / self.max_sample) as u16
556 }
557
558 /// Make a converter for samples to millivolts
559 pub fn make_sample_to_millivolts(&self) -> impl Fn(u16) -> u16 {
560 let calibrated_vdda = self.calibrated_vdda;
561 let max_sample = self.max_sample;
562 move |sample| ((u32::from(sample) * calibrated_vdda) / max_sample) as u16
563 }
564
565 /// Returns the VDDA in millivolts calculated from the factory calibration and vrefint. Can be used to get calibration data from ADC1 and use it to configure ADCs that don't support calibration.
566 pub fn reference_voltage(&self) -> u32 {
567 self.calibrated_vdda
568 }
569
570 /// Block until the conversion is completed
571 /// # Panics
572 /// Will panic if there is no conversion started and the end-of-conversion bit is not set
573 pub fn wait_for_conversion_sequence(&self) {
574 if !self.adc_reg.sr().read().strt().bit_is_set()
575 && !self.adc_reg.sr().read().eoc().bit_is_set()
576 {
577 panic!("Waiting for end-of-conversion but no conversion started");
578 }
579 while !self.adc_reg.sr().read().eoc().bit_is_set() {}
580 //Clear the conversion started flag
581 self.adc_reg.sr().modify(|_, w| w.strt().clear_bit());
582 }
583
584 /// Synchronously convert a single sample
585 /// Note that it reconfigures the adc sequence and doesn't restore it
586 pub fn convert<PIN>(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16
587 where
588 PIN: embedded_hal_02::adc::Channel<ADC, ID = u8>,
589 {
590 self.adc_reg.cr2().modify(|_, w| {
591 //Disable dma
592 w.dma().clear_bit();
593 //Disable continuous mode
594 w.cont().clear_bit();
595 //Disable trigger
596 w.exten().set(config::TriggerMode::Disabled.into());
597 //EOC is set at the end of the sequence
598 w.eocs().clear_bit()
599 });
600 self.adc_reg.cr1().modify(|_, w| {
601 //Disable scan mode
602 w.scan().clear_bit();
603 //Disable end of conversion interrupt
604 w.eocie().clear_bit()
605 });
606
607 self.reset_sequence();
608 self.configure_channel(pin, config::Sequence::One, sample_time);
609 self.enable();
610 self.clear_end_of_conversion_flag();
611 self.start_conversion();
612
613 //Wait for the sequence to complete
614 self.wait_for_conversion_sequence();
615
616 let result = self.current_sample();
617
618 //Reset the config
619 self.apply_config(self.config);
620
621 result
622 }
623}
624
625impl<ADC: Instance> Adc<ADC> {
626 fn read<PIN>(&mut self, pin: &mut PIN) -> nb::Result<u16, ()>
627 where
628 PIN: embedded_hal_02::adc::Channel<ADC, ID = u8>,
629 {
630 let enabled = self.is_enabled();
631 if !enabled {
632 self.enable();
633 }
634
635 let sample = self.convert(pin, self.config.default_sample_time);
636
637 if !enabled {
638 self.disable();
639 }
640
641 Ok(sample)
642 }
643}
644
645impl<ADC: Instance, PIN> embedded_hal_02::adc::OneShot<ADC, u16, PIN> for Adc<ADC>
646where
647 PIN: embedded_hal_02::adc::Channel<ADC, ID = u8>,
648{
649 type Error = ();
650
651 fn read(&mut self, pin: &mut PIN) -> nb::Result<u16, Self::Error> {
652 self.read::<PIN>(pin)
653 }
654}
655
656unsafe impl<ADC: Instance> PeriAddress for Adc<ADC> {
657 #[inline(always)]
658 fn address(&self) -> u32 {
659 self.data_register_address()
660 }
661
662 type MemSize = u16;
663}
664
665unsafe impl<ADC: Instance, STREAM, const CHANNEL: u8> DMASet<STREAM, CHANNEL, PeripheralToMemory>
666 for Adc<ADC>
667where
668 ADC: DMASet<STREAM, CHANNEL, PeripheralToMemory>,
669{
670}
671
672macro_rules! adc_pins {
673 ($($pin:ty => ($adc:ident, $chan:expr)),+ $(,)*) => {
674 $(
675 impl embedded_hal_02::adc::Channel<pac::$adc> for $pin {
676 type ID = u8;
677 fn channel() -> u8 { $chan }
678 }
679 )+
680 };
681}
682use adc_pins;