From 37564505e7a9e1890bbf9d4d36803c60e43f5cf9 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sun, 2 Jul 2023 18:05:08 +0300 Subject: [PATCH] move adc docs on module level --- src/adc.rs | 253 +++++++++++++++++++++++++---------------------------- 1 file changed, 121 insertions(+), 132 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 167a7bf4..428c1c53 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -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)] @@ -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 { /// Current config of the ADC, kept up to date by the various set methods