|
1 | 1 | //! Analog to digital converter configuration.
|
2 |
| -//! According to CubeMx, all STM32F4 chips use the same ADC IP so this should be correct for all variants. |
| 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 | +//! ``` |
3 | 123 |
|
4 | 124 | #![deny(missing_docs)]
|
5 | 125 |
|
@@ -424,137 +544,6 @@ pub mod config {
|
424 | 544 | }
|
425 | 545 |
|
426 | 546 | /// Analog to Digital Converter
|
427 |
| -/// # Status |
428 |
| -/// Most options relating to regular conversions are implemented. One-shot and sequences of conversions |
429 |
| -/// have been tested and work as expected. |
430 |
| -/// |
431 |
| -/// GPIO to channel mapping should be correct for all supported F4 devices. The mappings were taken from |
432 |
| -/// CubeMX. The mappings are feature gated per 4xx device but there are actually sub variants for some |
433 |
| -/// devices and some pins may be missing on some variants. The implementation has been split up and commented |
434 |
| -/// to show which pins are available on certain device variants but currently the library doesn't enforce this. |
435 |
| -/// To fully support the right pins would require 10+ more features for the various variants. |
436 |
| -/// ## Todo |
437 |
| -/// * Injected conversions |
438 |
| -/// * Analog watchdog config |
439 |
| -/// * Discontinuous mode |
440 |
| -/// # Examples |
441 |
| -/// ## One-shot conversion |
442 |
| -/// ``` |
443 |
| -/// use stm32f4xx_hal::{ |
444 |
| -/// gpio::gpioa, |
445 |
| -/// adc::{ |
446 |
| -/// Adc, |
447 |
| -/// config::AdcConfig, |
448 |
| -/// config::SampleTime, |
449 |
| -/// }, |
450 |
| -/// }; |
451 |
| -/// |
452 |
| -/// let mut adc = Adc::adc1(device.ADC1, true, AdcConfig::default()); |
453 |
| -/// let pa3 = gpioa.pa3.into_analog(); |
454 |
| -/// let sample = adc.convert(&pa3, SampleTime::Cycles_480); |
455 |
| -/// let millivolts = adc.sample_to_millivolts(sample); |
456 |
| -/// info!("pa3: {}mV", millivolts); |
457 |
| -/// ``` |
458 |
| -/// |
459 |
| -/// ## Sequence conversion |
460 |
| -/// ``` |
461 |
| -/// use stm32f4xx_hal::{ |
462 |
| -/// gpio::gpioa, |
463 |
| -/// adc::{ |
464 |
| -/// Adc, |
465 |
| -/// config::AdcConfig, |
466 |
| -/// config::SampleTime, |
467 |
| -/// config::Sequence, |
468 |
| -/// config::Eoc, |
469 |
| -/// config::Scan, |
470 |
| -/// config::Clock, |
471 |
| -/// }, |
472 |
| -/// }; |
473 |
| -/// |
474 |
| -/// let config = AdcConfig::default() |
475 |
| -/// //We'll either need DMA or an interrupt per conversion to convert |
476 |
| -/// //multiple values in a sequence |
477 |
| -/// .end_of_conversion_interrupt(Eoc::Conversion) |
478 |
| -/// //Scan mode is also required to convert a sequence |
479 |
| -/// .scan(Scan::Enabled) |
480 |
| -/// //And since we're looking for one interrupt per conversion the |
481 |
| -/// //clock will need to be fairly slow to avoid overruns breaking |
482 |
| -/// //the sequence. If you are running in debug mode and logging in |
483 |
| -/// //the interrupt, good luck... try setting pclk2 really low. |
484 |
| -/// //(Better yet use DMA) |
485 |
| -/// .clock(Clock::Pclk2_div_8); |
486 |
| -/// let mut adc = Adc::adc1(device.ADC1, true, config); |
487 |
| -/// let pa0 = gpioa.pa0.into_analog(); |
488 |
| -/// let pa3 = gpioa.pa3.into_analog(); |
489 |
| -/// adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112); |
490 |
| -/// adc.configure_channel(&pa3, Sequence::Two, SampleTime::Cycles_480); |
491 |
| -/// adc.configure_channel(&pa0, Sequence::Three, SampleTime::Cycles_112); |
492 |
| -/// adc.start_conversion(); |
493 |
| -/// ``` |
494 |
| -/// |
495 |
| -/// ## External trigger |
496 |
| -/// |
497 |
| -/// A common mistake on STM forums is enabling continuous mode but that causes it to start |
498 |
| -/// capturing on the first trigger and capture as fast as possible forever, regardless of |
499 |
| -/// future triggers. Continuous mode is disabled by default but I thought it was worth |
500 |
| -/// highlighting. |
501 |
| -/// |
502 |
| -/// Getting the timer config right to make sure it's sending the event the ADC is listening |
503 |
| -/// to can be a bit of a pain but the key fields are highlighted below. Try hooking a timer |
504 |
| -/// channel up to an external pin with an LED or oscilloscope attached to check it's really |
505 |
| -/// generating pulses if the ADC doesn't seem to be triggering. |
506 |
| -/// ``` |
507 |
| -/// use stm32f4xx_hal::{ |
508 |
| -/// gpio::gpioa, |
509 |
| -/// adc::{ |
510 |
| -/// Adc, |
511 |
| -/// config::AdcConfig, |
512 |
| -/// config::SampleTime, |
513 |
| -/// config::Sequence, |
514 |
| -/// config::Eoc, |
515 |
| -/// config::Scan, |
516 |
| -/// config::Clock, |
517 |
| -/// }, |
518 |
| -/// }; |
519 |
| -/// |
520 |
| -/// let config = AdcConfig::default() |
521 |
| -/// //Set the trigger you want |
522 |
| -/// .external_trigger(TriggerMode::RisingEdge, ExternalTrigger::Tim_1_cc_1); |
523 |
| -/// let mut adc = Adc::adc1(device.ADC1, true, config); |
524 |
| -/// let pa0 = gpioa.pa0.into_analog(); |
525 |
| -/// adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112); |
526 |
| -/// //Make sure it's enabled but don't start the conversion |
527 |
| -/// adc.enable(); |
528 |
| -/// |
529 |
| -/// //Configure the timer |
530 |
| -/// let mut tim = Timer::tim1(device.TIM1, 1.hz(), clocks); |
531 |
| -/// unsafe { |
532 |
| -/// let tim = &(*TIM1::ptr()); |
533 |
| -/// |
534 |
| -/// //Channel 1 |
535 |
| -/// //Disable the channel before configuring it |
536 |
| -/// tim.ccer.modify(|_, w| w.cc1e().clear_bit()); |
537 |
| -/// |
538 |
| -/// tim.ccmr1_output().modify(|_, w| w |
539 |
| -/// //Preload enable for channel |
540 |
| -/// .oc1pe().set_bit() |
541 |
| -/// |
542 |
| -/// //Set mode for channel, the default mode is "frozen" which won't work |
543 |
| -/// .oc1m().pwm_mode1() |
544 |
| -/// ); |
545 |
| -/// |
546 |
| -/// //Set the duty cycle, 0 won't work in pwm mode but might be ok in |
547 |
| -/// //toggle mode or match mode |
548 |
| -/// let max_duty = tim.arr.read().arr().bits() as u16; |
549 |
| -/// tim.ccr1.modify(|_, w| w.ccr().bits(max_duty / 2)); |
550 |
| -/// |
551 |
| -/// //Enable the channel |
552 |
| -/// tim.ccer.modify(|_, w| w.cc1e().set_bit()); |
553 |
| -/// |
554 |
| -/// //Enable the TIM main Output |
555 |
| -/// tim.bdtr.modify(|_, w| w.moe().set_bit()); |
556 |
| -/// } |
557 |
| -/// ``` |
558 | 547 | #[derive(Clone, Copy)]
|
559 | 548 | pub struct Adc<ADC> {
|
560 | 549 | /// Current config of the ADC, kept up to date by the various set methods
|
|
0 commit comments