Skip to content

Commit

Permalink
move adc docs on module level
Browse files Browse the repository at this point in the history
  • Loading branch information
burrbull committed Jul 2, 2023
1 parent 51022eb commit 3756450
Showing 1 changed file with 121 additions and 132 deletions.
253 changes: 121 additions & 132 deletions src/adc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,125 @@
//! Analog to digital converter configuration.
//! According to CubeMx, all STM32F4 chips use the same ADC IP so this should be correct for all variants.
//!
//! # Status
//! Most options relating to regular conversions are implemented. One-shot and sequences of conversions
//! have been tested and work as expected.
//!
//! GPIO to channel mapping should be correct for all supported F4 devices. The mappings were taken from
//! CubeMX. The mappings are feature gated per 4xx device but there are actually sub variants for some
//! devices and some pins may be missing on some variants. The implementation has been split up and commented
//! to show which pins are available on certain device variants but currently the library doesn't enforce this.
//! To fully support the right pins would require 10+ more features for the various variants.
//! ## Todo
//! * Injected conversions
//! * Analog watchdog config
//! * Discontinuous mode
//! # Examples
//! ## One-shot conversion
//! ```
//! use stm32f4xx_hal::{
//! gpio::gpioa,
//! adc::{
//! Adc,
//! config::{AdcConfig, SampleTime},
//! },
//! };
//!
//! let mut adc = Adc::adc1(device.ADC1, true, AdcConfig::default());
//! let pa3 = gpioa.pa3.into_analog();
//! let sample = adc.convert(&pa3, SampleTime::Cycles_480);
//! let millivolts = adc.sample_to_millivolts(sample);
//! info!("pa3: {}mV", millivolts);
//! ```
//!
//! ## Sequence conversion
//! ```
//! use stm32f4xx_hal::{
//! gpio::gpioa,
//! adc::{
//! Adc,
//! config::{AdcConfig, SampleTime, Sequence, Eoc, Scan, Clock},
//! },
//! };
//!
//! let config = AdcConfig::default()
//! //We'll either need DMA or an interrupt per conversion to convert
//! //multiple values in a sequence
//! .end_of_conversion_interrupt(Eoc::Conversion)
//! //Scan mode is also required to convert a sequence
//! .scan(Scan::Enabled)
//! //And since we're looking for one interrupt per conversion the
//! //clock will need to be fairly slow to avoid overruns breaking
//! //the sequence. If you are running in debug mode and logging in
//! //the interrupt, good luck... try setting pclk2 really low.
//! //(Better yet use DMA)
//! .clock(Clock::Pclk2_div_8);
//! let mut adc = Adc::adc1(device.ADC1, true, config);
//! let pa0 = gpioa.pa0.into_analog();
//! let pa3 = gpioa.pa3.into_analog();
//! adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112);
//! adc.configure_channel(&pa3, Sequence::Two, SampleTime::Cycles_480);
//! adc.configure_channel(&pa0, Sequence::Three, SampleTime::Cycles_112);
//! adc.start_conversion();
//! ```
//!
//! ## External trigger
//!
//! A common mistake on STM forums is enabling continuous mode but that causes it to start
//! capturing on the first trigger and capture as fast as possible forever, regardless of
//! future triggers. Continuous mode is disabled by default but I thought it was worth
//! highlighting.
//!
//! Getting the timer config right to make sure it's sending the event the ADC is listening
//! to can be a bit of a pain but the key fields are highlighted below. Try hooking a timer
//! channel up to an external pin with an LED or oscilloscope attached to check it's really
//! generating pulses if the ADC doesn't seem to be triggering.
//! ```
//! use stm32f4xx_hal::{
//! gpio::gpioa,
//! adc::{
//! Adc,
//! config::{AdcConfig, SampleTime, Sequence, Eoc, Scan, Clock},
//! },
//! };
//!
//! let config = AdcConfig::default()
//! //Set the trigger you want
//! .external_trigger(TriggerMode::RisingEdge, ExternalTrigger::Tim_1_cc_1);
//! let mut adc = Adc::adc1(device.ADC1, true, config);
//! let pa0 = gpioa.pa0.into_analog();
//! adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112);
//! //Make sure it's enabled but don't start the conversion
//! adc.enable();
//!
//! //Configure the timer
//! let mut tim = Timer::tim1(device.TIM1, 1.hz(), clocks);
//! unsafe {
//! let tim = &(*TIM1::ptr());
//!
//! //Channel 1
//! //Disable the channel before configuring it
//! tim.ccer.modify(|_, w| w.cc1e().clear_bit());
//!
//! tim.ccmr1_output().modify(|_, w| w
//! //Preload enable for channel
//! .oc1pe().set_bit()
//!
//! //Set mode for channel, the default mode is "frozen" which won't work
//! .oc1m().pwm_mode1()
//! );
//!
//! //Set the duty cycle, 0 won't work in pwm mode but might be ok in
//! //toggle mode or match mode
//! let max_duty = tim.arr.read().arr().bits() as u16;
//! tim.ccr1.modify(|_, w| w.ccr().bits(max_duty / 2));
//!
//! //Enable the channel
//! tim.ccer.modify(|_, w| w.cc1e().set_bit());
//!
//! //Enable the TIM main Output
//! tim.bdtr.modify(|_, w| w.moe().set_bit());
//! }
//! ```

#![deny(missing_docs)]

Expand Down Expand Up @@ -424,137 +544,6 @@ pub mod config {
}

/// Analog to Digital Converter
/// # Status
/// Most options relating to regular conversions are implemented. One-shot and sequences of conversions
/// have been tested and work as expected.
///
/// GPIO to channel mapping should be correct for all supported F4 devices. The mappings were taken from
/// CubeMX. The mappings are feature gated per 4xx device but there are actually sub variants for some
/// devices and some pins may be missing on some variants. The implementation has been split up and commented
/// to show which pins are available on certain device variants but currently the library doesn't enforce this.
/// To fully support the right pins would require 10+ more features for the various variants.
/// ## Todo
/// * Injected conversions
/// * Analog watchdog config
/// * Discontinuous mode
/// # Examples
/// ## One-shot conversion
/// ```
/// use stm32f4xx_hal::{
/// gpio::gpioa,
/// adc::{
/// Adc,
/// config::AdcConfig,
/// config::SampleTime,
/// },
/// };
///
/// let mut adc = Adc::adc1(device.ADC1, true, AdcConfig::default());
/// let pa3 = gpioa.pa3.into_analog();
/// let sample = adc.convert(&pa3, SampleTime::Cycles_480);
/// let millivolts = adc.sample_to_millivolts(sample);
/// info!("pa3: {}mV", millivolts);
/// ```
///
/// ## Sequence conversion
/// ```
/// use stm32f4xx_hal::{
/// gpio::gpioa,
/// adc::{
/// Adc,
/// config::AdcConfig,
/// config::SampleTime,
/// config::Sequence,
/// config::Eoc,
/// config::Scan,
/// config::Clock,
/// },
/// };
///
/// let config = AdcConfig::default()
/// //We'll either need DMA or an interrupt per conversion to convert
/// //multiple values in a sequence
/// .end_of_conversion_interrupt(Eoc::Conversion)
/// //Scan mode is also required to convert a sequence
/// .scan(Scan::Enabled)
/// //And since we're looking for one interrupt per conversion the
/// //clock will need to be fairly slow to avoid overruns breaking
/// //the sequence. If you are running in debug mode and logging in
/// //the interrupt, good luck... try setting pclk2 really low.
/// //(Better yet use DMA)
/// .clock(Clock::Pclk2_div_8);
/// let mut adc = Adc::adc1(device.ADC1, true, config);
/// let pa0 = gpioa.pa0.into_analog();
/// let pa3 = gpioa.pa3.into_analog();
/// adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112);
/// adc.configure_channel(&pa3, Sequence::Two, SampleTime::Cycles_480);
/// adc.configure_channel(&pa0, Sequence::Three, SampleTime::Cycles_112);
/// adc.start_conversion();
/// ```
///
/// ## External trigger
///
/// A common mistake on STM forums is enabling continuous mode but that causes it to start
/// capturing on the first trigger and capture as fast as possible forever, regardless of
/// future triggers. Continuous mode is disabled by default but I thought it was worth
/// highlighting.
///
/// Getting the timer config right to make sure it's sending the event the ADC is listening
/// to can be a bit of a pain but the key fields are highlighted below. Try hooking a timer
/// channel up to an external pin with an LED or oscilloscope attached to check it's really
/// generating pulses if the ADC doesn't seem to be triggering.
/// ```
/// use stm32f4xx_hal::{
/// gpio::gpioa,
/// adc::{
/// Adc,
/// config::AdcConfig,
/// config::SampleTime,
/// config::Sequence,
/// config::Eoc,
/// config::Scan,
/// config::Clock,
/// },
/// };
///
/// let config = AdcConfig::default()
/// //Set the trigger you want
/// .external_trigger(TriggerMode::RisingEdge, ExternalTrigger::Tim_1_cc_1);
/// let mut adc = Adc::adc1(device.ADC1, true, config);
/// let pa0 = gpioa.pa0.into_analog();
/// adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_112);
/// //Make sure it's enabled but don't start the conversion
/// adc.enable();
///
/// //Configure the timer
/// let mut tim = Timer::tim1(device.TIM1, 1.hz(), clocks);
/// unsafe {
/// let tim = &(*TIM1::ptr());
///
/// //Channel 1
/// //Disable the channel before configuring it
/// tim.ccer.modify(|_, w| w.cc1e().clear_bit());
///
/// tim.ccmr1_output().modify(|_, w| w
/// //Preload enable for channel
/// .oc1pe().set_bit()
///
/// //Set mode for channel, the default mode is "frozen" which won't work
/// .oc1m().pwm_mode1()
/// );
///
/// //Set the duty cycle, 0 won't work in pwm mode but might be ok in
/// //toggle mode or match mode
/// let max_duty = tim.arr.read().arr().bits() as u16;
/// tim.ccr1.modify(|_, w| w.ccr().bits(max_duty / 2));
///
/// //Enable the channel
/// tim.ccer.modify(|_, w| w.cc1e().set_bit());
///
/// //Enable the TIM main Output
/// tim.bdtr.modify(|_, w| w.moe().set_bit());
/// }
/// ```
#[derive(Clone, Copy)]
pub struct Adc<ADC> {
/// Current config of the ADC, kept up to date by the various set methods
Expand Down

0 comments on commit 3756450

Please sign in to comment.