diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 333c9aa5..69de92ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,6 @@ jobs: device: - stm32g431 - stm32g441 - - stm32g471 - stm32g473 - stm32g474 - stm32g483 @@ -28,8 +27,8 @@ jobs: - log-rtt,defmt # TODO: -log-rtt # log-rtt without defmt, more combos? - log-itm - - log-semihost - - cordic,log-rtt,defmt + - log-semihost,can + - cordic,usb,log-rtt,defmt steps: - uses: actions/checkout@v2 diff --git a/.vscode/settings.json b/.vscode/settings.json index 535b9ec0..5ecc8746 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,8 @@ ], "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", "rust-analyzer.cargo.features": [ - "stm32g473", + "stm32g484", "defmt", + "cordic" ] -} \ No newline at end of file +} diff --git a/Cargo.toml b/Cargo.toml index 6f56daa7..a36ece64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,6 @@ version = "0.0.2" nb = "1" stm32g4 = { version = "0.22.0", package = "stm32g4-staging" } paste = "1.0" -bitflags = "1.2" -vcell = "0.1" -static_assertions = "1.1" fugit = "0.3.7" stm32-usbd = { version = "0.7.0", optional = true } fixed = { version = "1.28.0", optional = true } @@ -30,15 +27,12 @@ features = ["critical-section-single-core"] [dependencies.fdcan] version = "0.2.1" features = ["fdcan_g0_g4_l5"] +optional = true [dependencies.cast] version = "0.2.7" default-features = false -[dependencies.bare-metal] -features = ["const-fn"] -version = "0.2.5" - [dependencies.embedded-hal-old] package = "embedded-hal" features = ["unproven"] @@ -54,12 +48,8 @@ version = "0.1.2" default-features = false version = "1.0.2" -[dependencies.stable_deref_trait] -default-features = false -version = "1.1" - [dependencies.defmt] -version = "0.3.2" +version = "1" optional = true [dev-dependencies] @@ -89,13 +79,12 @@ rt = ["stm32g4/rt"] usb = ["dep:stm32-usbd"] stm32g431 = ["stm32g4/stm32g431"] stm32g441 = ["stm32g4/stm32g441"] -stm32g471 = ["stm32g4/stm32g471"] -stm32g473 = ["stm32g4/stm32g473"] -stm32g474 = ["stm32g4/stm32g474"] -stm32g483 = ["stm32g4/stm32g483"] -stm32g484 = ["stm32g4/stm32g484"] -stm32g491 = ["stm32g4/stm32g491"] -stm32g4a1 = ["stm32g4/stm32g4a1"] +stm32g473 = ["stm32g4/stm32g473", "adc3", "adc4", "adc5"] +stm32g474 = ["stm32g4/stm32g474", "adc3", "adc4", "adc5"] +stm32g483 = ["stm32g4/stm32g483", "adc3", "adc4", "adc5"] +stm32g484 = ["stm32g4/stm32g484", "adc3", "adc4", "adc5"] +stm32g491 = ["stm32g4/stm32g491", "adc3"] +stm32g4a1 = ["stm32g4/stm32g4a1", "adc3"] log-itm = ["cortex-m-log/itm"] log-rtt = [] log-semihost = ["cortex-m-log/semihosting"] @@ -107,6 +96,10 @@ defmt = [ "embedded-io/defmt-03", ] cordic = ["dep:fixed"] +adc3 = [] +adc4 = [] +adc5 = [] +can = ["dep:fdcan"] [profile.dev] codegen-units = 1 @@ -127,6 +120,10 @@ codegen-units = 1 incremental = false lto = true +[[example]] +name = "can-echo" +required-features = ["can"] + [[example]] name = "flash_with_rtic" required-features = ["stm32g474"] diff --git a/README.md b/README.md index 99e51882..0389916e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,14 @@ probe-rs chip list For furher information, see the documentation for [probe-rs](https://github.com/probe-rs/probe-rs). +#### Running tests on a Nucleo-G474RE + +First move jumper from CN12 and put it between A1 and A2 on CN8 + +```bash +cargo test --features stm32g474,defmt,cordic --tests -- --chip stm32g474RETx +``` + ### Using as a Dependency When using this crate as a dependency in your project, the microcontroller can diff --git a/examples/adc-continious-dma.rs b/examples/adc-continious-dma.rs index c3ffbac5..088382e0 100644 --- a/examples/adc-continious-dma.rs +++ b/examples/adc-continious-dma.rs @@ -8,7 +8,7 @@ use crate::hal::{ adc::{ config, config::{Continuous, Dma as AdcDma, SampleTime, Sequence}, - AdcClaim, ClockSource, Temperature, Vref, + AdcClaim, Vref, }, delay::SYSTDelayExt, dma::{channel::DMAExt, config::DmaConfig, TransferExt}, @@ -18,7 +18,10 @@ use crate::hal::{ signature::{VrefCal, VDDA_CALIB}, stm32::Peripherals, }; -use stm32g4xx_hal as hal; +use stm32g4xx_hal::{ + self as hal, + adc::{temperature::Temperature, AdcCommonExt}, +}; use cortex_m_rt::entry; @@ -49,12 +52,11 @@ fn main() -> ! { info!("Setup Adc1"); let mut delay = cp.SYST.delay(&rcc.clocks); - let mut adc = dp - .ADC1 - .claim(ClockSource::SystemClock, &rcc, &mut delay, true); + let mut adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim(dp.ADC1, &mut delay); - adc.enable_temperature(&dp.ADC12_COMMON); - adc.enable_vref(&dp.ADC12_COMMON); + adc12_common.enable_temperature(); + adc12_common.enable_vref(); adc.set_continuous(Continuous::Continuous); adc.reset_sequence(); adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_640_5); diff --git a/examples/adc-continious.rs b/examples/adc-continious.rs index 53f12e1a..b44b4755 100644 --- a/examples/adc-continious.rs +++ b/examples/adc-continious.rs @@ -4,7 +4,7 @@ use crate::hal::{ adc::{ config::{Continuous, Resolution, SampleTime, Sequence}, - AdcClaim, ClockSource, Temperature, Vref, + AdcClaim, Vref, }, delay::SYSTDelayExt, gpio::GpioExt, @@ -13,7 +13,10 @@ use crate::hal::{ signature::{VrefCal, VDDA_CALIB}, stm32::Peripherals, }; -use stm32g4xx_hal as hal; +use stm32g4xx_hal::{ + self as hal, + adc::{temperature::Temperature, AdcCommonExt}, +}; use cortex_m_rt::entry; @@ -43,12 +46,11 @@ fn main() -> ! { info!("Setup Adc1"); let mut delay = cp.SYST.delay(&rcc.clocks); - let mut adc = dp - .ADC1 - .claim(ClockSource::SystemClock, &rcc, &mut delay, true); + let mut adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim(dp.ADC1, &mut delay); - adc.enable_temperature(&dp.ADC12_COMMON); - adc.enable_vref(&dp.ADC12_COMMON); + adc12_common.enable_temperature(); + adc12_common.enable_vref(); adc.set_auto_delay(true); adc.set_continuous(Continuous::Continuous); adc.reset_sequence(); diff --git a/examples/adc-one-shot-dma.rs b/examples/adc-one-shot-dma.rs index 8a0eab7e..13b91368 100644 --- a/examples/adc-one-shot-dma.rs +++ b/examples/adc-one-shot-dma.rs @@ -6,7 +6,7 @@ use cortex_m_rt::entry; use crate::hal::{ adc::{ config::{Continuous, Dma as AdcDma, Resolution, SampleTime, Sequence}, - AdcClaim, ClockSource, Temperature, + AdcClaim, }, delay::SYSTDelayExt, dma::{channel::DMAExt, config::DmaConfig, TransferExt}, @@ -15,7 +15,10 @@ use crate::hal::{ rcc::{Config, RccExt}, stm32::Peripherals, }; -use stm32g4xx_hal as hal; +use stm32g4xx_hal::{ + self as hal, + adc::{temperature::Temperature, AdcCommonExt}, +}; #[macro_use] mod utils; @@ -49,11 +52,10 @@ fn main() -> ! { info!("Setup Adc1"); let mut delay = cp.SYST.delay(&rcc.clocks); - let mut adc = dp - .ADC1 - .claim(ClockSource::SystemClock, &rcc, &mut delay, true); + let mut adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim(dp.ADC1, &mut delay); - adc.enable_temperature(&dp.ADC12_COMMON); + adc12_common.enable_temperature(); adc.set_continuous(Continuous::Single); adc.reset_sequence(); adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_640_5); diff --git a/examples/adc-one-shot.rs b/examples/adc-one-shot.rs index 43f36ecb..388b6757 100644 --- a/examples/adc-one-shot.rs +++ b/examples/adc-one-shot.rs @@ -8,7 +8,7 @@ use crate::hal::{ stm32::Peripherals, }; use hal::prelude::*; -use stm32g4xx_hal as hal; +use stm32g4xx_hal::{self as hal, adc::AdcCommonExt}; use cortex_m_rt::entry; @@ -33,12 +33,11 @@ fn main() -> ! { info!("Setup Adc1"); let mut delay = cp.SYST.delay(&rcc.clocks); - let mut adc = dp.ADC2.claim_and_configure( - stm32g4xx_hal::adc::ClockSource::SystemClock, - &rcc, + let adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim_and_configure( + dp.ADC2, stm32g4xx_hal::adc::config::AdcConfig::default(), &mut delay, - false, ); info!("Setup Gpio"); diff --git a/examples/observe.rs b/examples/observe.rs index 500aa466..c933c6e3 100644 --- a/examples/observe.rs +++ b/examples/observe.rs @@ -17,7 +17,12 @@ use hal::{ stm32, }; use rt::entry; -use stm32g4xx_hal::{self as hal, adc::config::SampleTime, delay::DelayExt as _, stasis::Freeze}; +use stm32g4xx_hal::{ + self as hal, + adc::{config::SampleTime, AdcCommonExt}, + delay::DelayExt as _, + stasis::Freeze, +}; #[entry] fn main() -> ! { @@ -40,12 +45,11 @@ fn main() -> ! { let _comp1 = comp1.enable(); // <-- TODO: Do things with comparator let mut delay = cp.SYST.delay(&rcc.clocks); - let mut adc = dp.ADC1.claim_and_configure( - stm32g4xx_hal::adc::ClockSource::SystemClock, - &rcc, + let adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim_and_configure( + dp.ADC1, stm32g4xx_hal::adc::config::AdcConfig::default(), &mut delay, - false, ); // Can not reconfigure pa1 here diff --git a/examples/opamp.rs b/examples/opamp.rs index 37fa8925..79645b08 100644 --- a/examples/opamp.rs +++ b/examples/opamp.rs @@ -4,7 +4,7 @@ #![no_main] use stm32g4xx_hal::adc::AdcClaim; -use stm32g4xx_hal::adc::ClockSource; +use stm32g4xx_hal::adc::AdcCommonExt; use stm32g4xx_hal::opamp::Gain; use stm32g4xx_hal::prelude::*; use stm32g4xx_hal::pwr::PwrExt; @@ -66,9 +66,8 @@ fn main() -> ! { let opamp2 = opamp2.lock(); let mut delay = cp.SYST.delay(&rcc.clocks); - let mut adc = dp - .ADC2 - .claim(ClockSource::SystemClock, &rcc, &mut delay, true); + let adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim(dp.ADC2, &mut delay); loop { // Here we can sample the output of opamp2 as if it was a regular AD pin diff --git a/src/adc.rs b/src/adc.rs deleted file mode 100644 index 74b0cc4a..00000000 --- a/src/adc.rs +++ /dev/null @@ -1,2923 +0,0 @@ -//! Analog to digital converter configuration. -//! - -#![deny(missing_docs)] - -/* - Currently unused but this is the formula for using temperature calibration: - Temperature in °C = ( ( (TS_CAL2_TEMP-TS_CAL1_TEMP) / (TS_CAL2-TS_CAL1) ) * (TS_DATA-TS_CAL1) ) + 30°C -*/ - -//use crate::dma::traits::PeriAddress; -use crate::stasis; -pub use crate::time::U32Ext as _; -use crate::{ - dma::{mux::DmaMuxResources, traits::TargetAddress, PeripheralToMemory}, - gpio::*, - opamp::{self, InternalOutput}, - rcc::{Enable, Rcc, Reset}, - signature::{VtempCal130, VtempCal30, VDDA_CALIB}, - stm32, -}; -use core::fmt; -use core::marker::PhantomData; -use embedded_hal::delay::DelayNs; -use embedded_hal_old::adc::{Channel, OneShot}; - -use self::config::ExternalTrigger12; - -#[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", -))] -use self::config::ExternalTrigger345; - -/// Vref internal signal, used for calibration -pub struct Vref; -impl Vref { - /// Converts a sample value to millivolts using calibrated VDDA and configured resolution - #[inline(always)] - pub fn sample_to_millivolts_ext(sample: u16, vdda: u32, resolution: config::Resolution) -> u16 { - let mx_s = resolution.to_max_sample(); - ((u32::from(sample) * vdda) / mx_s) as u16 - } - /// Converts a sample value to millivolts using calibrated VDDA and configured resolution - #[inline(always)] - pub fn sample_to_millivolts(sample: u16) -> u16 { - Self::sample_to_millivolts_ext(sample, VDDA_CALIB, config::Resolution::Twelve) - } -} -impl stasis::Freeze for Vref {} - -/// Vbat internal signal, used for monitoring the battery (if used) -pub struct Vbat; -impl stasis::Freeze for Vbat {} - -/// Core temperature internal signal -pub struct Temperature; -impl stasis::Freeze for Temperature {} - -impl Temperature { - /// Precompute the inverse of `VTEMP_CAL_VREFANALOG`, in volts, - /// for floating point calculations - const INV_VREFANALOG_VOLTS: f32 = 1000. / VDDA_CALIB as f32; - /// Temperature at which temperature sensor has been calibrated in production - /// for data into [`VtempCal30`] (tolerance: +-5 DegC) (unit: DegC). - const VTEMP_CAL_T30: u16 = 30; - /// Temperature at which temperature sensor has been calibrated in production - /// for data into [`VtempCal130`] (tolerance: +-5 DegC) (unit: DegC). - const VTEMP_CAL_T130: u16 = 130; - - /// Convert a sample to 12 bits. Reference voltages were captured at 12 bits. - const fn to_12b(sample: u16, resolution: config::Resolution) -> u16 { - match resolution { - config::Resolution::Six => sample << 6, - config::Resolution::Eight => sample << 4, - config::Resolution::Ten => sample << 2, - config::Resolution::Twelve => sample, - } - } - - /// Convert a raw sample from `Temperature` to deg C. - /// - /// ## Arguments - /// * `sample`: ADC sample taken on the [`Temperature`] channel. - /// * `vdda`: Analog reference voltage (vref+) when the temperature - /// sample was taken, in volts. - /// * `resolution`: Configured ADC resolution. - #[inline(always)] - pub fn temperature_to_degrees_centigrade( - sample: u16, - vdda: f32, - resolution: config::Resolution, - ) -> f32 { - // Reference measurements were taken at 12 bits - let sample_12b = Self::to_12b(sample, resolution); - - // Normalize for the difference in VDDA - let sample_normalized = sample_12b as f32 * (vdda * Self::INV_VREFANALOG_VOLTS); - - ((sample_normalized - VtempCal30::get().read() as f32) - * ((Self::VTEMP_CAL_T130 - Self::VTEMP_CAL_T30) as f32)) - / ((VtempCal130::get().read() - VtempCal30::get().read()) as f32) - + Self::VTEMP_CAL_T30 as f32 - } - - /// Convert a raw sample from `Temperature` to deg C - /// - /// ## Arguments - /// * `sample`: ADC sample taken on the [`Temperature`] channel. - /// * `vdda`: Analog reference voltage (vref+) when the temperature - /// sample was taken, in millivolts. - /// * `resolution`: Configured ADC resolution. - #[inline(always)] - pub fn temperature_to_degrees_centigrade_coarse( - sample: u16, - vdda: u32, - resolution: config::Resolution, - ) -> i16 { - // Reference measurements were taken at 12 bits - let sample_12b = Self::to_12b(sample, resolution); - - // Normalize for the difference in VDDA - let sample_normalized = ((sample_12b as u32 * vdda) / VDDA_CALIB) as u16; - - let t = ((sample_normalized as i32 - VtempCal30::get().read() as i32) - * ((Self::VTEMP_CAL_T130 - Self::VTEMP_CAL_T30) as i32)) - / ((VtempCal130::get().read() - VtempCal30::get().read()) as i32) - + Self::VTEMP_CAL_T30 as i32; - - t as i16 - } -} - -// TODO: Is there any way to avoid this wrapper to overcome the orphan rule -/// Wrapper to side step orphan rule -pub struct Ad(T); -macro_rules! adc_channel_helper { - ($adc:ident, $chan:expr, $r:ty, $($a:ident),*) => { - impl<$($a,)*> Channel> for stasis::Entitlement<$r> { - type ID = u8; - fn channel() -> u8 { - $chan - } - } - - impl Channel> for stasis::Frozen<$r, N> { - type ID = u8; - fn channel() -> u8 { - $chan - } - } - - impl<$($a,)*> Channel> for $r { - type ID = u8; - fn channel() -> u8 { - $chan - } - } - }; -} - -macro_rules! adc_pins { - ($($pin:ty => ($adc:ident, $chan:expr)),+ $(,)*) => { - $( - adc_channel_helper!($adc, $chan, $pin,); - )+ - }; -} - -macro_rules! adc_opamp { - ($($opamp:ty => ($adc:ident, $chan:expr)),+ $(,)*) => { - $( - adc_channel_helper!($adc, $chan, opamp::Follower<$opamp, A, InternalOutput>, A); - adc_channel_helper!($adc, $chan, opamp::OpenLoop<$opamp, A, B, InternalOutput>, A, B); - adc_channel_helper!($adc, $chan, opamp::Pga<$opamp, A, InternalOutput>, A); - adc_channel_helper!($adc, $chan, opamp::Locked<$opamp, InternalOutput>,); - )+ - }; -} - -/// Contains types related to ADC configuration -pub mod config { - use embedded_hal_old::adc::Channel; - - /// The place in the sequence a given channel should be captured - #[derive(Debug, PartialEq, PartialOrd, Copy, Clone)] - pub enum Sequence { - /// 1 - One, - /// 2 - Two, - /// 3 - Three, - /// 4 - Four, - /// 5 - Five, - /// 6 - Six, - /// 7 - Seven, - /// 8 - Eight, - /// 9 - Nine, - /// 10 - Ten, - /// 11 - Eleven, - /// 12 - Twelve, - /// 13 - Thirteen, - /// 14 - Fourteen, - /// 15 - Fifteen, - /// 16 - Sixteen, - } - - impl From for u8 { - fn from(s: Sequence) -> u8 { - match s { - Sequence::One => 0, - Sequence::Two => 1, - Sequence::Three => 2, - Sequence::Four => 3, - Sequence::Five => 4, - Sequence::Six => 5, - Sequence::Seven => 6, - Sequence::Eight => 7, - Sequence::Nine => 8, - Sequence::Ten => 9, - Sequence::Eleven => 10, - Sequence::Twelve => 11, - Sequence::Thirteen => 12, - Sequence::Fourteen => 13, - Sequence::Fifteen => 14, - Sequence::Sixteen => 15, - } - } - } - - impl From for Sequence { - fn from(bits: u8) -> Self { - match bits { - 0 => Sequence::One, - 1 => Sequence::Two, - 2 => Sequence::Three, - 3 => Sequence::Four, - 4 => Sequence::Five, - 5 => Sequence::Six, - 6 => Sequence::Seven, - 7 => Sequence::Eight, - 8 => Sequence::Nine, - 9 => Sequence::Ten, - 10 => Sequence::Eleven, - 11 => Sequence::Twelve, - 12 => Sequence::Thirteen, - 13 => Sequence::Fourteen, - 14 => Sequence::Fifteen, - 15 => Sequence::Sixteen, - _ => unimplemented!(), - } - } - } - - /// The number of cycles to sample a given channel for - #[derive(Debug, PartialEq, Copy, Clone)] - pub enum SampleTime { - /// 2.5 cycles - Cycles_2_5, - /// 6.5 cycles - Cycles_6_5, - /// 12_5 cycles - Cycles_12_5, - /// 24.5 cycles - Cycles_24_5, - /// 47.5 cycles - Cycles_47_5, - /// 92.5 cycles - Cycles_92_5, - /// 247.5 cycles - Cycles_247_5, - /// 640.5 cycles - Cycles_640_5, - } - - impl From for SampleTime { - fn from(f: u8) -> SampleTime { - match f { - 0 => SampleTime::Cycles_2_5, - 1 => SampleTime::Cycles_6_5, - 2 => SampleTime::Cycles_12_5, - 3 => SampleTime::Cycles_24_5, - 4 => SampleTime::Cycles_47_5, - 5 => SampleTime::Cycles_92_5, - 6 => SampleTime::Cycles_247_5, - 7 => SampleTime::Cycles_640_5, - _ => unimplemented!(), - } - } - } - - impl From for u8 { - fn from(l: SampleTime) -> u8 { - match l { - SampleTime::Cycles_2_5 => 0, - SampleTime::Cycles_6_5 => 1, - SampleTime::Cycles_12_5 => 2, - SampleTime::Cycles_24_5 => 3, - SampleTime::Cycles_47_5 => 4, - SampleTime::Cycles_92_5 => 5, - SampleTime::Cycles_247_5 => 6, - SampleTime::Cycles_640_5 => 7, - } - } - } - - /// ClockMode config for the ADC - /// Check the datasheet for the maximum speed the ADC supports - #[derive(Debug, Clone, Copy)] - pub enum ClockMode { - /// (Asynchronous clock mode), adc_ker_ck. generated at product level (refer to Section 6: Reset and clock control (RCC) - Asynchronous, - /// (Synchronous clock mode). adc_hclk/1 - /// This configuration must be enabled only if the AHB clock prescaler is set to 1 (HPRE\[3:0\] = 0xxx in RCC_CFGR register) and if the system clock has a 50% duty cycle. - Synchronous_Div_1, - /// Synchronous clock mode. adc_hclk/2 - Synchronous_Div_2, - /// Synchronous clock mode. adc_hclk/4 - Synchronous_Div_4, - } - - impl From for u8 { - fn from(c: ClockMode) -> u8 { - match c { - ClockMode::Asynchronous => 0b00, - ClockMode::Synchronous_Div_1 => 0b001, - ClockMode::Synchronous_Div_2 => 0b010, - ClockMode::Synchronous_Div_4 => 0b011, - } - } - } - - impl From for ClockMode { - fn from(b: u8) -> ClockMode { - match b { - 0b000 => ClockMode::Asynchronous, - 0b001 => ClockMode::Synchronous_Div_1, - 0b010 => ClockMode::Synchronous_Div_2, - 0b011 => ClockMode::Synchronous_Div_4, - _ => unimplemented!(), - } - } - } - - /// Clock config for the ADC - /// Check the datasheet for the maximum speed the ADC supports - #[derive(Debug, Clone, Copy)] - pub enum Clock { - /// Clock not divided - Div_1, - /// Clock divided by 2 - Div_2, - /// Clock divided by 4 - Div_4, - /// Clock divided by 6 - Div_6, - /// Clock divided by 8 - Div_8, - /// Clock divided by 10 - Div_10, - /// Clock divided by 12 - Div_12, - /// Clock divided by 16 - Div_16, - /// Clock divided by 32 - Div_32, - /// Clock divided by 64 - Div_64, - /// Clock divided by 128 - Div_128, - /// Clock divided by 256 - Div_256, - } - - impl From for u8 { - fn from(c: Clock) -> u8 { - match c { - Clock::Div_1 => 0b000, - Clock::Div_2 => 0b001, - Clock::Div_4 => 0b010, - Clock::Div_6 => 0b011, - Clock::Div_8 => 0b100, - Clock::Div_10 => 0b101, - Clock::Div_12 => 0b110, - Clock::Div_16 => 0b111, - Clock::Div_32 => 0b1000, - Clock::Div_64 => 0b1001, - Clock::Div_128 => 0b1010, - Clock::Div_256 => 0b1011, - } - } - } - - impl From for Clock { - fn from(b: u8) -> Clock { - match b { - 0b000 => Clock::Div_1, - 0b001 => Clock::Div_2, - 0b010 => Clock::Div_4, - 0b011 => Clock::Div_6, - 0b100 => Clock::Div_8, - 0b101 => Clock::Div_10, - 0b110 => Clock::Div_12, - 0b111 => Clock::Div_16, - 0b1000 => Clock::Div_32, - 0b1001 => Clock::Div_64, - 0b1010 => Clock::Div_128, - 0b1011 => Clock::Div_256, - _ => unimplemented!(), - } - } - } - - /// Resolution to sample at - #[derive(Debug, Clone, Copy)] - pub enum Resolution { - /// 12-bit - Twelve, - /// 10-bit - Ten, - /// 8-bit - Eight, - /// 6-bit - Six, - } - impl Resolution { - /// Return the maximum value of a sample with the given Resolution - pub fn to_max_sample(self) -> u32 { - match self { - Resolution::Twelve => (1 << 12) - 1, - Resolution::Ten => (1 << 10) - 1, - Resolution::Eight => (1 << 8) - 1, - Resolution::Six => (1 << 6) - 1, - } - } - } - impl From for u8 { - fn from(r: Resolution) -> u8 { - match r { - Resolution::Twelve => 0b00, - Resolution::Ten => 0b01, - Resolution::Eight => 0b10, - Resolution::Six => 0b11, - } - } - } - impl From for Resolution { - fn from(r: u8) -> Resolution { - match r { - 0b00 => Resolution::Twelve, - 0b01 => Resolution::Ten, - 0b10 => Resolution::Eight, - 0b11 => Resolution::Six, - _ => unimplemented!(), - } - } - } - - /// Possible external triggers the ADC can listen to - /// - /// This applies to ADC3, ADC4 and ADC5 - #[derive(Debug, Clone, Copy, Default)] - pub enum ExternalTrigger12 { - /// TIM1 compare channel 1 - #[default] - Tim_1_cc_1, - /// TIM1 compare channel 2 - Tim_1_cc_2, - /// TIM1 compare channel 3 - Tim_1_cc_3, - /// TIM2 compare channel 2 - Tim_2_cc_2, - /// TIM3 trigger out - Tim_3_trgo, - /// TIM4 compare channel 4 - Tim_4_cc_4, - /// External interupt line 11 - Exti_11, - /// TIM8 trigger out - Tim_8_trgo, - /// TIM8 trigger out 2 - Tim_8_trgo_2, - /// TIM1 trigger out - Tim_1_trgo, - /// TIM1 trigger out 2 - Tim_1_trgo_2, - /// TIM2 trigger out - Tim_2_trgo, - /// TIM4 trigger out - Tim_4_trgo, - /// TIM6 trigger out - Tim_6_trgo, - /// TIM15 trigger out - Tim_15_trgo, - /// TIM3 compare channel 4 - Tim_3_cc_4, - /// TIM20 trigger out - Tim_20_trgo, - /// TIM20 trigger out 2 - Tim_20_trgo_2, - /// TIM20 compare channel 1 - Tim_20_cc_1, - /// TIM20 compare channel 2 - Tim_20_cc_2, - /// TIM20 compare channel 3 - Tim_20_cc_3, - /// hrtim_adc_trg1 - Hrtim_adc_trg_1, - /// hrtim_adc_trg3 - Hrtim_adc_trg_3, - /// hrtim_adc_trg5 - Hrtim_adc_trg_5, - /// hrtim_adc_trg6 - Hrtim_adc_trg_6, - /// hrtim_adc_trg7 - Hrtim_adc_trg_7, - /// hrtim_adc_trg8 - Hrtim_adc_trg_8, - /// hrtim_adc_trg9 - Hrtim_adc_trg_9, - /// hrtim_adc_trg10 - Hrtim_adc_trg_10, - /// LP_timeout - Lp_timeout, - /// TIM7 trigger out - Tim_7_trgo, - } - - /// Possible external triggers the ADC can listen to - /// - /// This applies to ADC3, ADC4 and ADC5 - /// - #[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", - ))] - #[derive(Debug, Clone, Copy, Default)] - pub enum ExternalTrigger345 { - /// TIM3 compare channel 1 - #[default] - Tim_3_cc_1, - /// TIM2 compare channel 3 - Tim_2_cc_3, - /// TIM1 compare channel 3 - Tim_1_cc_3, - /// TIM8 compare channel 1 - Tim_8_cc_1, - /// TIM3 trigger out - Tim_3_trgo, - /// External interupt line 2 - Exti_2, - /// TIM4 compare channel 1 - Tim_4_cc_1, - /// TIM8 trigger out - Tim_8_trgo, - /// TIM8 trigger out 2 - Tim_8_trgo_2, - /// TIM1 trigger out - Tim_1_trgo, - /// TIM1 trigger out 2 - Tim_1_trgo_2, - /// TIM2 trigger out - Tim_2_trgo, - /// TIM4 trigger out - Tim_4_trgo, - /// TIM6 trigger out - Tim_6_trgo, - /// TIM15 trigger out - Tim_15_trgo, - /// TIM2 compare channel 1 - Tim_2_cc_1, - /// TIM20 trigger out - Tim_20_trgo, - /// TIM20 trigger out 2 - Tim_20_trgo_2, - /// TIM20 compare channel 1 - Tim_20_cc_1, - /// hrtim_adc_trg2 - Hrtim_adc_trg_2, - /// hrtim_adc_trg4 - Hrtim_adc_trg_4, - /// hrtim_adc_trg1 - Hrtim_adc_trg_1, - /// hrtim_adc_trg3 - Hrtim_adc_trg_3, - /// hrtim_adc_trg5 - Hrtim_adc_trg_5, - /// hrtim_adc_trg6 - Hrtim_adc_trg_6, - /// hrtim_adc_trg7 - Hrtim_adc_trg_7, - /// hrtim_adc_trg8 - Hrtim_adc_trg_8, - /// hrtim_adc_trg9 - Hrtim_adc_trg_9, - /// hrtim_adc_trg10 - Hrtim_adc_trg_10, - /// LP_timeout - Lp_timeout, - /// TIM7 trigger out - Tim_7_trgo, - } - - impl From for u8 { - fn from(et: ExternalTrigger12) -> u8 { - match et { - ExternalTrigger12::Tim_1_cc_1 => 0b00000, - ExternalTrigger12::Tim_1_cc_2 => 0b00001, - ExternalTrigger12::Tim_1_cc_3 => 0b00010, - ExternalTrigger12::Tim_2_cc_2 => 0b00011, - ExternalTrigger12::Tim_3_trgo => 0b00100, - ExternalTrigger12::Tim_4_cc_4 => 0b00101, - ExternalTrigger12::Exti_11 => 0b00110, - ExternalTrigger12::Tim_8_trgo => 0b00111, - ExternalTrigger12::Tim_8_trgo_2 => 0b01000, - ExternalTrigger12::Tim_1_trgo => 0b01001, - ExternalTrigger12::Tim_1_trgo_2 => 0b01010, - ExternalTrigger12::Tim_2_trgo => 0b01011, - ExternalTrigger12::Tim_4_trgo => 0b01100, - ExternalTrigger12::Tim_6_trgo => 0b01101, - ExternalTrigger12::Tim_15_trgo => 0b01110, - ExternalTrigger12::Tim_3_cc_4 => 0b01111, - ExternalTrigger12::Tim_20_trgo => 0b10000, - ExternalTrigger12::Tim_20_trgo_2 => 0b10001, - ExternalTrigger12::Tim_20_cc_1 => 0b10010, - ExternalTrigger12::Tim_20_cc_2 => 0b10011, - ExternalTrigger12::Tim_20_cc_3 => 0b10100, - ExternalTrigger12::Hrtim_adc_trg_1 => 0b10101, - ExternalTrigger12::Hrtim_adc_trg_3 => 0b10110, - ExternalTrigger12::Hrtim_adc_trg_5 => 0b10111, - ExternalTrigger12::Hrtim_adc_trg_6 => 0b11000, - ExternalTrigger12::Hrtim_adc_trg_7 => 0b11001, - ExternalTrigger12::Hrtim_adc_trg_8 => 0b11010, - ExternalTrigger12::Hrtim_adc_trg_9 => 0b11011, - ExternalTrigger12::Hrtim_adc_trg_10 => 0b11100, - ExternalTrigger12::Lp_timeout => 0b11101, - ExternalTrigger12::Tim_7_trgo => 0b11110, - // Reserved => 0b11111 - } - } - } - - #[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", - ))] - impl From for u8 { - fn from(et: ExternalTrigger345) -> u8 { - match et { - ExternalTrigger345::Tim_3_cc_1 => 0b00000, - ExternalTrigger345::Tim_2_cc_3 => 0b00001, - ExternalTrigger345::Tim_1_cc_3 => 0b00010, - ExternalTrigger345::Tim_8_cc_1 => 0b00011, - ExternalTrigger345::Tim_3_trgo => 0b00100, - ExternalTrigger345::Exti_2 => 0b00101, - ExternalTrigger345::Tim_4_cc_1 => 0b00110, - ExternalTrigger345::Tim_8_trgo => 0b00111, - ExternalTrigger345::Tim_8_trgo_2 => 0b01000, - ExternalTrigger345::Tim_1_trgo => 0b01001, - ExternalTrigger345::Tim_1_trgo_2 => 0b01010, - ExternalTrigger345::Tim_2_trgo => 0b01011, - ExternalTrigger345::Tim_4_trgo => 0b01100, - ExternalTrigger345::Tim_6_trgo => 0b01101, - ExternalTrigger345::Tim_15_trgo => 0b01110, - ExternalTrigger345::Tim_2_cc_1 => 0b01111, - ExternalTrigger345::Tim_20_trgo => 0b10000, - ExternalTrigger345::Tim_20_trgo_2 => 0b10001, - ExternalTrigger345::Tim_20_cc_1 => 0b10010, - ExternalTrigger345::Hrtim_adc_trg_2 => 0b10011, - ExternalTrigger345::Hrtim_adc_trg_4 => 0b10100, - ExternalTrigger345::Hrtim_adc_trg_1 => 0b10101, - ExternalTrigger345::Hrtim_adc_trg_3 => 0b10110, - ExternalTrigger345::Hrtim_adc_trg_5 => 0b10111, - ExternalTrigger345::Hrtim_adc_trg_6 => 0b11000, - ExternalTrigger345::Hrtim_adc_trg_7 => 0b11001, - ExternalTrigger345::Hrtim_adc_trg_8 => 0b11010, - ExternalTrigger345::Hrtim_adc_trg_9 => 0b11011, - ExternalTrigger345::Hrtim_adc_trg_10 => 0b11100, - ExternalTrigger345::Lp_timeout => 0b11101, - ExternalTrigger345::Tim_7_trgo => 0b11110, - // Reserved => 0b11111 - } - } - } - - /// Possible oversampling shift - #[derive(Debug, Clone, Copy)] - pub enum OverSamplingShift { - /// No right shift - NoShift, - /// Shift of 1 toward the right - Shift_1, - /// Shift of 2 toward the right - Shift_2, - /// Shift of 3 toward the right - Shift_3, - /// Shift of 4 toward the right - Shift_4, - /// Shift of 5 toward the right - Shift_5, - /// Shift of 6 toward the right - Shift_6, - /// Shift of 7 toward the right - Shift_7, - /// Shift of 8 toward the right - Shift_8, - } - impl From for u8 { - fn from(oss: OverSamplingShift) -> u8 { - match oss { - OverSamplingShift::NoShift => 0, - OverSamplingShift::Shift_1 => 1, - OverSamplingShift::Shift_2 => 2, - OverSamplingShift::Shift_3 => 3, - OverSamplingShift::Shift_4 => 4, - OverSamplingShift::Shift_5 => 5, - OverSamplingShift::Shift_6 => 6, - OverSamplingShift::Shift_7 => 7, - OverSamplingShift::Shift_8 => 8, - } - } - } - - /// Possible oversampling modes - #[derive(Debug, Clone, Copy)] - pub enum OverSampling { - /// Oversampling 2x - Ratio_2, - /// Oversampling 4x - Ratio_4, - /// Oversampling 8x - Ratio_8, - /// Oversampling 16x - Ratio_16, - /// Oversampling 32x - Ratio_32, - /// Oversampling 64x - Ratio_64, - /// Oversampling 128x - Ratio_128, - /// Oversampling 256x - Ratio_256, - } - impl From for u8 { - fn from(os: OverSampling) -> u8 { - match os { - OverSampling::Ratio_2 => 0, - OverSampling::Ratio_4 => 1, - OverSampling::Ratio_8 => 2, - OverSampling::Ratio_16 => 3, - OverSampling::Ratio_32 => 4, - OverSampling::Ratio_64 => 5, - OverSampling::Ratio_128 => 6, - OverSampling::Ratio_256 => 7, - } - } - } - - /// Possible trigger modes - #[derive(Debug, Clone, Copy)] - pub enum TriggerMode { - /// Don't listen to external trigger - Disabled, - /// Listen for rising edges of external trigger - RisingEdge, - /// Listen for falling edges of external trigger - FallingEdge, - /// Listen for both rising and falling edges of external trigger - BothEdges, - } - impl From for u8 { - fn from(tm: TriggerMode) -> u8 { - match tm { - TriggerMode::Disabled => 0, - TriggerMode::RisingEdge => 1, - TriggerMode::FallingEdge => 2, - TriggerMode::BothEdges => 3, - } - } - } - - /// Data register alignment - #[derive(Debug, Clone, Copy)] - pub enum Align { - /// Right align output data - Right, - /// Left align output data - Left, - } - impl From for bool { - fn from(a: Align) -> bool { - match a { - Align::Right => false, - Align::Left => true, - } - } - } - - /// Continuous mode enable/disable - #[derive(Debug, Clone, Copy, PartialEq)] - pub enum Continuous { - /// Single mode, continuous disabled - Single, - /// Continuous mode enabled - Continuous, - - /// Discontinuous mode enabled - /// - /// Will perform `subgroup_len` number samples per trigger - Discontinuous, - } - - /// Number of channels to sample per trigger in discontinuous mode - /// - /// NOTE: This only applies to discontinuous - #[derive(Debug, Clone, Copy)] - pub enum SubGroupLength { - /// One single sample per trigger - One = 0b000, - - /// Two samples per trigger - Two = 0b001, - - /// Three samples per trigger - Three = 0b010, - - /// Four samples per trigger - Four = 0b011, - - /// Five samples per trigger - Five = 0b100, - - /// Six samples per trigger - Six = 0b101, - - /// Seven samples per trigger - Seven = 0b110, - - /// Eight samples per trigger - Eight = 0b111, - } - - /// DMA mode - #[derive(Debug, Clone, Copy)] - pub enum Dma { - /// No DMA, disabled - Disabled, - /// Single DMA, DMA will be disabled after each conversion sequence - Single, - /// Continuous DMA, DMA will remain enabled after conversion - Continuous, - } - - /// End-of-conversion interrupt enabled/disabled - #[derive(Debug, Clone, Copy)] - pub enum Eoc { - /// End-of-conversion interrupt disabled - Disabled, - /// End-of-conversion interrupt enabled per conversion - Conversion, - /// End-of-conversion interrupt enabled per sequence - Sequence, - } - - /// Input Type Selection - #[derive(Debug, Clone, Copy)] - pub enum InputType { - /// Single-Ended Input Channels - SingleEnded, - /// Differential Input Channels - Differential, - } - impl From for bool { - fn from(it: InputType) -> bool { - match it { - InputType::SingleEnded => false, - InputType::Differential => true, - } - } - } - - /// Sets the input type per channel - #[derive(Debug, Clone, Copy, Default)] - pub struct DifferentialSelection(pub(crate) u32); - impl DifferentialSelection { - /// Set pin to Single-Ended or Differential - pub fn set(&mut self, it: InputType) - where - PIN: Channel, - { - match it { - InputType::SingleEnded => { - self.singleended_by_id(PIN::channel()); - } - InputType::Differential => { - self.differential_by_id(PIN::channel()); - } - } - } - - /// Set to single ended by id - fn singleended_by_id(&mut self, id: u8) { - self.0 &= !(1 << id); - } - - /// Set to differential by id - fn differential_by_id(&mut self, id: u8) { - self.0 |= 1 << id; - } - - /// get the differential setting of channel - pub fn get_channel(&self, channel: u8) -> InputType { - if self.0 & (1 << channel) > 0 { - InputType::Differential - } else { - InputType::SingleEnded - } - } - - /// Sets all channels to SingleEnded - pub fn clear_all(&mut self) { - self.0 = 0; - } - } - - /// Configuration for the adc. - /// There are some additional parameters on the adc peripheral that can be - /// added here when needed but this covers several basic usecases. - #[derive(Debug, Clone, Copy)] - pub struct AdcConfig { - pub(crate) clock_mode: ClockMode, - pub(crate) clock: Clock, - pub(crate) resolution: Resolution, - pub(crate) align: Align, - pub(crate) external_trigger: (TriggerMode, ET), - pub(crate) continuous: Continuous, - pub(crate) subgroup_len: SubGroupLength, - pub(crate) dma: Dma, - pub(crate) end_of_conversion_interrupt: Eoc, - pub(crate) overrun_interrupt: bool, - pub(crate) default_sample_time: SampleTime, - pub(crate) vdda: Option, - pub(crate) auto_delay: bool, - - /// Sets the differential input type of the Adc - pub difsel: DifferentialSelection, - } - - impl AdcConfig { - /// change the clock_mode field - #[inline(always)] - pub fn clock_mode(mut self, clock_mode: ClockMode) -> Self { - self.clock_mode = clock_mode; - self - } - /// change the clock field - #[inline(always)] - pub fn clock(mut self, clock: Clock) -> Self { - self.clock = clock; - self - } - /// change the resolution field - #[inline(always)] - pub fn resolution(mut self, resolution: Resolution) -> Self { - self.resolution = resolution; - self - } - /// change the align field - #[inline(always)] - pub fn align(mut self, align: Align) -> Self { - self.align = align; - self - } - /// change the continuous field - #[inline(always)] - pub fn continuous(mut self, continuous: Continuous) -> Self { - self.continuous = continuous; - self - } - - /// change the subgroup_len field, only relevant in discontinous mode - pub fn subgroup_len(mut self, subgroup_len: SubGroupLength) -> Self { - self.subgroup_len = subgroup_len; - self - } - - /// change the dma field - #[inline(always)] - pub fn dma(mut self, dma: Dma) -> Self { - self.dma = dma; - self - } - /// change the end_of_conversion_interrupt field - #[inline(always)] - pub fn end_of_conversion_interrupt(mut self, end_of_conversion_interrupt: Eoc) -> Self { - self.end_of_conversion_interrupt = end_of_conversion_interrupt; - self - } - - /// Enable/disable overrun interrupt - /// - /// This is triggered when the AD finishes a conversion before the last value was read by CPU/DMA - pub fn overrun_interrupt(mut self, enable: bool) -> Self { - self.overrun_interrupt = enable; - self - } - - /// change the default_sample_time field - #[inline(always)] - pub fn default_sample_time(mut self, default_sample_time: SampleTime) -> Self { - self.default_sample_time = default_sample_time; - self - } - - /// Specify the reference voltage for the ADC. - /// - /// # Args - /// * `vdda_mv` - The ADC reference voltage in millivolts. - #[inline(always)] - pub fn reference_voltage(mut self, vdda_mv: u32) -> Self { - self.vdda = Some(vdda_mv); - self - } - - /// Specify the single-ened or differential channel selection - #[inline(always)] - pub fn difsel(mut self, df: DifferentialSelection) -> Self { - self.difsel = df; - self - } - - /// Enable of disable the auto delay function - #[inline(always)] - pub fn auto_delay(mut self, delay: bool) -> Self { - self.auto_delay = delay; - self - } - } - - impl AdcConfig { - /// change the external_trigger field - #[inline(always)] - pub fn external_trigger( - mut self, - trigger_mode: TriggerMode, - trigger: ExternalTrigger12, - ) -> Self { - self.external_trigger = (trigger_mode, trigger); - self - } - } - - #[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", - ))] - impl AdcConfig { - /// change the external_trigger field - #[inline(always)] - pub fn external_trigger( - mut self, - trigger_mode: TriggerMode, - trigger: ExternalTrigger345, - ) -> Self { - self.external_trigger = (trigger_mode, trigger); - self - } - } - - impl Default for AdcConfig { - fn default() -> Self { - Self { - clock_mode: ClockMode::Synchronous_Div_1, - clock: Clock::Div_2, - resolution: Resolution::Twelve, - align: Align::Right, - external_trigger: (TriggerMode::Disabled, ET::default()), - continuous: Continuous::Single, - subgroup_len: SubGroupLength::One, - dma: Dma::Disabled, - end_of_conversion_interrupt: Eoc::Disabled, - overrun_interrupt: false, - default_sample_time: SampleTime::Cycles_640_5, - vdda: None, - difsel: DifferentialSelection::default(), - auto_delay: false, - } - } - } -} - -/// Type-State for Adc, indicating a deep-powered-down-pheripheral -#[derive(Debug)] -pub struct PoweredDown; -/// Type-State for Adc, indicating a non-configured peripheral -#[derive(Debug)] -pub struct Disabled; -/// Type-State for Adc, indicating a configured peripheral -#[derive(Debug)] -pub struct Configured; -/// Type-State for Adc, indicating an peripheral configured for DMA -#[derive(Debug)] -pub struct DMA; -/// Type-State for Adc, indicating am active measuring peripheral -#[derive(Debug)] -pub struct Active; - -/// Enum for the wait_for_conversion_sequence function, -/// which can return either a stopped ADC typestate or a -/// continuing ADC typestate. -pub enum Conversion { - /// Contains an Active Conversion ADC - Active(Adc), - /// Contains an Stopped ADC - Stopped(Adc), -} -impl Conversion { - /// unwraps the enum and panics if the result is not an Active ADC - #[inline(always)] - pub fn unwrap_active(self) -> Adc { - match self { - Conversion::Active(adc) => adc, - _ => { - panic!("Conversion is Stopped, not Active!") - } - } - } - - /// unwraps the enum and panics if the result is not a Stopped ADC - #[inline(always)] - pub fn unwrap_stopped(self) -> Adc { - match self { - Conversion::Stopped(adc) => adc, - _ => { - panic!("Conversion is Continuing, not Stopped!") - } - } - } - - /// Returns true if the adc is Active. - #[inline(always)] - pub fn is_active(&self) -> bool { - match *self { - Conversion::Active(..) => true, - Conversion::Stopped(..) => false, - } - } - - /// Returns true if the adc is Stopped. - #[inline(always)] - pub fn is_stopped(&self) -> bool { - !self.is_active() - } - - /// Converts from `Conversion` to `Option`. - /// - /// Converts self into an `Option`, consuming self, and discarding the adc, if it is stopped. - #[inline(always)] - pub fn active(self) -> Option> { - match self { - Conversion::Active(adc) => Some(adc), - Conversion::Stopped(..) => None, - } - } - - /// Converts from `Conversion` to `Option`. - /// - /// Converts self into an `Option`, consuming self, and discarding the adc, if it is still active. - #[inline(always)] - pub fn stopped(self) -> Option> { - match self { - Conversion::Active(..) => None, - Conversion::Stopped(adc) => Some(adc), - } - } -} - -/// 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::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) -/// //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::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 DynamicAdc { - /// Current config of the ADC, kept up to date by the various set methods - config: config::AdcConfig, - /// The adc peripheral - adc_reg: ADC, - /// VDDA in millivolts calculated from the factory calibration and vrefint - calibrated_vdda: u32, -} -impl fmt::Debug for DynamicAdc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "DynamicAdc: {{ calibrated_vdda: {:?}, {:?}, ... }}", - self.calibrated_vdda, self.config - ) - } -} - -/// Typestate wrapper around DynamicAdc -pub struct Adc { - adc: DynamicAdc, - _status: PhantomData, -} -impl fmt::Debug for Adc -where - STATUS: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Adc<{:?}>: {{ calibrated_vdda: {:?}, {:?}, ... }}", - self._status, self.adc.calibrated_vdda, self.adc.config - ) - } -} - -/// ADC Clock Source selection -#[derive(Debug, Clone, Copy)] -pub enum ClockSource { - /// Use the System Clock as Clock Source - SystemClock, - /// use the Internal PLL as Clock Source - PLL_P, -} - -impl From for u8 { - fn from(c: ClockSource) -> u8 { - match c { - ClockSource::PLL_P => 0b01, - ClockSource::SystemClock => 0b10, - } - } -} - -/// used to create an ADC instance from the stm32::Adc -pub trait AdcClaim { - /// create a disabled ADC instance from the stm32::Adc - fn claim( - self, - cs: ClockSource, - rcc: &Rcc, - delay: &mut impl DelayNs, - reset: bool, - ) -> Adc; - - /// create an enabled ADC instance from the stm32::Adc - fn claim_and_configure( - self, - cs: ClockSource, - rcc: &Rcc, - config: config::AdcConfig, - delay: &mut impl DelayNs, - reset: bool, - ) -> Adc; -} - -trait AdcConfig { - fn configure_clock_source(cs: ClockSource, rcc: &Rcc); -} - -/// Specifies what External trigger type the ADC uses -pub trait TriggerType { - /// Specifies what External trigger type the ADC uses - type ExternalTrigger: fmt::Debug; -} - -#[inline(always)] -fn configure_clock_source12(cs: ClockSource, rcc: &Rcc) { - // Select system clock as ADC clock source - rcc.rb.ccipr().modify(|_, w| { - // This is sound, as `0b10` is a valid value for this field. - unsafe { - w.adc12sel().bits(cs.into()); - } - - w - }); -} - -#[inline(always)] -#[allow(dead_code)] -fn configure_clock_source345(cs: ClockSource, rcc: &Rcc) { - // Select system clock as ADC clock source - rcc.rb.ccipr().modify(|_, w| { - // This is sound, as `0b10` is a valid value for this field. - unsafe { - w.adc345sel().bits(cs.into()); - } - - w - }); -} - -macro_rules! adc { - - (vbat => ($common_type:ident)) => { - /// Enables the vbat internal channel - #[inline(always)] - pub fn enable_vbat(&self, common: &stm32::$common_type) { - self.adc.enable_vbat(common) - } - - /// Enables the vbat internal channel - #[inline(always)] - pub fn disable_vbat(&self, common: &stm32::$common_type) { - self.adc.disable_vbat(common) - } - }; - - (vbat_check => ($common_type:ident)) => { - /// Returns if the vbat internal channel is enabled - #[inline(always)] - pub fn is_vbat_enabled(&mut self, common: &stm32::$common_type) -> bool { - self.adc.is_vbat_enabled(common) - } - }; - - (vtemp => ($common_type:ident)) => { - /// Enables the temp internal channel. - #[inline(always)] - pub fn enable_temperature(&mut self, common: &stm32::$common_type) { - self.adc.enable_temperature(common) - } - - /// Disables the temp internal channel - #[inline(always)] - pub fn disable_temperature(&mut self, common: &stm32::$common_type) { - self.adc.disable_temperature(common) - } - }; - - (vtemp_check => ($common_type:ident)) => { - /// Returns if the temp internal channel is enabled - #[inline(always)] - pub fn is_temperature_enabled(&mut self, common: &stm32::$common_type) -> bool { - self.adc.is_temperature_enabled(common) - } - }; - - (vref => ($common_type:ident)) => { - /// Enables the vref internal channel. - #[inline(always)] - pub fn enable_vref(&mut self, common: &stm32::$common_type) { - self.adc.enable_vref(common) - } - - /// Disables the vref internal channel - #[inline(always)] - pub fn disable_vref(&mut self, common: &stm32::$common_type) { - self.adc.disable_vref(common) - } - }; - - (vref_check => ($common_type:ident)) => { - /// Returns if the vref internal channel is enabled - #[inline(always)] - pub fn is_vref_enabled(&mut self, common: &stm32::$common_type) -> bool { - self.adc.is_vref_enabled(common) - } - }; - - // Note that ADC1 supports measurement of VREF, VBAT, and the internal temperature sensor. - (additionals: ADC1 => ($common_type:ident)) => { - adc!(vbat => ($common_type)); - adc!(vtemp => ($common_type)); - adc!(vref => ($common_type)); - }; - - (additionals_checks: ADC1 => ($common_type:ident)) => { - adc!(vbat_check => ($common_type)); - adc!(vtemp_check => ($common_type)); - adc!(vref_check => ($common_type)); - }; - - - (additionals: ADC2 => ($common_type:ident)) => { - }; - - (additionals_checks: ADC2 => ($common_type:ident)) => { - }; - - (additionals: ADC3 => ($common_type:ident)) => { - adc!(vbat=> ($common_type)); - adc!(vref=> ($common_type)); - }; - - (additionals_checks: ADC3 => ($common_type:ident)) => { - adc!(vbat_check => ($common_type)); - adc!(vref_check => ($common_type)); - }; - - (additionals: ADC4 => ($common_type:ident)) => { - adc!(vref => ($common_type)); - }; - - (additionals_checks: ADC4 => ($common_type:ident)) => { - adc!(vref_check => ($common_type)); - }; - - (additionals: ADC5 => ($common_type:ident)) => { - adc!(vbat => ($common_type)); - adc!(vref => ($common_type)); - }; - - (additionals_checks: ADC5 => ($common_type:ident)) => { - adc!(vbat_check => ($common_type)); - adc!(vref_check => ($common_type)); - }; - - // Provide a stub implementation for ADCs that do not have a means of sampling VREF. - (additionals: $adc_type:ident => ($common_type:ident)) => { - }; - - ($($adc_type:ident => ($trigger_type:ident, $configure_clocks_fn_name:ident, $mux:expr, ($common_type:ident) )),+ $(,)*) => { - $( - impl TriggerType for stm32::$adc_type { - type ExternalTrigger = $trigger_type; - } - - impl AdcConfig for stm32::$adc_type { - #[inline(always)] - fn configure_clock_source(cs: ClockSource, rcc: &Rcc) { - $configure_clocks_fn_name(cs, rcc); - } - } - - impl DynamicAdc { - /// Converts a sample value to millivolts using calibrated VDDA and configured resolution - #[inline(always)] - pub fn sample_to_millivolts(&self, sample: u16) -> u16 { - Vref::sample_to_millivolts_ext(sample, self.calibrated_vdda, self.config.resolution) - } - - /// Disables the Voltage Regulator and release the ADC - #[inline(always)] - pub fn release(mut self) -> stm32::$adc_type { - self.enable_deeppwd_down(); - - self.adc_reg - } - - /// Powers-up an powered-down Adc - #[inline(always)] - pub fn power_up(&mut self, delay: &mut impl DelayNs) { - if self.is_deeppwd_enabled() { - self.disable_deeppwd_down(); - } - if !self.is_vreg_enabled() { - self.enable_vreg(delay); - } - if self.is_enabled() { - self.disable(); - } - } - - /// Puts a Disabled Adc into Powered Mode - #[inline(always)] - pub fn power_down(&mut self) { - self.disable_vreg(); - } - - /// Enables the Deep Power Down Modus - #[inline(always)] - pub fn enable_deeppwd_down(&mut self) { - self.adc_reg.cr().modify(|_, w| w.deeppwd().set_bit()); - } - - /// Disables the Deep Power Down Modus - #[inline(always)] - pub fn disable_deeppwd_down(&mut self) { - self.adc_reg.cr().modify(|_, w| w.deeppwd().clear_bit()); - } - - - /// Enables the Voltage Regulator - #[inline(always)] - pub fn enable_vreg(&mut self, delay: &mut impl DelayNs) { - self.adc_reg.cr().modify(|_, w| w.advregen().set_bit()); - while !self.adc_reg.cr().read().advregen().bit_is_set() {} - - // According to the STM32G4xx Reference Manual, section 21.4.6, we need - // to wait for T_ADCVREG_STUP after enabling the internal voltage - // regulator. For the STM32G431, this is 20 us. We choose 25 us to - // account for bad clocks. - delay.delay_us(25); - } - - /// Disables the Voltage Regulator - #[inline(always)] - pub fn disable_vreg(&mut self) { - self.adc_reg.cr().modify(|_, w| w.advregen().clear_bit()); - } - - /// Returns if the ADC is enabled (ADEN) - #[inline(always)] - pub fn is_enabled(&self) -> bool { - self.adc_reg.cr().read().aden().bit_is_set() - } - - /// Disables the adc, since we don't know in what state we get it. - #[inline(always)] - pub fn disable(&mut self) { - // Disable any ongoing conversions - self.cancel_conversion(); - - // Turn off ADC - self.adc_reg.cr().modify(|_, w| w.addis().set_bit()); - while self.adc_reg.cr().read().addis().bit_is_set() {} - - // Wait until the ADC has turned off - while self.adc_reg.cr().read().aden().bit_is_set() {} - } - - /// Enables the adc - #[inline(always)] - pub fn enable(&mut self) { - self.calibrate_all(); - self.apply_config(self.config); - - self.adc_reg.isr().modify(|_, w| w.adrdy().clear()); - self.adc_reg.cr().modify(|_, w| w.aden().set_bit()); - - // Wait for adc to get ready - while !self.adc_reg.isr().read().adrdy().bit_is_set() {} - - // Clear ready flag - self.adc_reg.isr().modify(|_, w| w.adrdy().clear()); - - self.clear_end_of_conversion_flag(); - } - - /// enable the adc and configure for DMA. - pub fn enable_dma(&mut self, dma: config::Dma) { - self.set_dma(dma); - self.enable(); - } - - /// Applies all fields in AdcConfig - #[inline(always)] - fn apply_config(&mut self, config: config::AdcConfig<$trigger_type>) { - self.set_clock_mode(config.clock_mode); - self.set_clock(config.clock); - self.set_resolution(config.resolution); - self.set_align(config.align); - self.set_external_trigger(config.external_trigger); - self.set_continuous(config.continuous); - self.set_subgroup_len(config.subgroup_len); - self.set_dma(config.dma); - self.set_end_of_conversion_interrupt(config.end_of_conversion_interrupt); - self.set_overrun_interrupt(config.overrun_interrupt); - self.set_default_sample_time(config.default_sample_time); - self.set_channel_input_type(config.difsel); - self.set_auto_delay(config.auto_delay); - - if let Some(vdda) = config.vdda { - self.calibrated_vdda = vdda; - } - } - - /// Sets the clock_mode for the adc - #[inline(always)] - pub fn set_clock_mode(&mut self, clock_mode: config::ClockMode) { - self.config.clock_mode = clock_mode; - unsafe { - let common = &(*stm32::$common_type::ptr()); - common.ccr().modify(|_, w| w.ckmode().bits(clock_mode.into())); - } - } - - /// Sets the clock for the adc - #[inline(always)] - pub fn set_clock(&mut self, clock: config::Clock) { - self.config.clock = clock; - unsafe { - let common = &(*stm32::$common_type::ptr()); - common.ccr().modify(|_, w| w.presc().bits(clock.into())); - } - } - - /// Sets the sampling resolution - #[inline(always)] - pub fn set_resolution(&mut self, resolution: config::Resolution) { - self.config.resolution = resolution; - unsafe { - self.adc_reg.cfgr().modify(|_, w| w.res().bits(resolution.into())); - } - } - - - /// Enable oversampling - #[inline(always)] - pub fn set_oversampling(&mut self, oversampling: config::OverSampling, shift: config::OverSamplingShift) { - self.adc_reg.cfgr2().modify(|_, w| unsafe { - w.ovsr().bits(oversampling.into()) - .ovss().bits(shift.into()) - .rovse().set_bit() - }); - } - - /// Sets the DR register alignment to left or right - #[inline(always)] - pub fn set_align(&mut self, align: config::Align) { - self.config.align = align; - self.adc_reg.cfgr().modify(|_, w| w.align().bit(align.into())); - } - - /// Sets which external trigger to use and if it is disabled, rising, falling or both - #[inline(always)] - pub fn set_external_trigger(&mut self, (edge, extsel): (config::TriggerMode, $trigger_type)) { - self.config.external_trigger = (edge, extsel); - self.adc_reg.cfgr().modify(|_, w| unsafe { w - .extsel().bits(extsel.into()) - .exten().bits(edge.into()) - }); - } - - /// Sets auto delay to true or false - #[inline(always)] - pub fn set_auto_delay(&mut self, delay: bool) { - self.config.auto_delay = delay; - self.adc_reg.cfgr().modify(|_, w| w.autdly().bit(delay) ); - } - - /// Enables and disables dis-/continuous mode - #[inline(always)] - pub fn set_continuous(&mut self, continuous: config::Continuous) { - self.config.continuous = continuous; - self.adc_reg.cfgr().modify(|_, w| w - .cont().bit(continuous == config::Continuous::Continuous) - .discen().bit(continuous == config::Continuous::Discontinuous) - ); - } - - #[inline(always)] - // NOTE: The software is allowed to write these bits only when ADSTART = 0 - fn set_subgroup_len(&mut self, subgroup_len: config::SubGroupLength) { - self.config.subgroup_len = subgroup_len; - unsafe { - self.adc_reg.cfgr().modify(|_, w| w.discnum().bits(subgroup_len as u8)); - } - } - - /// Sets DMA to disabled, single or continuous - #[inline(always)] - pub fn set_dma(&mut self, dma: config::Dma) { - self.config.dma = dma; - let (dds, en) = match dma { - config::Dma::Disabled => (false, false), - config::Dma::Single => (false, true), - config::Dma::Continuous => (true, true), - }; - self.adc_reg.cfgr().modify(|_, w| w - //DDS stands for "DMA disable selection" - //0 means do one DMA then stop - //1 means keep sending DMA requests as long as DMA=1 - .dmacfg().bit(dds) - .dmaen().bit(en) - ); - } - - /// Sets if the end-of-conversion behaviour. - /// The end-of-conversion interrupt occur either per conversion or for the whole sequence. - #[inline(always)] - pub fn set_end_of_conversion_interrupt(&mut self, eoc: config::Eoc) { - self.config.end_of_conversion_interrupt = eoc; - let (en, eocs) = match eoc { - config::Eoc::Disabled => (false, false), - config::Eoc::Conversion => (true, true), - config::Eoc::Sequence => (true, false), - }; - self.adc_reg.ier().modify(|_, w|w - .eosie().bit(eocs) - .eocie().bit(en) - ); - } - - /// Enable/disable overrun interrupt - /// - /// This is triggered when the AD finishes a conversion before the last value was read by CPU/DMA - pub fn set_overrun_interrupt(&mut self, enable: bool) { - self.adc_reg.ier().modify(|_, w| w.ovrie().bit(enable)); - } - - /// Sets the default sample time that is used for one-shot conversions. - /// [configure_channel](#method.configure_channel) and [start_conversion](#method.start_conversion) can be \ - /// used for configurations where different sampling times are required per channel. - #[inline(always)] - pub fn set_default_sample_time(&mut self, sample_time: config::SampleTime) { - self.config.default_sample_time = sample_time; - } - - /// Sets the differential selection per channel. - #[inline(always)] - pub fn set_channel_input_type(&mut self, df: config::DifferentialSelection) { - self.config.difsel = df; - - self.adc_reg.difsel().modify(|_, w| { - for i in 0..19 { - w.difsel(i).bit(df.get_channel(i).into()); - } - w - }); - } - - /// Reset the sequence - #[inline(always)] - pub fn reset_sequence(&mut self) { - //The reset state is One conversion selected - self.adc_reg.sqr1().modify(|_, w| unsafe { w.l().bits(config::Sequence::One.into()) }); - } - - /// Returns the current sequence length. Primarily useful for configuring DMA. - #[inline(always)] - pub fn sequence_length(&mut self) -> u8 { - self.adc_reg.sqr1().read().l().bits() + 1 - } - - /// Returns the address of the ADC data register. Primarily useful for configuring DMA. - #[inline(always)] - pub fn data_register_address(&self) -> u32 { - self.adc_reg.dr() as *const _ as u32 - } - - /// Calibrate the adc for - #[inline(always)] - pub fn calibrate(&mut self, it: config::InputType) { - match it { - config::InputType::SingleEnded => { - self.adc_reg.cr().modify(|_, w| w.adcaldif().clear_bit() ); - }, - config::InputType::Differential => { - self.adc_reg.cr().modify(|_, w| w.adcaldif().set_bit() ); - }, - } - - self.adc_reg.cr().modify(|_, w| w.adcal().set_bit() ); - while self.adc_reg.cr().read().adcal().bit_is_set() {} - } - - /// Calibrate the Adc for all Input Types - #[inline(always)] - pub fn calibrate_all(&mut self) { - self.calibrate(config::InputType::Differential); - self.calibrate(config::InputType::SingleEnded); - } - - /// Configure a channel for sampling. - /// It will make sure the sequence is at least as long as the `sequence` provided. - /// # Arguments - /// * `channel` - channel to configure - /// * `sequence` - where in the sequence to sample the channel. Also called rank in some STM docs/code - /// * `sample_time` - how long to sample for. See datasheet and ref manual to work out how long you need\ - /// to sample for at a given ADC clock frequency - pub fn configure_channel(&mut self, _channel: &CHANNEL, sequence: config::Sequence, sample_time: config::SampleTime) - where - CHANNEL: Channel, ID=u8> - { - - //Check the sequence is long enough - self.adc_reg.sqr1().modify(|r, w| unsafe { - let prev: config::Sequence = r.l().bits().into(); - if prev < sequence { - w.l().bits(sequence.into()) - } else { - w - } - }); - - let channel = CHANNEL::channel(); - - //Set the channel in the right sequence field - match sequence { - config::Sequence::One => self.adc_reg.sqr1().modify(|_, w| unsafe {w.sq1().bits(channel) }), - config::Sequence::Two => self.adc_reg.sqr1().modify(|_, w| unsafe {w.sq2().bits(channel) }), - config::Sequence::Three => self.adc_reg.sqr1().modify(|_, w| unsafe {w.sq3().bits(channel) }), - config::Sequence::Four => self.adc_reg.sqr1().modify(|_, w| unsafe {w.sq4().bits(channel) }), - config::Sequence::Five => self.adc_reg.sqr2().modify(|_, w| unsafe {w.sq5().bits(channel) }), - config::Sequence::Six => self.adc_reg.sqr2().modify(|_, w| unsafe {w.sq6().bits(channel) }), - config::Sequence::Seven => self.adc_reg.sqr2().modify(|_, w| unsafe {w.sq7().bits(channel) }), - config::Sequence::Eight => self.adc_reg.sqr2().modify(|_, w| unsafe {w.sq8().bits(channel) }), - config::Sequence::Nine => self.adc_reg.sqr2().modify(|_, w| unsafe {w.sq9().bits(channel) }), - config::Sequence::Ten => self.adc_reg.sqr3().modify(|_, w| unsafe {w.sq10().bits(channel) }), - config::Sequence::Eleven => self.adc_reg.sqr3().modify(|_, w| unsafe {w.sq11().bits(channel) }), - config::Sequence::Twelve => self.adc_reg.sqr3().modify(|_, w| unsafe {w.sq12().bits(channel) }), - config::Sequence::Thirteen => self.adc_reg.sqr3().modify(|_, w| unsafe {w.sq13().bits(channel) }), - config::Sequence::Fourteen => self.adc_reg.sqr3().modify(|_, w| unsafe {w.sq14().bits(channel) }), - config::Sequence::Fifteen => self.adc_reg.sqr4().modify(|_, w| unsafe {w.sq15().bits(channel) }), - config::Sequence::Sixteen => self.adc_reg.sqr4().modify(|_, w| unsafe {w.sq16().bits(channel) }), - }; - - //Set the sample time for the channel - let st = u8::from(sample_time); - unsafe { - match channel { - 0 => self.adc_reg.smpr1().modify(|_, w| w.smp0().bits(st) ), - 1 => self.adc_reg.smpr1().modify(|_, w| w.smp1().bits(st) ), - 2 => self.adc_reg.smpr1().modify(|_, w| w.smp2().bits(st) ), - 3 => self.adc_reg.smpr1().modify(|_, w| w.smp3().bits(st) ), - 4 => self.adc_reg.smpr1().modify(|_, w| w.smp4().bits(st) ), - 5 => self.adc_reg.smpr1().modify(|_, w| w.smp5().bits(st) ), - 6 => self.adc_reg.smpr1().modify(|_, w| w.smp6().bits(st) ), - 7 => self.adc_reg.smpr1().modify(|_, w| w.smp7().bits(st) ), - 8 => self.adc_reg.smpr1().modify(|_, w| w.smp8().bits(st) ), - 9 => self.adc_reg.smpr1().modify(|_, w| w.smp9().bits(st) ), - 10 => self.adc_reg.smpr2().modify(|_, w| w.smp10().bits(st) ), - 11 => self.adc_reg.smpr2().modify(|_, w| w.smp11().bits(st) ), - 12 => self.adc_reg.smpr2().modify(|_, w| w.smp12().bits(st) ), - 13 => self.adc_reg.smpr2().modify(|_, w| w.smp13().bits(st) ), - 14 => self.adc_reg.smpr2().modify(|_, w| w.smp14().bits(st) ), - 15 => self.adc_reg.smpr2().modify(|_, w| w.smp15().bits(st) ), - 16 => self.adc_reg.smpr2().modify(|_, w| w.smp16().bits(st) ), - 17 => self.adc_reg.smpr2().modify(|_, w| w.smp17().bits(st) ), - 18 => self.adc_reg.smpr2().modify(|_, w| w.smp18().bits(st) ), - _ => unimplemented!(), - }; - } - } - /// Synchronously convert a single sample - /// Note that it reconfigures the adc sequence and doesn't restore it - pub fn convert(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16 - where - PIN: Channel, ID=u8> - { - let saved_config = self.config; - unsafe { - self.adc_reg.cfgr().modify(|_, w| w - .dmaen().clear_bit() //Disable dma - .cont().clear_bit() //Disable continuous mode - .exten().bits(config::TriggerMode::Disabled.into()) //Disable trigger - ); - } - self.adc_reg.ier().modify(|_, w| w - .eocie().clear_bit() //Disable end of conversion interrupt - ); - - self.enable(); - self.reset_sequence(); - self.configure_channel(pin, config::Sequence::One, sample_time); - self.start_conversion(); - - //Wait for the sequence to complete - self.wait_for_conversion_sequence(); - - let result = self.current_sample(); - - self.disable(); - - //Reset the config - self.apply_config(saved_config); - - result - } - - /// Resets the end-of-conversion flag - #[inline(always)] - pub fn clear_end_of_conversion_flag(&mut self) { - self.adc_reg.isr().modify(|_, w| w.eoc().clear()); - } - - /// Block until the conversion is completed and return to configured - pub fn wait_for_conversion_sequence(&mut self) { - while !self.adc_reg.isr().read().eoc().bit_is_set() {} - } - - /// get current sample - #[inline(always)] - pub fn current_sample(&self) -> u16 { - self.adc_reg.dr().read().rdata().bits() - } - - /// Starts conversion sequence. Waits for the hardware to indicate it's actually started. - #[inline(always)] - pub fn start_conversion(&mut self) { - //Start conversion - self.adc_reg.cr().modify(|_, w| w.adstart().set_bit()); - } - - /// Cancels an ongoing conversion - #[inline(always)] - pub fn cancel_conversion(&mut self) { - self.adc_reg.cr().modify(|_, w| w.adstp().set_bit()); - while self.adc_reg.cr().read().adstart().bit_is_set() {} - } - - /// Returns if the Voltage Regulator is enabled - #[inline(always)] - pub fn is_vreg_enabled(&self) -> bool { - self.adc_reg.cr().read().advregen().bit_is_set() - } - - /// Returns if Deep Power Down is enabled - #[inline(always)] - pub fn is_deeppwd_enabled(&self) -> bool { - self.adc_reg.cr().read().deeppwd().bit_is_set() - } - - /// Returns if a conversion is active - #[inline(always)] - pub fn is_conversion_active(&self) -> bool { - self.adc_reg.cr().read().adstart().bit_is_set() - } - - /// Enables the vbat internal channel - #[inline(always)] - pub fn enable_vbat(&self, common: &stm32::$common_type) { - common.ccr().modify(|_, w| w.vbatsel().set_bit()); - } - - /// Enables the vbat internal channel - #[inline(always)] - pub fn disable_vbat(&self, common: &stm32::$common_type) { - common.ccr().modify(|_, w| w.vbatsel().clear_bit()); - } - - /// Returns if the vbat internal channel is enabled - #[inline(always)] - pub fn is_vbat_enabled(&mut self, common: &stm32::$common_type) -> bool { - common.ccr().read().vbatsel().bit_is_set() - } - - /// Enables the temp internal channel. - #[inline(always)] - pub fn enable_temperature(&mut self, common: &stm32::$common_type) { - common.ccr().modify(|_, w| w.vsensesel().set_bit()); - } - - /// Disables the temp internal channel - #[inline(always)] - pub fn disable_temperature(&mut self, common: &stm32::$common_type) { - common.ccr().modify(|_, w| w.vsensesel().clear_bit()); - } - - /// Returns if the temp internal channel is enabled - #[inline(always)] - pub fn is_temperature_enabled(&mut self, common: &stm32::$common_type) -> bool { - common.ccr().read().vsensesel().bit_is_set() - } - - /// Enables the vref internal channel. - #[inline(always)] - pub fn enable_vref(&mut self, common: &stm32::$common_type) { - common.ccr().modify(|_, w| w.vrefen().set_bit()); - } - - /// Disables the vref internal channel - #[inline(always)] - pub fn disable_vref(&mut self, common: &stm32::$common_type) { - common.ccr().modify(|_, w| w.vrefen().clear_bit()); - } - - /// Returns if the vref internal channel is enabled - #[inline(always)] - pub fn is_vref_enabled(&mut self, common: &stm32::$common_type) -> bool { - common.ccr().read().vrefen().bit_is_set() - } - - /// Read overrun flag - #[inline(always)] - pub fn get_overrun_flag(&self) -> bool { - self.adc_reg.isr().read().ovr().bit() - } - - /// Resets the overrun flag - #[inline(always)] - pub fn clear_overrun_flag(&mut self) { - self.adc_reg.isr().modify(|_, w| w.ovr().clear()); - } - } - - //TODO: claim now configures the clock for all ADCs in the group (12 and 345). - //Ideally we should make this a function of the common group and claim all adc in that group at once. - //The situation now is that the clock source setting can change between claims, changing the existing - //setting of the allready claimed ADC. - impl AdcClaim for stm32::$adc_type { - /// Enables the ADC clock, resets the peripheral (optionally), runs calibration and applies the supplied config - /// # Arguments - /// * `reset` - should a reset be performed. This is provided because on some devices multiple ADCs share the same common reset - /// - /// TODO: fix needing SYST - #[inline(always)] - fn claim(self, cs: ClockSource, rcc: &Rcc, delay: &mut impl DelayNs, reset: bool) -> Adc { - unsafe { - let rcc_ptr = &(*stm32::RCC::ptr()); - stm32::$adc_type::enable(rcc_ptr); - if reset {stm32::$adc_type::reset(rcc_ptr);} - } - Self::configure_clock_source(cs, rcc); - - let dynadc = DynamicAdc { - config: config::AdcConfig::default(), - adc_reg: self, - calibrated_vdda: VDDA_CALIB, - }; - - let adc: Adc:: = Adc { - adc: dynadc, - _status: PhantomData, - }; - - adc.power_up(delay) - } - - /// claims and configures the Adc - #[inline(always)] - fn claim_and_configure(self, cs: ClockSource, rcc: &Rcc, config: config::AdcConfig<$trigger_type>, delay: &mut impl DelayNs, reset :bool) -> Adc { - let mut adc = self.claim(cs, rcc, delay, reset); - adc.adc.config = config; - - // If the user specified a VDDA, use that over the internally determined value. - if let Some(vdda) = config.vdda { - adc.adc.calibrated_vdda = vdda; - } - - adc.enable() - } - } - - impl Adc { - /// Converts a sample value to millivolts using calibrated VDDA and configured resolution - #[inline(always)] - pub fn sample_to_millivolts(&self, sample: u16) -> u16 { - self.adc.sample_to_millivolts(sample) - } - } - - impl Adc { - /// Powers-up an powered-down Adc - #[inline(always)] - pub fn power_up(mut self, delay: &mut impl DelayNs) -> Adc { - self.adc.power_up(delay); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// Puts a Disabled Adc into Powered Mode - #[inline(always)] - pub fn power_down(mut adc: Adc) -> Self { - adc.adc.power_down(); - - Adc { - adc: adc.adc, - _status: PhantomData, - } - } - - /// Disables the Voltage Regulator and release the ADC - #[inline(always)] - pub fn release(self) -> stm32::$adc_type { - self.adc.release() - } - - /// Releases the Adc as a DynamicAdc. - /// While this is not unsafe; using methods while the Adc is in the wrong state will mess it up. - #[inline(always)] - pub fn into_dynamic_adc(self) -> DynamicAdc { - self.adc - } - - /// Retrieves the DynamicAdc. - /// This will put the adc in power down state. - #[inline(always)] - pub fn from_dynamic_adc(mut dynadc: DynamicAdc) -> Self { - if dynadc.is_conversion_active() { - dynadc.cancel_conversion(); - } - if dynadc.is_enabled() { - dynadc.disable(); - } - if dynadc.is_deeppwd_enabled() { - dynadc.disable_deeppwd_down(); - } - - dynadc.power_down(); - - Adc { - adc: dynadc, - _status: PhantomData, - } - } - - /// Enables the Deep Power Down Modus - #[inline(always)] - pub fn enable_deeppwd_down(&mut self) { - self.adc.enable_deeppwd_down() - } - - /// Disables the Deep Power Down Modus - #[inline(always)] - pub fn disable_deeppwd_down(&mut self) { - self.adc.disable_deeppwd_down() - } - } - - impl Adc { - adc!(additionals: $adc_type => ($common_type)); - adc!(additionals_checks: $adc_type => ($common_type)); - - /// Enables the adc - #[inline(always)] - pub fn enable(mut self) -> Adc { - self.adc.enable(); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// Enables the adc - #[inline(always)] - pub fn configure_and_enable(mut self, config: config::AdcConfig<$trigger_type>) -> Adc { - self.adc.apply_config(config); - self.enable() - } - - /// enable the adc and configure for DMA. - /// panics if set to Dma::Disabled - #[inline(always)] - pub fn enable_dma(mut self, dma: config::Dma) -> Adc { - if let config::Dma::Disabled = dma { - panic!("Requesting Enabling DMA with DisableDma parameter"); - } - - self.adc.enable_dma(dma); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// Puts a disabled Adc into PoweredDown Mode - #[inline(always)] - pub fn power_down(mut self) -> Adc { - self.adc.power_down(); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// Sets the clock_mode for the adc - #[inline(always)] - pub fn set_clock_mode(&mut self, clock_mode: config::ClockMode) { - self.adc.set_clock_mode(clock_mode) - } - - /// Sets the clock for the adc - #[inline(always)] - pub fn set_clock(&mut self, clock: config::Clock) { - self.adc.set_clock(clock) - } - - /// Sets the oversampling - #[inline(always)] - pub fn set_oversampling(&mut self, oversampling: config::OverSampling, shift: config::OverSamplingShift) { - self.adc.set_oversampling(oversampling, shift) - } - - /// Sets the sampling resolution - #[inline(always)] - pub fn set_resolution(&mut self, resolution: config::Resolution) { - self.adc.set_resolution(resolution) - } - - /// Sets the DR register alignment to left or right - #[inline(always)] - pub fn set_align(&mut self, align: config::Align) { - self.adc.set_align(align) - } - - /// Sets which external trigger to use and if it is disabled, rising, falling or both - #[inline(always)] - pub fn set_external_trigger(&mut self, (edge, extsel): (config::TriggerMode, $trigger_type)) { - self.adc.set_external_trigger( (edge, extsel) ) - } - - /// Sets auto delay to true or false - #[inline(always)] - pub fn set_auto_delay(&mut self, delay: bool) { - self.adc.set_auto_delay(delay) - } - - /// Enables and disables continuous mode - #[inline(always)] - pub fn set_continuous(&mut self, continuous: config::Continuous) { - self.adc.set_continuous(continuous) - } - - /// Set subgroup length, number of AD readings per trigger event (only relevant in Discontinuous mode) - pub fn set_subgroup_len(&mut self, subgroup_len: config::SubGroupLength) { - self.adc.set_subgroup_len(subgroup_len); - } - - /// Sets DMA to disabled, single or continuous - #[inline(always)] - pub fn set_dma(&mut self, dma: config::Dma) { - self.adc.set_dma(dma) - } - - /// Sets if the end-of-conversion behaviour. - /// The end-of-conversion interrupt occur either per conversion or for the whole sequence. - #[inline(always)] - pub fn set_end_of_conversion_interrupt(&mut self, eoc: config::Eoc) { - self.adc.set_end_of_conversion_interrupt(eoc) - } - - /// Enable/disable overrun interrupt - /// - /// This is triggered when the AD finishes a conversion before the last value was read by CPU/DMA - #[inline(always)] - pub fn set_overrun_interrupt(&mut self, enable: bool) { - self.adc.set_overrun_interrupt(enable) - } - - /// Sets the default sample time that is used for one-shot conversions. - /// [configure_channel](#method.configure_channel) and [start_conversion](#method.start_conversion) can be \ - /// used for configurations where different sampling times are required per channel. - #[inline(always)] - pub fn set_default_sample_time(&mut self, sample_time: config::SampleTime) { - self.adc.set_default_sample_time(sample_time) - } - - /// Sets the differential selection per channel. - #[inline(always)] - pub fn set_channel_input_type(&mut self, df: config::DifferentialSelection) { - self.adc.set_channel_input_type(df) - } - - /// Reset the sequence - #[inline(always)] - pub fn reset_sequence(&mut self) { - self.adc.reset_sequence() - } - - /// Returns the current sequence length. Primarily useful for configuring DMA. - #[inline(always)] - pub fn sequence_length(&mut self) -> u8 { - self.adc.sequence_length() - } - - /// Calibrate the adc for - #[inline(always)] - pub fn calibrate(&mut self, it: config::InputType) { - self.adc.calibrate(it) - } - - /// Calibrate the Adc for all Input Types - #[inline(always)] - pub fn calibrate_all(&mut self) { - self.adc.calibrate_all(); - } - - /// Configure a channel for sampling. - /// It will make sure the sequence is at least as long as the `sequence` provided. - /// # Arguments - /// * `channel` - channel to configure - /// * `sequence` - where in the sequence to sample the channel. Also called rank in some STM docs/code - /// * `sample_time` - how long to sample for. See datasheet and ref manual to work out how long you need\ - /// to sample for at a given ADC clock frequency - #[inline(always)] - pub fn configure_channel(&mut self, channel: &CHANNEL, sequence: config::Sequence, sample_time: config::SampleTime) - where - CHANNEL: Channel, ID=u8> - { - self.adc.configure_channel(channel, sequence, sample_time) - } - - /// Synchronously convert a single sample - /// Note that it reconfigures the adc sequence and doesn't restore it - #[inline(always)] - pub fn convert(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16 - where - PIN: Channel, ID=u8> - { - self.adc.convert(pin, sample_time) - } - } - - impl Adc { - adc!(additionals_checks: $adc_type => ($common_type)); - - /// Disables the adc - #[inline(always)] - pub fn disable(mut self) -> Adc { - self.adc.disable(); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// Starts conversion sequence. Waits for the hardware to indicate it's actually started. - #[inline(always)] - pub fn start_conversion(mut self) -> Adc { - self.adc.clear_end_of_conversion_flag(); - self.adc.start_conversion(); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// Returns the current sample stored in the ADC data register - #[inline(always)] - pub fn current_sample(&self) -> u16 { - self.adc.current_sample() - } - - /// Synchronously convert a single sample - /// Note that it reconfigures the adc sequence and doesn't restore it - #[inline(always)] - pub fn convert(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16 - where - PIN: Channel, ID=u8> - { - self.adc.reset_sequence(); - self.adc.configure_channel(pin, config::Sequence::One, sample_time); - self.adc.start_conversion(); - - //Wait for the sequence to complete - self.adc.wait_for_conversion_sequence(); - - self.adc.current_sample() - } - } - - impl Conversion { - /// Wait in a potential infite loop untill the ADC has stopped the conversion. - /// Everytime an sample is retrieved 'func' is called. - /// Note: when the ADC has stopped the conversion, for the last sample, func is NOT run. - pub fn wait_untill_stopped(mut self, mut func: F) -> Adc where F: FnMut(u16, &Adc) { - let adc = loop { - match self { - Conversion::Stopped(adc) => break adc, - Conversion::Active(adc) => { - self = adc.wait_for_conversion_sequence(); - if let Conversion::Active(adc) = self { - let sample = adc.current_sample(); - func(sample, &adc); - - self = Conversion::Active(adc); - } - } - } - }; - adc - } - } - - impl Adc { - /// Block until the conversion is completed and return to configured - pub fn wait_for_conversion_sequence(mut self) -> Conversion { - self.adc.wait_for_conversion_sequence(); - - if !self.adc.is_conversion_active() { - let inactive: Adc<_, Configured> = Adc { - adc: self.adc, - _status: PhantomData, - }; - - Conversion::Stopped(inactive) - } - else { - Conversion::Active(self) - } - } - - - /// Returns if a conversion has been completed - /// Calling this before `wait_for_conversion_sequence` - /// should make that function return immediatly - pub fn is_conversion_done(&self) -> bool { - !self.adc.is_conversion_active() - } - - /// Cancels an ongoing conversion - #[inline(always)] - pub fn cancel_conversion(mut self) -> Adc { - self.adc.cancel_conversion(); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// get current sample - #[inline(always)] - pub fn current_sample(&self) -> u16 { - self.adc.current_sample() - } - - /// clear end conversion flag - #[inline(always)] - pub fn clear_end_conversion_flag(&mut self) { - self.adc.clear_end_of_conversion_flag(); - } - } - - impl Adc { - /// Starts conversion sequence. Waits for the hardware to indicate it's actually started. - #[inline(always)] - pub fn start_conversion(&mut self) { - self.adc.start_conversion() - } - - /// Cancels an ongoing conversion - #[inline(always)] - pub fn cancel_conversion(&mut self) { - self.adc.cancel_conversion() - } - - /// Stop the Adc - #[inline(always)] - pub fn stop(&mut self) { - self.adc.disable() - } - - /// Disable the Adc - #[inline(always)] - pub fn disable(mut self) -> Adc { - self.adc.set_dma(config::Dma::Disabled); - self.adc.disable(); - - Adc { - adc: self.adc, - _status: PhantomData, - } - } - - /// Read overrun flag - #[inline(always)] - pub fn get_overrun_flag(&self) -> bool { - self.adc.get_overrun_flag() - } - - /// Resets the overrun flag - #[inline(always)] - pub fn clear_overrun_flag(&mut self) { - self.adc.clear_overrun_flag(); - } - } - - unsafe impl TargetAddress for Adc { - #[inline(always)] - fn address(&self) -> u32 { - self.adc.data_register_address() - } - - type MemSize = u16; - - const REQUEST_LINE: Option = Some($mux as u8); - } - - impl OneShot, u16, PIN> for Adc - where - PIN: Channel, ID=u8>, - { - type Error = (); - - fn read(&mut self, pin: &mut PIN) -> nb::Result { - Ok(self.convert(pin, self.adc.config.default_sample_time) ) - } - } - )+ - }; -} - -#[cfg(any( - feature = "stm32g431", - feature = "stm32g441", - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", -))] -adc!(ADC1 => (ExternalTrigger12, configure_clock_source12, DmaMuxResources::ADC1, (ADC12_COMMON) )); - -#[cfg(any( - feature = "stm32g431", - feature = "stm32g441", - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", -))] -adc!(ADC2 => (ExternalTrigger12, configure_clock_source12, DmaMuxResources::ADC2, (ADC12_COMMON) )); - -#[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", -))] -adc!(ADC3 => (ExternalTrigger345, configure_clock_source345, DmaMuxResources::ADC3, (ADC345_COMMON) )); - -#[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", -))] -adc!(ADC4 => (ExternalTrigger345, configure_clock_source345, DmaMuxResources::ADC4, (ADC345_COMMON) )); - -#[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", -))] -adc!(ADC5 => (ExternalTrigger345, configure_clock_source345, DmaMuxResources::ADC5, (ADC345_COMMON) )); - -#[cfg(any(feature = "stm32g431", feature = "stm32g441", feature = "stm32g471",))] -adc_pins!( - gpioa::PA0 => (ADC1, 1), - gpioa::PA0 => (ADC2, 1), - gpioa::PA1 => (ADC1, 2), - gpioa::PA1 => (ADC2, 2), - gpioa::PA2 => (ADC1, 3), - gpioa::PA3 => (ADC1, 4), - gpioa::PA4 => (ADC2, 17), - gpioa::PA5 => (ADC2, 13), - gpioa::PA6 => (ADC2, 3), - gpioa::PA7 => (ADC2, 4), - - gpiob::PB0 => (ADC1, 15), - gpiob::PB1 => (ADC1, 12), - gpiob::PB2 => (ADC2, 12), - gpiob::PB11 => (ADC1, 14), - gpiob::PB11 => (ADC2, 14), - gpiob::PB12 => (ADC1, 11), - gpiob::PB15 => (ADC2, 15), - - gpioc::PC0 => (ADC1, 6), - gpioc::PC0 => (ADC2, 6), - gpioc::PC1 => (ADC1, 7), - gpioc::PC1 => (ADC2, 7), - gpioc::PC2 => (ADC1, 8), - gpioc::PC2 => (ADC2, 8), - gpioc::PC3 => (ADC1, 9), - gpioc::PC3 => (ADC2, 9), - gpioc::PC4 => (ADC2, 5), - gpioc::PC5 => (ADC2, 11), - - gpiof::PF0 => (ADC1, 10), - gpiof::PF1 => (ADC2, 10), - - Temperature => (ADC1, 16), - Vbat => (ADC1, 17), - Vref => (ADC1, 18), -); - -#[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", -))] -adc_pins!( - gpioa::PA0 => (ADC1, 1), - gpioa::PA0 => (ADC2, 1), - gpioa::PA1 => (ADC1, 2), - gpioa::PA1 => (ADC2, 2), - gpioa::PA2 => (ADC1, 3), - gpioa::PA3 => (ADC1, 4), - gpioa::PA4 => (ADC2, 17), - gpioa::PA5 => (ADC2, 13), - gpioa::PA6 => (ADC2, 3), - gpioa::PA7 => (ADC2, 4), - gpioa::PA8 => (ADC5, 1), - gpioa::PA9 => (ADC5, 2), - - gpiob::PB0 => (ADC3, 12), - gpiob::PB0 => (ADC1, 15), - gpiob::PB1 => (ADC3, 1), - gpiob::PB1 => (ADC1, 12), - gpiob::PB2 => (ADC2, 12), - gpiob::PB11 => (ADC1, 14), - gpiob::PB11 => (ADC2, 14), - gpiob::PB12 => (ADC4, 3), - gpiob::PB12 => (ADC1, 11), - gpiob::PB13 => (ADC3, 5), - gpiob::PB14 => (ADC4, 4), - gpiob::PB14 => (ADC1, 5), - gpiob::PB15 => (ADC4, 5), - gpiob::PB15 => (ADC2, 15), - - gpioc::PC0 => (ADC1, 6), - gpioc::PC0 => (ADC2, 6), - gpioc::PC1 => (ADC1, 7), - gpioc::PC1 => (ADC2, 7), - gpioc::PC2 => (ADC1, 8), - gpioc::PC2 => (ADC2, 8), - gpioc::PC3 => (ADC1, 9), - gpioc::PC3 => (ADC2, 9), - gpioc::PC4 => (ADC2, 5), - gpioc::PC5 => (ADC2, 11), - - gpioe::PE7 => (ADC3, 4), - gpioe::PE8 => (ADC3, 6), - gpioe::PE8 => (ADC4, 6), - gpioe::PE8 => (ADC5, 6), - gpioe::PE9 => (ADC3, 2), - gpioe::PE10 => (ADC3, 14), - gpioe::PE10 => (ADC4, 14), - gpioe::PE10 => (ADC5, 14), - gpioe::PE11 => (ADC3, 15), - gpioe::PE11 => (ADC4, 15), - gpioe::PE11 => (ADC5, 15), - gpioe::PE12 => (ADC3, 16), - gpioe::PE12 => (ADC4, 16), - gpioe::PE12 => (ADC5, 16), - gpioe::PE13 => (ADC3, 3), - gpioe::PE14 => (ADC4, 1), - gpioe::PE15 => (ADC4, 2), - - gpiod::PD8 => (ADC4, 12), - gpiod::PD8 => (ADC5, 12), - gpiod::PD9 => (ADC4, 13), - gpiod::PD9 => (ADC5, 13), - gpiod::PD10 => (ADC3, 7), - gpiod::PD10 => (ADC4, 7), - gpiod::PD10 => (ADC5, 7), - gpiod::PD11 => (ADC3, 8), - gpiod::PD11 => (ADC4, 8), - gpiod::PD11 => (ADC5, 8), - gpiod::PD12 => (ADC3, 9), - gpiod::PD12 => (ADC4, 9), - gpiod::PD12 => (ADC5, 9), - gpiod::PD13 => (ADC3, 10), - gpiod::PD13 => (ADC4, 10), - gpiod::PD13 => (ADC5, 10), - gpiod::PD14 => (ADC3, 11), - gpiod::PD14 => (ADC4, 11), - gpiod::PD14 => (ADC5, 11), - - gpiof::PF0 => (ADC1, 10), - gpiof::PF1 => (ADC2, 10), - - Temperature => (ADC1, 16), - Vbat => (ADC1, 17), - Vbat => (ADC3, 17), - Vbat => (ADC5, 17), - Vref => (ADC1, 18), - Vref => (ADC3, 18), - Vref => (ADC4, 18), - Vref => (ADC5, 18), -); - -// See https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#page=782 -adc_opamp!( - // TODO: Add all opamp types: OpenLoop, Follower(for all opamps) - // TODO: Should we restrict type parameters A and B? - // TODO: Also allow AD-channels shared by pins - opamp::Opamp1 => (ADC1, 13), - opamp::Opamp2 => (ADC2, 16), - opamp::Opamp3 => (ADC2, 18), -); - -#[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", -))] -adc_opamp!( - opamp::Opamp3 => (ADC3, 13), -); - -#[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", -))] -adc_opamp!( - opamp::Opamp4 => (ADC5, 5), - opamp::Opamp5 => (ADC5, 3), - opamp::Opamp6 => (ADC4, 17), -); - -#[cfg(any(feature = "stm32g491", feature = "stm32g4a1",))] -adc_opamp!( - opamp::Opamp6 => (ADC3, 17), -); - -#[cfg(any(feature = "stm32g491", feature = "stm32g4a1",))] -adc_pins!( - gpioa::PA0 => (ADC1, 1), - gpioa::PA0 => (ADC2, 1), - gpioa::PA1 => (ADC1, 2), - gpioa::PA1 => (ADC2, 2), - gpioa::PA2 => (ADC1, 3), - gpioa::PA3 => (ADC1, 4), - gpioa::PA4 => (ADC2, 17), - gpioa::PA5 => (ADC2, 13), - gpioa::PA6 => (ADC2, 3), - gpioa::PA7 => (ADC2, 4), - - gpiob::PB0 => (ADC3, 12), - gpiob::PB0 => (ADC1, 15), - gpiob::PB1 => (ADC3, 1), - gpiob::PB1 => (ADC1, 12), - gpiob::PB2 => (ADC2, 12), - gpiob::PB11 => (ADC1, 14), - gpiob::PB11 => (ADC2, 14), - gpiob::PB12 => (ADC1, 11), - gpiob::PB13 => (ADC3, 5), - gpiob::PB14 => (ADC1, 5), - gpiob::PB15 => (ADC2, 15), - - gpioc::PC0 => (ADC1, 6), - gpioc::PC0 => (ADC2, 6), - gpioc::PC1 => (ADC1, 7), - gpioc::PC1 => (ADC2, 7), - gpioc::PC2 => (ADC1, 8), - gpioc::PC2 => (ADC2, 8), - gpioc::PC3 => (ADC1, 9), - gpioc::PC3 => (ADC2, 9), - gpioc::PC4 => (ADC2, 5), - gpioc::PC5 => (ADC2, 11), - - gpioe::PE7 => (ADC3, 4), - gpioe::PE8 => (ADC3, 6), - gpioe::PE9 => (ADC3, 2), - gpioe::PE10 => (ADC3, 14), - gpioe::PE11 => (ADC3, 15), - gpioe::PE12 => (ADC3, 16), - gpioe::PE13 => (ADC3, 3), - - gpiod::PD10 => (ADC3, 7), - gpiod::PD11 => (ADC3, 8), - gpiod::PD12 => (ADC3, 9), - gpiod::PD13 => (ADC3, 10), - gpiod::PD14 => (ADC3, 11), - - gpiof::PF0 => (ADC1, 10), - gpiof::PF1 => (ADC2, 10), - - Temperature => (ADC1, 16), - Vbat => (ADC1, 17), - Vbat => (ADC3, 17), - Vref => (ADC1, 18), - Vref => (ADC3, 18), -); diff --git a/src/adc/config.rs b/src/adc/config.rs new file mode 100644 index 00000000..6a918c44 --- /dev/null +++ b/src/adc/config.rs @@ -0,0 +1,1021 @@ +/// Contains types related to ADC configuration +use embedded_hal_old::adc::Channel; +use fugit::HertzU32; + +use crate::rcc; + +/// The place in the sequence a given channel should be captured +#[derive(Debug, PartialEq, PartialOrd, Copy, Clone)] +pub enum Sequence { + /// 1 + One, + /// 2 + Two, + /// 3 + Three, + /// 4 + Four, + /// 5 + Five, + /// 6 + Six, + /// 7 + Seven, + /// 8 + Eight, + /// 9 + Nine, + /// 10 + Ten, + /// 11 + Eleven, + /// 12 + Twelve, + /// 13 + Thirteen, + /// 14 + Fourteen, + /// 15 + Fifteen, + /// 16 + Sixteen, +} + +impl From for u8 { + fn from(s: Sequence) -> u8 { + match s { + Sequence::One => 0, + Sequence::Two => 1, + Sequence::Three => 2, + Sequence::Four => 3, + Sequence::Five => 4, + Sequence::Six => 5, + Sequence::Seven => 6, + Sequence::Eight => 7, + Sequence::Nine => 8, + Sequence::Ten => 9, + Sequence::Eleven => 10, + Sequence::Twelve => 11, + Sequence::Thirteen => 12, + Sequence::Fourteen => 13, + Sequence::Fifteen => 14, + Sequence::Sixteen => 15, + } + } +} + +impl From for Sequence { + fn from(bits: u8) -> Self { + match bits { + 0 => Sequence::One, + 1 => Sequence::Two, + 2 => Sequence::Three, + 3 => Sequence::Four, + 4 => Sequence::Five, + 5 => Sequence::Six, + 6 => Sequence::Seven, + 7 => Sequence::Eight, + 8 => Sequence::Nine, + 9 => Sequence::Ten, + 10 => Sequence::Eleven, + 11 => Sequence::Twelve, + 12 => Sequence::Thirteen, + 13 => Sequence::Fourteen, + 14 => Sequence::Fifteen, + 15 => Sequence::Sixteen, + _ => unimplemented!(), + } + } +} + +/// The number of cycles to sample a given channel for +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum SampleTime { + /// 2.5 cycles + Cycles_2_5, + /// 6.5 cycles + Cycles_6_5, + /// 12_5 cycles + Cycles_12_5, + /// 24.5 cycles + Cycles_24_5, + /// 47.5 cycles + Cycles_47_5, + /// 92.5 cycles + Cycles_92_5, + /// 247.5 cycles + Cycles_247_5, + /// 640.5 cycles + Cycles_640_5, +} + +impl From for SampleTime { + fn from(f: u8) -> SampleTime { + match f { + 0 => SampleTime::Cycles_2_5, + 1 => SampleTime::Cycles_6_5, + 2 => SampleTime::Cycles_12_5, + 3 => SampleTime::Cycles_24_5, + 4 => SampleTime::Cycles_47_5, + 5 => SampleTime::Cycles_92_5, + 6 => SampleTime::Cycles_247_5, + 7 => SampleTime::Cycles_640_5, + _ => unimplemented!(), + } + } +} + +impl From for u8 { + fn from(l: SampleTime) -> u8 { + match l { + SampleTime::Cycles_2_5 => 0, + SampleTime::Cycles_6_5 => 1, + SampleTime::Cycles_12_5 => 2, + SampleTime::Cycles_24_5 => 3, + SampleTime::Cycles_47_5 => 4, + SampleTime::Cycles_92_5 => 5, + SampleTime::Cycles_247_5 => 6, + SampleTime::Cycles_640_5 => 7, + } + } +} + +/// ADC Clock Source selection +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone, Default)] +pub enum ClockSource { + /// Use the System Clock as Clock Source + #[default] + SystemClock, + /// use the Internal PLL as Clock Source + PllP, +} + +impl From for u8 { + fn from(c: ClockSource) -> u8 { + match c { + ClockSource::PllP => 0b01, + ClockSource::SystemClock => 0b10, + } + } +} + +/// ClockMode config for the ADC +/// Check the datasheet for the maximum speed the ADC supports +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum ClockMode { + /// (Asynchronous clock mode), adc_ker_ck. generated at product level (refer to Section 6: Reset and clock control (RCC) + AdcKerCk { + /// Clock prescaler + /// + /// The final clock frequency will be `f_src / prescaler` + prescaler: Prescaler, + + /// Clock source for the ADC + /// + /// The final clock frequency will be `f_src / clock` + src: ClockSource, + }, + /// (Synchronous clock mode). adc_hclk/1 + /// This configuration must be enabled only if the AHB clock prescaler is set to 1 (HPRE\[3:0\] = 0xxx in RCC_CFGR register) and if the system clock has a 50% duty cycle. + AdcHclkDiv1, + /// Synchronous clock mode. adc_hclk/2 + AdcHclkDiv2, + /// Synchronous clock mode. adc_hclk/4 + AdcHclkDiv4, +} + +impl ClockMode { + /// Validate configuration + pub fn validate(&self, rcc: &mut crate::rcc::Rcc) -> HertzU32 { + fn prescaler_to_factor(p: Prescaler) -> u32 { + match p { + Prescaler::Div_1 => 1, + Prescaler::Div_2 => 2, + Prescaler::Div_4 => 4, + Prescaler::Div_6 => 6, + Prescaler::Div_8 => 8, + Prescaler::Div_10 => 10, + Prescaler::Div_12 => 12, + Prescaler::Div_16 => 16, + Prescaler::Div_32 => 32, + Prescaler::Div_64 => 64, + Prescaler::Div_128 => 128, + Prescaler::Div_256 => 256, + } + } + + let f = match self { + ClockMode::AdcKerCk { + prescaler: clock, + src: ClockSource::PllP, + } => { + rcc.clocks + .pll_clk + .p + .expect("Pll-P selected as clock source for ADC but is disabled") + .raw() + / prescaler_to_factor(*clock) + } + ClockMode::AdcKerCk { + prescaler: clock, + src: ClockSource::SystemClock, + } => rcc.clocks.sys_clk.raw() / prescaler_to_factor(*clock), + + //01: adc_hclk/1 (Synchronous clock mode). This configuration must be enabled only if the + //AHB clock prescaler is set (HPRE[3:0] = 0xxx in RCC_CFGR register) and if the system + //clock has a 50% duty cycle. + ClockMode::AdcHclkDiv1 => { + assert!(rcc.rb.cfgr().read().hpre().is_div1()); + rcc.clocks.ahb_clk.raw() + } + ClockMode::AdcHclkDiv2 => rcc.clocks.ahb_clk.raw() / 2, + ClockMode::AdcHclkDiv4 => rcc.clocks.ahb_clk.raw() / 4, + }; + + HertzU32::Hz(f) + } + + pub(crate) fn to_bits(self, rcc: &mut rcc::Rcc) -> ClockBits { + assert!(self.validate(rcc) <= HertzU32::MHz(60)); + match self { + ClockMode::AdcKerCk { + prescaler: clock, + src, + } => ClockBits { + ckmode: 0b00, + presc: clock.into(), + adcsel: src.into(), + }, + ClockMode::AdcHclkDiv1 => ClockBits { + ckmode: 0b01, + presc: 0, + adcsel: 0, + }, + ClockMode::AdcHclkDiv2 => ClockBits { + ckmode: 0b10, + presc: 0, + adcsel: 0, + }, + ClockMode::AdcHclkDiv4 => ClockBits { + ckmode: 0b11, + presc: 0, + adcsel: 0, + }, + } + } +} + +impl Default for ClockMode { + fn default() -> Self { + Self::AdcKerCk { + prescaler: Default::default(), + src: Default::default(), + } + } +} + +pub(crate) struct ClockBits { + pub(crate) ckmode: u8, + pub(crate) presc: u8, + pub(crate) adcsel: u8, +} + +/// Clock config for the ADC +/// Check the datasheet for the maximum speed the ADC supports +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone, Default)] +pub enum Prescaler { + /// Clock not divided + #[default] + Div_1, + /// Clock divided by 2 + Div_2, + /// Clock divided by 4 + Div_4, + /// Clock divided by 6 + Div_6, + /// Clock divided by 8 + Div_8, + /// Clock divided by 10 + Div_10, + /// Clock divided by 12 + Div_12, + /// Clock divided by 16 + Div_16, + /// Clock divided by 32 + Div_32, + /// Clock divided by 64 + Div_64, + /// Clock divided by 128 + Div_128, + /// Clock divided by 256 + Div_256, +} + +impl From for u8 { + fn from(c: Prescaler) -> u8 { + match c { + Prescaler::Div_1 => 0b000, + Prescaler::Div_2 => 0b001, + Prescaler::Div_4 => 0b010, + Prescaler::Div_6 => 0b011, + Prescaler::Div_8 => 0b100, + Prescaler::Div_10 => 0b101, + Prescaler::Div_12 => 0b110, + Prescaler::Div_16 => 0b111, + Prescaler::Div_32 => 0b1000, + Prescaler::Div_64 => 0b1001, + Prescaler::Div_128 => 0b1010, + Prescaler::Div_256 => 0b1011, + } + } +} + +impl From for Prescaler { + fn from(b: u8) -> Prescaler { + match b { + 0b000 => Prescaler::Div_1, + 0b001 => Prescaler::Div_2, + 0b010 => Prescaler::Div_4, + 0b011 => Prescaler::Div_6, + 0b100 => Prescaler::Div_8, + 0b101 => Prescaler::Div_10, + 0b110 => Prescaler::Div_12, + 0b111 => Prescaler::Div_16, + 0b1000 => Prescaler::Div_32, + 0b1001 => Prescaler::Div_64, + 0b1010 => Prescaler::Div_128, + 0b1011 => Prescaler::Div_256, + _ => unimplemented!(), + } + } +} + +/// Resolution to sample at +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum Resolution { + /// 12-bit + Twelve, + /// 10-bit + Ten, + /// 8-bit + Eight, + /// 6-bit + Six, +} +impl Resolution { + /// Return the maximum value of a sample with the given Resolution + pub fn to_max_sample(self) -> u32 { + match self { + Resolution::Twelve => (1 << 12) - 1, + Resolution::Ten => (1 << 10) - 1, + Resolution::Eight => (1 << 8) - 1, + Resolution::Six => (1 << 6) - 1, + } + } +} +impl From for u8 { + fn from(r: Resolution) -> u8 { + match r { + Resolution::Twelve => 0b00, + Resolution::Ten => 0b01, + Resolution::Eight => 0b10, + Resolution::Six => 0b11, + } + } +} +impl From for Resolution { + fn from(r: u8) -> Resolution { + match r { + 0b00 => Resolution::Twelve, + 0b01 => Resolution::Ten, + 0b10 => Resolution::Eight, + 0b11 => Resolution::Six, + _ => unimplemented!(), + } + } +} + +/// Possible external triggers the ADC can listen to +/// +/// This applies to ADC3, ADC4 and ADC5 +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy, Default)] +pub enum ExternalTrigger12 { + /// TIM1 compare channel 1 + #[default] + Tim_1_cc_1, + /// TIM1 compare channel 2 + Tim_1_cc_2, + /// TIM1 compare channel 3 + Tim_1_cc_3, + /// TIM2 compare channel 2 + Tim_2_cc_2, + /// TIM3 trigger out + Tim_3_trgo, + /// TIM4 compare channel 4 + Tim_4_cc_4, + /// External interupt line 11 + Exti_11, + /// TIM8 trigger out + Tim_8_trgo, + /// TIM8 trigger out 2 + Tim_8_trgo_2, + /// TIM1 trigger out + Tim_1_trgo, + /// TIM1 trigger out 2 + Tim_1_trgo_2, + /// TIM2 trigger out + Tim_2_trgo, + /// TIM4 trigger out + Tim_4_trgo, + /// TIM6 trigger out + Tim_6_trgo, + /// TIM15 trigger out + Tim_15_trgo, + /// TIM3 compare channel 4 + Tim_3_cc_4, + /// TIM20 trigger out + Tim_20_trgo, + /// TIM20 trigger out 2 + Tim_20_trgo_2, + /// TIM20 compare channel 1 + Tim_20_cc_1, + /// TIM20 compare channel 2 + Tim_20_cc_2, + /// TIM20 compare channel 3 + Tim_20_cc_3, + /// hrtim_adc_trg1 + Hrtim_adc_trg_1, + /// hrtim_adc_trg3 + Hrtim_adc_trg_3, + /// hrtim_adc_trg5 + Hrtim_adc_trg_5, + /// hrtim_adc_trg6 + Hrtim_adc_trg_6, + /// hrtim_adc_trg7 + Hrtim_adc_trg_7, + /// hrtim_adc_trg8 + Hrtim_adc_trg_8, + /// hrtim_adc_trg9 + Hrtim_adc_trg_9, + /// hrtim_adc_trg10 + Hrtim_adc_trg_10, + /// LP_timeout + Lp_timeout, + /// TIM7 trigger out + Tim_7_trgo, +} + +/// Possible external triggers the ADC can listen to +/// +/// This applies to ADC3, ADC4 and ADC5 +/// +#[cfg(feature = "adc3")] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy, Default)] +pub enum ExternalTrigger345 { + /// TIM3 compare channel 1 + #[default] + Tim_3_cc_1, + /// TIM2 compare channel 3 + Tim_2_cc_3, + /// TIM1 compare channel 3 + Tim_1_cc_3, + /// TIM8 compare channel 1 + Tim_8_cc_1, + /// TIM3 trigger out + Tim_3_trgo, + /// External interupt line 2 + Exti_2, + /// TIM4 compare channel 1 + Tim_4_cc_1, + /// TIM8 trigger out + Tim_8_trgo, + /// TIM8 trigger out 2 + Tim_8_trgo_2, + /// TIM1 trigger out + Tim_1_trgo, + /// TIM1 trigger out 2 + Tim_1_trgo_2, + /// TIM2 trigger out + Tim_2_trgo, + /// TIM4 trigger out + Tim_4_trgo, + /// TIM6 trigger out + Tim_6_trgo, + /// TIM15 trigger out + Tim_15_trgo, + /// TIM2 compare channel 1 + Tim_2_cc_1, + /// TIM20 trigger out + Tim_20_trgo, + /// TIM20 trigger out 2 + Tim_20_trgo_2, + /// TIM20 compare channel 1 + Tim_20_cc_1, + /// hrtim_adc_trg2 + Hrtim_adc_trg_2, + /// hrtim_adc_trg4 + Hrtim_adc_trg_4, + /// hrtim_adc_trg1 + Hrtim_adc_trg_1, + /// hrtim_adc_trg3 + Hrtim_adc_trg_3, + /// hrtim_adc_trg5 + Hrtim_adc_trg_5, + /// hrtim_adc_trg6 + Hrtim_adc_trg_6, + /// hrtim_adc_trg7 + Hrtim_adc_trg_7, + /// hrtim_adc_trg8 + Hrtim_adc_trg_8, + /// hrtim_adc_trg9 + Hrtim_adc_trg_9, + /// hrtim_adc_trg10 + Hrtim_adc_trg_10, + /// LP_timeout + Lp_timeout, + /// TIM7 trigger out + Tim_7_trgo, +} + +impl From for u8 { + fn from(et: ExternalTrigger12) -> u8 { + match et { + ExternalTrigger12::Tim_1_cc_1 => 0b00000, + ExternalTrigger12::Tim_1_cc_2 => 0b00001, + ExternalTrigger12::Tim_1_cc_3 => 0b00010, + ExternalTrigger12::Tim_2_cc_2 => 0b00011, + ExternalTrigger12::Tim_3_trgo => 0b00100, + ExternalTrigger12::Tim_4_cc_4 => 0b00101, + ExternalTrigger12::Exti_11 => 0b00110, + ExternalTrigger12::Tim_8_trgo => 0b00111, + ExternalTrigger12::Tim_8_trgo_2 => 0b01000, + ExternalTrigger12::Tim_1_trgo => 0b01001, + ExternalTrigger12::Tim_1_trgo_2 => 0b01010, + ExternalTrigger12::Tim_2_trgo => 0b01011, + ExternalTrigger12::Tim_4_trgo => 0b01100, + ExternalTrigger12::Tim_6_trgo => 0b01101, + ExternalTrigger12::Tim_15_trgo => 0b01110, + ExternalTrigger12::Tim_3_cc_4 => 0b01111, + ExternalTrigger12::Tim_20_trgo => 0b10000, + ExternalTrigger12::Tim_20_trgo_2 => 0b10001, + ExternalTrigger12::Tim_20_cc_1 => 0b10010, + ExternalTrigger12::Tim_20_cc_2 => 0b10011, + ExternalTrigger12::Tim_20_cc_3 => 0b10100, + ExternalTrigger12::Hrtim_adc_trg_1 => 0b10101, + ExternalTrigger12::Hrtim_adc_trg_3 => 0b10110, + ExternalTrigger12::Hrtim_adc_trg_5 => 0b10111, + ExternalTrigger12::Hrtim_adc_trg_6 => 0b11000, + ExternalTrigger12::Hrtim_adc_trg_7 => 0b11001, + ExternalTrigger12::Hrtim_adc_trg_8 => 0b11010, + ExternalTrigger12::Hrtim_adc_trg_9 => 0b11011, + ExternalTrigger12::Hrtim_adc_trg_10 => 0b11100, + ExternalTrigger12::Lp_timeout => 0b11101, + ExternalTrigger12::Tim_7_trgo => 0b11110, + // Reserved => 0b11111 + } + } +} + +#[cfg(feature = "adc3")] +impl From for u8 { + fn from(et: ExternalTrigger345) -> u8 { + match et { + ExternalTrigger345::Tim_3_cc_1 => 0b00000, + ExternalTrigger345::Tim_2_cc_3 => 0b00001, + ExternalTrigger345::Tim_1_cc_3 => 0b00010, + ExternalTrigger345::Tim_8_cc_1 => 0b00011, + ExternalTrigger345::Tim_3_trgo => 0b00100, + ExternalTrigger345::Exti_2 => 0b00101, + ExternalTrigger345::Tim_4_cc_1 => 0b00110, + ExternalTrigger345::Tim_8_trgo => 0b00111, + ExternalTrigger345::Tim_8_trgo_2 => 0b01000, + ExternalTrigger345::Tim_1_trgo => 0b01001, + ExternalTrigger345::Tim_1_trgo_2 => 0b01010, + ExternalTrigger345::Tim_2_trgo => 0b01011, + ExternalTrigger345::Tim_4_trgo => 0b01100, + ExternalTrigger345::Tim_6_trgo => 0b01101, + ExternalTrigger345::Tim_15_trgo => 0b01110, + ExternalTrigger345::Tim_2_cc_1 => 0b01111, + ExternalTrigger345::Tim_20_trgo => 0b10000, + ExternalTrigger345::Tim_20_trgo_2 => 0b10001, + ExternalTrigger345::Tim_20_cc_1 => 0b10010, + ExternalTrigger345::Hrtim_adc_trg_2 => 0b10011, + ExternalTrigger345::Hrtim_adc_trg_4 => 0b10100, + ExternalTrigger345::Hrtim_adc_trg_1 => 0b10101, + ExternalTrigger345::Hrtim_adc_trg_3 => 0b10110, + ExternalTrigger345::Hrtim_adc_trg_5 => 0b10111, + ExternalTrigger345::Hrtim_adc_trg_6 => 0b11000, + ExternalTrigger345::Hrtim_adc_trg_7 => 0b11001, + ExternalTrigger345::Hrtim_adc_trg_8 => 0b11010, + ExternalTrigger345::Hrtim_adc_trg_9 => 0b11011, + ExternalTrigger345::Hrtim_adc_trg_10 => 0b11100, + ExternalTrigger345::Lp_timeout => 0b11101, + ExternalTrigger345::Tim_7_trgo => 0b11110, + // Reserved => 0b11111 + } + } +} + +/// Possible oversampling shift +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum OverSamplingShift { + /// No right shift + NoShift, + /// Shift of 1 toward the right + Shift_1, + /// Shift of 2 toward the right + Shift_2, + /// Shift of 3 toward the right + Shift_3, + /// Shift of 4 toward the right + Shift_4, + /// Shift of 5 toward the right + Shift_5, + /// Shift of 6 toward the right + Shift_6, + /// Shift of 7 toward the right + Shift_7, + /// Shift of 8 toward the right + Shift_8, +} +impl From for u8 { + fn from(oss: OverSamplingShift) -> u8 { + match oss { + OverSamplingShift::NoShift => 0, + OverSamplingShift::Shift_1 => 1, + OverSamplingShift::Shift_2 => 2, + OverSamplingShift::Shift_3 => 3, + OverSamplingShift::Shift_4 => 4, + OverSamplingShift::Shift_5 => 5, + OverSamplingShift::Shift_6 => 6, + OverSamplingShift::Shift_7 => 7, + OverSamplingShift::Shift_8 => 8, + } + } +} + +/// Possible oversampling modes +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum OverSampling { + /// Oversampling 2x + Ratio_2, + /// Oversampling 4x + Ratio_4, + /// Oversampling 8x + Ratio_8, + /// Oversampling 16x + Ratio_16, + /// Oversampling 32x + Ratio_32, + /// Oversampling 64x + Ratio_64, + /// Oversampling 128x + Ratio_128, + /// Oversampling 256x + Ratio_256, +} +impl From for u8 { + fn from(os: OverSampling) -> u8 { + match os { + OverSampling::Ratio_2 => 0, + OverSampling::Ratio_4 => 1, + OverSampling::Ratio_8 => 2, + OverSampling::Ratio_16 => 3, + OverSampling::Ratio_32 => 4, + OverSampling::Ratio_64 => 5, + OverSampling::Ratio_128 => 6, + OverSampling::Ratio_256 => 7, + } + } +} + +/// Possible trigger modes +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum TriggerMode { + /// Don't listen to external trigger + Disabled, + /// Listen for rising edges of external trigger + RisingEdge, + /// Listen for falling edges of external trigger + FallingEdge, + /// Listen for both rising and falling edges of external trigger + BothEdges, +} +impl From for u8 { + fn from(tm: TriggerMode) -> u8 { + match tm { + TriggerMode::Disabled => 0, + TriggerMode::RisingEdge => 1, + TriggerMode::FallingEdge => 2, + TriggerMode::BothEdges => 3, + } + } +} + +/// Data register alignment +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum Align { + /// Right align output data + Right, + /// Left align output data + Left, +} +impl From for bool { + fn from(a: Align) -> bool { + match a { + Align::Right => false, + Align::Left => true, + } + } +} + +/// Continuous mode enable/disable +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Continuous { + /// Single mode, continuous disabled + Single, + /// Continuous mode enabled + Continuous, + + /// Discontinuous mode enabled + /// + /// Will perform `subgroup_len` number samples per trigger + Discontinuous, +} + +/// Number of channels to sample per trigger in discontinuous mode +/// +/// NOTE: This only applies to discontinuous +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum SubGroupLength { + /// One single sample per trigger + One = 0b000, + + /// Two samples per trigger + Two = 0b001, + + /// Three samples per trigger + Three = 0b010, + + /// Four samples per trigger + Four = 0b011, + + /// Five samples per trigger + Five = 0b100, + + /// Six samples per trigger + Six = 0b101, + + /// Seven samples per trigger + Seven = 0b110, + + /// Eight samples per trigger + Eight = 0b111, +} + +/// DMA mode +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum Dma { + /// No DMA, disabled + Disabled, + /// Single DMA, DMA will be disabled after each conversion sequence + Single, + /// Continuous DMA, DMA will remain enabled after conversion + Continuous, +} + +/// End-of-conversion interrupt enabled/disabled +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum Eoc { + /// End-of-conversion interrupt disabled + Disabled, + /// End-of-conversion interrupt enabled per conversion + Conversion, + /// End-of-conversion interrupt enabled per sequence + Sequence, +} + +/// Input Type Selection +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub enum InputType { + /// Single-Ended Input Channels + SingleEnded, + /// Differential Input Channels + Differential, +} +impl From for bool { + fn from(it: InputType) -> bool { + match it { + InputType::SingleEnded => false, + InputType::Differential => true, + } + } +} + +/// Sets the input type per channel +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy, Default)] +pub struct DifferentialSelection(pub(crate) u32); +impl DifferentialSelection { + /// Set pin to Single-Ended or Differential + pub fn set(&mut self, it: InputType) + where + PIN: Channel, + { + match it { + InputType::SingleEnded => { + self.singleended_by_id(PIN::channel()); + } + InputType::Differential => { + self.differential_by_id(PIN::channel()); + } + } + } + + /// Set to single ended by id + fn singleended_by_id(&mut self, id: u8) { + self.0 &= !(1 << id); + } + + /// Set to differential by id + fn differential_by_id(&mut self, id: u8) { + self.0 |= 1 << id; + } + + /// get the differential setting of channel + pub fn get_channel(&self, channel: u8) -> InputType { + if self.0 & (1 << channel) > 0 { + InputType::Differential + } else { + InputType::SingleEnded + } + } + + /// Sets all channels to SingleEnded + pub fn clear_all(&mut self) { + self.0 = 0; + } +} + +/// Configuration for the adc. +/// There are some additional parameters on the adc peripheral that can be +/// added here when needed but this covers several basic usecases. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Copy, Clone)] +pub struct AdcConfig { + pub(crate) resolution: Resolution, + pub(crate) align: Align, + pub(crate) external_trigger: (TriggerMode, ET), + pub(crate) continuous: Continuous, + pub(crate) subgroup_len: SubGroupLength, + pub(crate) dma: Dma, + pub(crate) end_of_conversion_interrupt: Eoc, + pub(crate) overrun_interrupt: bool, + pub(crate) default_sample_time: SampleTime, + pub(crate) vdda: Option, + pub(crate) auto_delay: bool, + + /// Sets the differential input type of the Adc + pub difsel: DifferentialSelection, +} + +impl AdcConfig { + /// change the resolution field + #[inline(always)] + pub fn resolution(mut self, resolution: Resolution) -> Self { + self.resolution = resolution; + self + } + /// change the align field + #[inline(always)] + pub fn align(mut self, align: Align) -> Self { + self.align = align; + self + } + /// change the continuous field + #[inline(always)] + pub fn continuous(mut self, continuous: Continuous) -> Self { + self.continuous = continuous; + self + } + + /// change the subgroup_len field, only relevant in discontinous mode + pub fn subgroup_len(mut self, subgroup_len: SubGroupLength) -> Self { + self.subgroup_len = subgroup_len; + self + } + + /// change the dma field + #[inline(always)] + pub fn dma(mut self, dma: Dma) -> Self { + self.dma = dma; + self + } + /// change the end_of_conversion_interrupt field + #[inline(always)] + pub fn end_of_conversion_interrupt(mut self, end_of_conversion_interrupt: Eoc) -> Self { + self.end_of_conversion_interrupt = end_of_conversion_interrupt; + self + } + + /// Enable/disable overrun interrupt + /// + /// This is triggered when the AD finishes a conversion before the last value was read by CPU/DMA + pub fn overrun_interrupt(mut self, enable: bool) -> Self { + self.overrun_interrupt = enable; + self + } + + /// change the default_sample_time field + #[inline(always)] + pub fn default_sample_time(mut self, default_sample_time: SampleTime) -> Self { + self.default_sample_time = default_sample_time; + self + } + + /// Specify the reference voltage for the ADC. + /// + /// # Args + /// * `vdda_mv` - The ADC reference voltage in millivolts. + #[inline(always)] + pub fn reference_voltage(mut self, vdda_mv: u32) -> Self { + self.vdda = Some(vdda_mv); + self + } + + /// Specify the single-ened or differential channel selection + #[inline(always)] + pub fn difsel(mut self, df: DifferentialSelection) -> Self { + self.difsel = df; + self + } + + /// Enable of disable the auto delay function + #[inline(always)] + pub fn auto_delay(mut self, delay: bool) -> Self { + self.auto_delay = delay; + self + } +} + +impl AdcConfig { + /// change the external_trigger field + #[inline(always)] + pub fn external_trigger( + mut self, + trigger_mode: TriggerMode, + trigger: ExternalTrigger12, + ) -> Self { + self.external_trigger = (trigger_mode, trigger); + self + } +} + +#[cfg(feature = "adc3")] +impl AdcConfig { + /// change the external_trigger field + #[inline(always)] + pub fn external_trigger( + mut self, + trigger_mode: TriggerMode, + trigger: ExternalTrigger345, + ) -> Self { + self.external_trigger = (trigger_mode, trigger); + self + } +} + +impl Default for AdcConfig { + fn default() -> Self { + Self { + resolution: Resolution::Twelve, + align: Align::Right, + external_trigger: (TriggerMode::Disabled, ET::default()), + continuous: Continuous::Single, + subgroup_len: SubGroupLength::One, + dma: Dma::Disabled, + end_of_conversion_interrupt: Eoc::Disabled, + overrun_interrupt: false, + default_sample_time: SampleTime::Cycles_640_5, + vdda: None, + difsel: DifferentialSelection::default(), + auto_delay: false, + } + } +} diff --git a/src/adc/g4.rs b/src/adc/g4.rs new file mode 100644 index 00000000..3f448c0d --- /dev/null +++ b/src/adc/g4.rs @@ -0,0 +1,245 @@ +use crate::{ + gpio::{gpioa, gpiob, gpioc, gpiof, Analog}, + opamp, +}; + +#[cfg(feature = "adc3")] +use crate::gpio::{gpiod, gpioe}; + +use super::{adc_channel_helper, adc_opamp, adc_pins, temperature::Temperature, Vbat, Vref}; + +#[cfg(any(feature = "stm32g431", feature = "stm32g441"))] +adc_pins!( + gpioa::PA0 => (ADC1, 1), + gpioa::PA0 => (ADC2, 1), + gpioa::PA1 => (ADC1, 2), + gpioa::PA1 => (ADC2, 2), + gpioa::PA2 => (ADC1, 3), + gpioa::PA3 => (ADC1, 4), + gpioa::PA4 => (ADC2, 17), + gpioa::PA5 => (ADC2, 13), + gpioa::PA6 => (ADC2, 3), + gpioa::PA7 => (ADC2, 4), + + gpiob::PB0 => (ADC1, 15), + gpiob::PB1 => (ADC1, 12), + gpiob::PB2 => (ADC2, 12), + gpiob::PB11 => (ADC1, 14), + gpiob::PB11 => (ADC2, 14), + gpiob::PB12 => (ADC1, 11), + gpiob::PB15 => (ADC2, 15), + + gpioc::PC0 => (ADC1, 6), + gpioc::PC0 => (ADC2, 6), + gpioc::PC1 => (ADC1, 7), + gpioc::PC1 => (ADC2, 7), + gpioc::PC2 => (ADC1, 8), + gpioc::PC2 => (ADC2, 8), + gpioc::PC3 => (ADC1, 9), + gpioc::PC3 => (ADC2, 9), + gpioc::PC4 => (ADC2, 5), + gpioc::PC5 => (ADC2, 11), + + gpiof::PF0 => (ADC1, 10), + gpiof::PF1 => (ADC2, 10), + + Temperature => (ADC1, 16), + Vbat => (ADC1, 17), + Vref => (ADC1, 18), +); + +#[cfg(any( + feature = "stm32g473", + feature = "stm32g474", + feature = "stm32g483", + feature = "stm32g484", +))] +adc_pins!( + gpioa::PA0 => (ADC1, 1), + gpioa::PA0 => (ADC2, 1), + gpioa::PA1 => (ADC1, 2), + gpioa::PA1 => (ADC2, 2), + gpioa::PA2 => (ADC1, 3), + gpioa::PA3 => (ADC1, 4), + gpioa::PA4 => (ADC2, 17), + gpioa::PA5 => (ADC2, 13), + gpioa::PA6 => (ADC2, 3), + gpioa::PA7 => (ADC2, 4), + gpioa::PA8 => (ADC5, 1), + gpioa::PA9 => (ADC5, 2), + + gpiob::PB0 => (ADC3, 12), + gpiob::PB0 => (ADC1, 15), + gpiob::PB1 => (ADC3, 1), + gpiob::PB1 => (ADC1, 12), + gpiob::PB2 => (ADC2, 12), + gpiob::PB11 => (ADC1, 14), + gpiob::PB11 => (ADC2, 14), + gpiob::PB12 => (ADC4, 3), + gpiob::PB12 => (ADC1, 11), + gpiob::PB13 => (ADC3, 5), + gpiob::PB14 => (ADC4, 4), + gpiob::PB14 => (ADC1, 5), + gpiob::PB15 => (ADC4, 5), + gpiob::PB15 => (ADC2, 15), + + gpioc::PC0 => (ADC1, 6), + gpioc::PC0 => (ADC2, 6), + gpioc::PC1 => (ADC1, 7), + gpioc::PC1 => (ADC2, 7), + gpioc::PC2 => (ADC1, 8), + gpioc::PC2 => (ADC2, 8), + gpioc::PC3 => (ADC1, 9), + gpioc::PC3 => (ADC2, 9), + gpioc::PC4 => (ADC2, 5), + gpioc::PC5 => (ADC2, 11), + + gpioe::PE7 => (ADC3, 4), + gpioe::PE8 => (ADC3, 6), + gpioe::PE8 => (ADC4, 6), + gpioe::PE8 => (ADC5, 6), + gpioe::PE9 => (ADC3, 2), + gpioe::PE10 => (ADC3, 14), + gpioe::PE10 => (ADC4, 14), + gpioe::PE10 => (ADC5, 14), + gpioe::PE11 => (ADC3, 15), + gpioe::PE11 => (ADC4, 15), + gpioe::PE11 => (ADC5, 15), + gpioe::PE12 => (ADC3, 16), + gpioe::PE12 => (ADC4, 16), + gpioe::PE12 => (ADC5, 16), + gpioe::PE13 => (ADC3, 3), + gpioe::PE14 => (ADC4, 1), + gpioe::PE15 => (ADC4, 2), + + gpiod::PD8 => (ADC4, 12), + gpiod::PD8 => (ADC5, 12), + gpiod::PD9 => (ADC4, 13), + gpiod::PD9 => (ADC5, 13), + gpiod::PD10 => (ADC3, 7), + gpiod::PD10 => (ADC4, 7), + gpiod::PD10 => (ADC5, 7), + gpiod::PD11 => (ADC3, 8), + gpiod::PD11 => (ADC4, 8), + gpiod::PD11 => (ADC5, 8), + gpiod::PD12 => (ADC3, 9), + gpiod::PD12 => (ADC4, 9), + gpiod::PD12 => (ADC5, 9), + gpiod::PD13 => (ADC3, 10), + gpiod::PD13 => (ADC4, 10), + gpiod::PD13 => (ADC5, 10), + gpiod::PD14 => (ADC3, 11), + gpiod::PD14 => (ADC4, 11), + gpiod::PD14 => (ADC5, 11), + + gpiof::PF0 => (ADC1, 10), + gpiof::PF1 => (ADC2, 10), + + Temperature => (ADC1, 16), + Vbat => (ADC1, 17), + Vbat => (ADC3, 17), + Vbat => (ADC5, 17), + Vref => (ADC1, 18), + Vref => (ADC3, 18), + Vref => (ADC4, 18), + Vref => (ADC5, 18), +); + +// See https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#page=782 +adc_opamp!( + // TODO: Add all opamp types: OpenLoop, Follower(for all opamps) + // TODO: Should we restrict type parameters A and B? + // TODO: Also allow AD-channels shared by pins + opamp::Opamp1 => (ADC1, 13), + opamp::Opamp2 => (ADC2, 16), + opamp::Opamp3 => (ADC2, 18), +); + +#[cfg(any( + feature = "stm32g473", + feature = "stm32g474", + feature = "stm32g483", + feature = "stm32g484", + feature = "stm32g491", + feature = "stm32g4a1", +))] +adc_opamp!( + opamp::Opamp3 => (ADC3, 13), +); + +#[cfg(any( + feature = "stm32g473", + feature = "stm32g474", + feature = "stm32g483", + feature = "stm32g484", +))] +adc_opamp!( + opamp::Opamp4 => (ADC5, 5), + opamp::Opamp5 => (ADC5, 3), + opamp::Opamp6 => (ADC4, 17), +); + +#[cfg(any(feature = "stm32g491", feature = "stm32g4a1",))] +adc_opamp!( + opamp::Opamp6 => (ADC3, 17), +); + +#[cfg(any(feature = "stm32g491", feature = "stm32g4a1",))] +adc_pins!( + gpioa::PA0 => (ADC1, 1), + gpioa::PA0 => (ADC2, 1), + gpioa::PA1 => (ADC1, 2), + gpioa::PA1 => (ADC2, 2), + gpioa::PA2 => (ADC1, 3), + gpioa::PA3 => (ADC1, 4), + gpioa::PA4 => (ADC2, 17), + gpioa::PA5 => (ADC2, 13), + gpioa::PA6 => (ADC2, 3), + gpioa::PA7 => (ADC2, 4), + + gpiob::PB0 => (ADC3, 12), + gpiob::PB0 => (ADC1, 15), + gpiob::PB1 => (ADC3, 1), + gpiob::PB1 => (ADC1, 12), + gpiob::PB2 => (ADC2, 12), + gpiob::PB11 => (ADC1, 14), + gpiob::PB11 => (ADC2, 14), + gpiob::PB12 => (ADC1, 11), + gpiob::PB13 => (ADC3, 5), + gpiob::PB14 => (ADC1, 5), + gpiob::PB15 => (ADC2, 15), + + gpioc::PC0 => (ADC1, 6), + gpioc::PC0 => (ADC2, 6), + gpioc::PC1 => (ADC1, 7), + gpioc::PC1 => (ADC2, 7), + gpioc::PC2 => (ADC1, 8), + gpioc::PC2 => (ADC2, 8), + gpioc::PC3 => (ADC1, 9), + gpioc::PC3 => (ADC2, 9), + gpioc::PC4 => (ADC2, 5), + gpioc::PC5 => (ADC2, 11), + + gpioe::PE7 => (ADC3, 4), + gpioe::PE8 => (ADC3, 6), + gpioe::PE9 => (ADC3, 2), + gpioe::PE10 => (ADC3, 14), + gpioe::PE11 => (ADC3, 15), + gpioe::PE12 => (ADC3, 16), + gpioe::PE13 => (ADC3, 3), + + gpiod::PD10 => (ADC3, 7), + gpiod::PD11 => (ADC3, 8), + gpiod::PD12 => (ADC3, 9), + gpiod::PD13 => (ADC3, 10), + gpiod::PD14 => (ADC3, 11), + + gpiof::PF0 => (ADC1, 10), + gpiof::PF1 => (ADC2, 10), + + Temperature => (ADC1, 16), + Vbat => (ADC1, 17), + Vbat => (ADC3, 17), + Vref => (ADC1, 18), + Vref => (ADC3, 18), +); diff --git a/src/adc/mod.rs b/src/adc/mod.rs new file mode 100644 index 00000000..e1442216 --- /dev/null +++ b/src/adc/mod.rs @@ -0,0 +1,1517 @@ +//! Analog to digital converter configuration. +//! + +#![deny(missing_docs)] + +mod g4; + +/// Holds config related types for setting up the ADC's +pub mod config; + +/// Temperature related types used for reading the device's internal temperature +pub mod temperature; + +use crate::stm32::rcc::ccipr; +pub use crate::time::U32Ext as _; +use crate::{ + dma::{mux::DmaMuxResources, traits::TargetAddress, PeripheralToMemory}, + rcc::Rcc, + signature::VDDA_CALIB, + stm32, +}; +use crate::{rcc, stasis}; +use core::marker::PhantomData; +use core::{fmt, ops::Deref}; +use embedded_hal::delay::DelayNs; +use embedded_hal_old::adc::{Channel, OneShot}; + +/// Extension trait for `ADC12_COMMON` and `ADC345_COMMON` +/// +/// These types hold some configuration which is common to all +/// ADCs in that group such as clock configuration. +pub trait AdcCommonExt: + Deref + Sized + rcc::Enable + rcc::Reset +{ + /// Pointer to the underlaying register block + const PTR: *const stm32::adc12_common::RegisterBlock; + + /// Setup and initialize the adc group + fn claim(self, cfg: config::ClockMode, rcc: &mut Rcc) -> AdcCommon; +} + +fn init_adc_common( + adc_common: ADCC, + rcc: &mut rcc::Rcc, + cfg: config::ClockMode, + adcsel: impl FnOnce( + &mut stm32g4::raw::W, + ) -> stm32::rcc::ccipr::ADC12SEL_W, +) -> AdcCommon { + let cfg = cfg.to_bits(rcc); + + unsafe { + let rcc_ptr = &(*stm32::RCC::ptr()); + ADCC::enable(rcc_ptr); + ADCC::reset(rcc_ptr); + } + + // Select system clock as ADC clock source + rcc.rb + .ccipr() + .modify(|_, w: &mut stm32g4::raw::W| { + // This is sound, as `0b10` is a valid value for this field. + unsafe { + adcsel(w).bits(cfg.adcsel); + } + + w + }); + + adc_common + .ccr() + .modify(|_, w| unsafe { w.ckmode().bits(cfg.ckmode) }); + adc_common + .ccr() + .modify(|_, w| unsafe { w.presc().bits(cfg.presc) }); + + AdcCommon { reg: adc_common } +} + +impl AdcCommonExt for stm32::ADC12_COMMON { + const PTR: *const stm32::adc12_common::RegisterBlock = Self::ptr(); + + fn claim(self, cfg: config::ClockMode, rcc: &mut Rcc) -> AdcCommon { + init_adc_common(self, rcc, cfg, |w| w.adc12sel()) + } +} + +#[cfg(feature = "adc3")] +impl AdcCommonExt for stm32::ADC345_COMMON { + const PTR: *const stm32::adc12_common::RegisterBlock = Self::ptr(); + + fn claim(self, cfg: config::ClockMode, rcc: &mut Rcc) -> AdcCommon { + init_adc_common(self, rcc, cfg, |w| w.adc345sel()) + } +} + +/// Type for initialized `ADC12_COMMON` or `ADC345_COMMON` +/// +/// See [`AdcCommon::claim`] +pub struct AdcCommon { + reg: ADCC, +} + +/// Marker trait for all ADC peripherals +pub trait Instance: crate::Sealed + Deref { + /// Specifies adc common register block for configuration common to this and other ADC in the same group + type Common: AdcCommonExt; + + /// Specifies what External trigger type the ADC uses + type ExternalTrigger: fmt::Debug + Default + Copy + Into; + + /// Specifies what dma mux resource this ADC is + const DMA_MUX_RESOURCE: DmaMuxResources; +} + +impl Instance for stm32::ADC1 { + type Common = stm32::ADC12_COMMON; + type ExternalTrigger = config::ExternalTrigger12; + const DMA_MUX_RESOURCE: DmaMuxResources = DmaMuxResources::ADC1; +} +impl Instance for stm32::ADC2 { + type Common = stm32::ADC12_COMMON; + type ExternalTrigger = config::ExternalTrigger12; + const DMA_MUX_RESOURCE: DmaMuxResources = DmaMuxResources::ADC2; +} +#[cfg(feature = "adc3")] +impl Instance for stm32::ADC3 { + type Common = stm32::ADC345_COMMON; + type ExternalTrigger = config::ExternalTrigger345; + const DMA_MUX_RESOURCE: DmaMuxResources = DmaMuxResources::ADC3; +} +#[cfg(feature = "adc4")] +impl Instance for stm32::ADC4 { + type Common = stm32::ADC345_COMMON; + type ExternalTrigger = config::ExternalTrigger345; + const DMA_MUX_RESOURCE: DmaMuxResources = DmaMuxResources::ADC4; +} + +#[cfg(feature = "adc5")] +impl Instance for stm32::ADC5 { + type Common = stm32::ADC345_COMMON; + type ExternalTrigger = config::ExternalTrigger345; + const DMA_MUX_RESOURCE: DmaMuxResources = DmaMuxResources::ADC5; +} + +/// Vref internal signal, used for calibration +pub struct Vref; +impl Vref { + /// Converts a sample value to millivolts using calibrated VDDA and configured resolution + #[inline(always)] + pub fn sample_to_millivolts_ext(sample: u16, vdda: u32, resolution: config::Resolution) -> u16 { + let mx_s = resolution.to_max_sample(); + ((u32::from(sample) * vdda) / mx_s) as u16 + } + /// Converts a sample value to millivolts using calibrated VDDA and configured resolution + #[inline(always)] + pub fn sample_to_millivolts(sample: u16) -> u16 { + Self::sample_to_millivolts_ext(sample, VDDA_CALIB, config::Resolution::Twelve) + } +} +impl stasis::Freeze for Vref {} + +/// Vbat internal signal, used for monitoring the battery (if used) +pub struct Vbat; +impl stasis::Freeze for Vbat {} + +// TODO: Is there any way to avoid this wrapper to overcome the orphan rule +/// Wrapper to side step orphan rule +pub struct Ad(T); +macro_rules! adc_channel_helper { + ($adc:ident, $chan:expr, $r:ty, $($a:ident),*) => { + impl<$($a,)*> embedded_hal_old::adc::Channel> for crate::stasis::Entitlement<$r> { + type ID = u8; + fn channel() -> u8 { + $chan + } + } + + impl embedded_hal_old::adc::Channel> for crate::stasis::Frozen<$r, N> { + type ID = u8; + fn channel() -> u8 { + $chan + } + } + + impl<$($a,)*> embedded_hal_old::adc::Channel> for $r { + type ID = u8; + fn channel() -> u8 { + $chan + } + } + }; +} + +use adc_channel_helper; + +macro_rules! adc_pins { + ($($pin:ty => ($adc:ident, $chan:expr)),+ $(,)*) => { + $( + adc_channel_helper!($adc, $chan, $pin,); + )+ + }; +} + +use adc_pins; + +macro_rules! adc_opamp { + ($($opamp:ty => ($adc:ident, $chan:expr)),+ $(,)*) => { + $( + adc_channel_helper!($adc, $chan, opamp::Follower<$opamp, A, opamp::InternalOutput>, A); + adc_channel_helper!($adc, $chan, opamp::OpenLoop<$opamp, A, B, opamp::InternalOutput>, A, B); + adc_channel_helper!($adc, $chan, opamp::Pga<$opamp, A, opamp::InternalOutput>, A); + adc_channel_helper!($adc, $chan, opamp::Locked<$opamp, opamp::InternalOutput>,); + )+ + }; +} + +use adc_opamp; + +/// Type-State for Adc, indicating a deep-powered-down-pheripheral +#[derive(Debug)] +pub struct PoweredDown; +/// Type-State for Adc, indicating a non-configured peripheral +#[derive(Debug)] +pub struct Disabled; +/// Type-State for Adc, indicating a configured peripheral +#[derive(Debug)] +pub struct Configured; +/// Type-State for Adc, indicating an peripheral configured for DMA +#[derive(Debug)] +pub struct DMA; +/// Type-State for Adc, indicating am active measuring peripheral +#[derive(Debug)] +pub struct Active; + +/// Enum for the wait_for_conversion_sequence function, +/// which can return either a stopped ADC typestate or a +/// continuing ADC typestate. +pub enum Conversion { + /// Contains an Active Conversion ADC + Active(Adc), + /// Contains an Stopped ADC + Stopped(Adc), +} +impl Conversion { + /// unwraps the enum and panics if the result is not an Active ADC + #[inline(always)] + pub fn unwrap_active(self) -> Adc { + match self { + Conversion::Active(adc) => adc, + _ => { + panic!("Conversion is Stopped, not Active!") + } + } + } + + /// unwraps the enum and panics if the result is not a Stopped ADC + #[inline(always)] + pub fn unwrap_stopped(self) -> Adc { + match self { + Conversion::Stopped(adc) => adc, + _ => { + panic!("Conversion is Continuing, not Stopped!") + } + } + } + + /// Returns true if the adc is Active. + #[inline(always)] + pub fn is_active(&self) -> bool { + match *self { + Conversion::Active(..) => true, + Conversion::Stopped(..) => false, + } + } + + /// Returns true if the adc is Stopped. + #[inline(always)] + pub fn is_stopped(&self) -> bool { + !self.is_active() + } + + /// Converts from `Conversion` to `Option`. + /// + /// Converts self into an `Option`, consuming self, and discarding the adc, if it is stopped. + #[inline(always)] + pub fn active(self) -> Option> { + match self { + Conversion::Active(adc) => Some(adc), + Conversion::Stopped(..) => None, + } + } + + /// Converts from `Conversion` to `Option`. + /// + /// Converts self into an `Option`, consuming self, and discarding the adc, if it is still active. + #[inline(always)] + pub fn stopped(self) -> Option> { + match self { + Conversion::Active(..) => None, + Conversion::Stopped(adc) => Some(adc), + } + } +} + +/// 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::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) +/// //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::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 DynamicAdc { + /// Current config of the ADC, kept up to date by the various set methods + config: config::AdcConfig, + /// The adc peripheral + adc_reg: ADC, + /// VDDA in millivolts calculated from the factory calibration and vrefint + calibrated_vdda: u32, +} +impl fmt::Debug for DynamicAdc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "DynamicAdc: {{ calibrated_vdda: {:?}, {:?}, ... }}", + self.calibrated_vdda, self.config + ) + } +} + +/// Typestate wrapper around DynamicAdc +pub struct Adc { + adc: DynamicAdc, + _status: PhantomData, +} +impl fmt::Debug for Adc +where + STATUS: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Adc<{:?}>: {{ calibrated_vdda: {:?}, {:?}, ... }}", + self._status, self.adc.calibrated_vdda, self.adc.config + ) + } +} + +/// used to create an ADC instance from the stm32::Adc +pub trait AdcClaim { + /// create a disabled ADC instance from the stm32::Adc + fn claim(&self, adc: ADC, delay: &mut impl DelayNs) -> Adc; + + /// create an enabled ADC instance from the stm32::Adc + fn claim_and_configure( + &self, + adc: ADC, + config: config::AdcConfig, + delay: &mut impl DelayNs, + ) -> Adc; +} + +impl DynamicAdc { + /// Converts a sample value to millivolts using calibrated VDDA and configured resolution + #[inline(always)] + pub fn sample_to_millivolts(&self, sample: u16) -> u16 { + Vref::sample_to_millivolts_ext(sample, self.calibrated_vdda, self.config.resolution) + } + + /// Disables the Voltage Regulator and release the ADC + #[inline(always)] + pub fn release(mut self) -> ADC { + self.enable_deeppwd_down(); + + self.adc_reg + } + + /// Powers-up an powered-down Adc + #[inline(always)] + pub fn power_up(&mut self, delay: &mut impl DelayNs) { + if self.is_deeppwd_enabled() { + self.disable_deeppwd_down(); + } + if !self.is_vreg_enabled() { + self.enable_vreg(delay); + } + if self.is_enabled() { + self.disable(); + } + } + + /// Puts a Disabled Adc into Powered Mode + #[inline(always)] + pub fn power_down(&mut self) { + self.disable_vreg(); + } + + /// Enables the Deep Power Down Modus + #[inline(always)] + pub fn enable_deeppwd_down(&mut self) { + self.adc_reg.cr().modify(|_, w| w.deeppwd().set_bit()); + } + + /// Disables the Deep Power Down Modus + #[inline(always)] + pub fn disable_deeppwd_down(&mut self) { + self.adc_reg.cr().modify(|_, w| w.deeppwd().clear_bit()); + } + + /// Enables the Voltage Regulator + #[inline(always)] + pub fn enable_vreg(&mut self, delay: &mut impl DelayNs) { + self.adc_reg.cr().modify(|_, w| w.advregen().set_bit()); + while !self.adc_reg.cr().read().advregen().bit_is_set() {} + + // According to the STM32G4xx Reference Manual, section 21.4.6, we need + // to wait for T_ADCVREG_STUP after enabling the internal voltage + // regulator. For the STM32G431, this is 20 us. We choose 25 us to + // account for bad clocks. + delay.delay_us(25); + } + + /// Disables the Voltage Regulator + #[inline(always)] + pub fn disable_vreg(&mut self) { + self.adc_reg.cr().modify(|_, w| w.advregen().clear_bit()); + } + + /// Returns if the ADC is enabled (ADEN) + #[inline(always)] + pub fn is_enabled(&self) -> bool { + self.adc_reg.cr().read().aden().bit_is_set() + } + + /// Disables the adc, since we don't know in what state we get it. + #[inline(always)] + pub fn disable(&mut self) { + // Disable any ongoing conversions + self.cancel_conversion(); + + // Turn off ADC + self.adc_reg.cr().modify(|_, w| w.addis().set_bit()); + while self.adc_reg.cr().read().addis().bit_is_set() {} + + // Wait until the ADC has turned off + while self.adc_reg.cr().read().aden().bit_is_set() {} + } + + /// Enables the adc + #[inline(always)] + pub fn enable(&mut self) { + self.calibrate_all(); + self.apply_config(self.config); + + self.adc_reg.isr().modify(|_, w| w.adrdy().clear()); + self.adc_reg.cr().modify(|_, w| w.aden().set_bit()); + + // Wait for adc to get ready + while !self.adc_reg.isr().read().adrdy().bit_is_set() {} + + // Clear ready flag + self.adc_reg.isr().modify(|_, w| w.adrdy().clear()); + + self.clear_end_of_conversion_flag(); + } + + /// enable the adc and configure for DMA. + pub fn enable_dma(&mut self, dma: config::Dma) { + self.set_dma(dma); + self.enable(); + } + + /// Applies all fields in AdcConfig + #[inline(always)] + fn apply_config(&mut self, config: config::AdcConfig) { + self.set_resolution(config.resolution); + self.set_align(config.align); + self.set_external_trigger(config.external_trigger); + self.set_continuous(config.continuous); + self.set_subgroup_len(config.subgroup_len); + self.set_dma(config.dma); + self.set_end_of_conversion_interrupt(config.end_of_conversion_interrupt); + self.set_overrun_interrupt(config.overrun_interrupt); + self.set_default_sample_time(config.default_sample_time); + self.set_channel_input_type(config.difsel); + self.set_auto_delay(config.auto_delay); + + if let Some(vdda) = config.vdda { + self.calibrated_vdda = vdda; + } + } + + /// Sets the sampling resolution + #[inline(always)] + pub fn set_resolution(&mut self, resolution: config::Resolution) { + self.config.resolution = resolution; + unsafe { + self.adc_reg + .cfgr() + .modify(|_, w| w.res().bits(resolution.into())); + } + } + + /// Enable oversampling + #[inline(always)] + pub fn set_oversampling( + &mut self, + oversampling: config::OverSampling, + shift: config::OverSamplingShift, + ) { + self.adc_reg.cfgr2().modify(|_, w| unsafe { + w.ovsr() + .bits(oversampling.into()) + .ovss() + .bits(shift.into()) + .rovse() + .set_bit() + }); + } + + /// Sets the DR register alignment to left or right + #[inline(always)] + pub fn set_align(&mut self, align: config::Align) { + self.config.align = align; + self.adc_reg + .cfgr() + .modify(|_, w| w.align().bit(align.into())); + } + + /// Sets which external trigger to use and if it is disabled, rising, falling or both + #[inline(always)] + pub fn set_external_trigger( + &mut self, + (edge, extsel): (config::TriggerMode, ADC::ExternalTrigger), + ) { + self.config.external_trigger = (edge, extsel); + self.adc_reg + .cfgr() + .modify(|_, w| unsafe { w.extsel().bits(extsel.into()).exten().bits(edge.into()) }); + } + + /// Sets auto delay to true or false + #[inline(always)] + pub fn set_auto_delay(&mut self, delay: bool) { + self.config.auto_delay = delay; + self.adc_reg.cfgr().modify(|_, w| w.autdly().bit(delay)); + } + + /// Enables and disables dis-/continuous mode + #[inline(always)] + pub fn set_continuous(&mut self, continuous: config::Continuous) { + self.config.continuous = continuous; + self.adc_reg.cfgr().modify(|_, w| { + w.cont() + .bit(continuous == config::Continuous::Continuous) + .discen() + .bit(continuous == config::Continuous::Discontinuous) + }); + } + + #[inline(always)] + // NOTE: The software is allowed to write these bits only when ADSTART = 0 + fn set_subgroup_len(&mut self, subgroup_len: config::SubGroupLength) { + self.config.subgroup_len = subgroup_len; + unsafe { + self.adc_reg + .cfgr() + .modify(|_, w| w.discnum().bits(subgroup_len as u8)); + } + } + + /// Sets DMA to disabled, single or continuous + #[inline(always)] + pub fn set_dma(&mut self, dma: config::Dma) { + self.config.dma = dma; + let (dds, en) = match dma { + config::Dma::Disabled => (false, false), + config::Dma::Single => (false, true), + config::Dma::Continuous => (true, true), + }; + self.adc_reg.cfgr().modify(|_, w| { + w + //DDS stands for "DMA disable selection" + //0 means do one DMA then stop + //1 means keep sending DMA requests as long as DMA=1 + .dmacfg() + .bit(dds) + .dmaen() + .bit(en) + }); + } + + /// Sets if the end-of-conversion behaviour. + /// The end-of-conversion interrupt occur either per conversion or for the whole sequence. + #[inline(always)] + pub fn set_end_of_conversion_interrupt(&mut self, eoc: config::Eoc) { + self.config.end_of_conversion_interrupt = eoc; + let (en, eocs) = match eoc { + config::Eoc::Disabled => (false, false), + config::Eoc::Conversion => (true, true), + config::Eoc::Sequence => (true, false), + }; + self.adc_reg + .ier() + .modify(|_, w| w.eosie().bit(eocs).eocie().bit(en)); + } + + /// Enable/disable overrun interrupt + /// + /// This is triggered when the AD finishes a conversion before the last value was read by CPU/DMA + pub fn set_overrun_interrupt(&mut self, enable: bool) { + self.adc_reg.ier().modify(|_, w| w.ovrie().bit(enable)); + } + + /// Sets the default sample time that is used for one-shot conversions. + /// [configure_channel](#method.configure_channel) and [start_conversion](#method.start_conversion) can be \ + /// used for configurations where different sampling times are required per channel. + #[inline(always)] + pub fn set_default_sample_time(&mut self, sample_time: config::SampleTime) { + self.config.default_sample_time = sample_time; + } + + /// Sets the differential selection per channel. + #[inline(always)] + pub fn set_channel_input_type(&mut self, df: config::DifferentialSelection) { + self.config.difsel = df; + + self.adc_reg.difsel().modify(|_, w| { + for i in 0..19 { + w.difsel(i).bit(df.get_channel(i).into()); + } + w + }); + } + + /// Reset the sequence + #[inline(always)] + pub fn reset_sequence(&mut self) { + //The reset state is One conversion selected + self.adc_reg + .sqr1() + .modify(|_, w| unsafe { w.l().bits(config::Sequence::One.into()) }); + } + + /// Returns the current sequence length. Primarily useful for configuring DMA. + #[inline(always)] + pub fn sequence_length(&mut self) -> u8 { + self.adc_reg.sqr1().read().l().bits() + 1 + } + + /// Returns the address of the ADC data register. Primarily useful for configuring DMA. + #[inline(always)] + pub fn data_register_address(&self) -> u32 { + self.adc_reg.dr() as *const _ as u32 + } + + /// Calibrate the adc for + #[inline(always)] + pub fn calibrate(&mut self, it: config::InputType) { + match it { + config::InputType::SingleEnded => { + self.adc_reg.cr().modify(|_, w| w.adcaldif().clear_bit()); + } + config::InputType::Differential => { + self.adc_reg.cr().modify(|_, w| w.adcaldif().set_bit()); + } + } + + self.adc_reg.cr().modify(|_, w| w.adcal().set_bit()); + while self.adc_reg.cr().read().adcal().bit_is_set() {} + } + + /// Calibrate the Adc for all Input Types + #[inline(always)] + pub fn calibrate_all(&mut self) { + self.calibrate(config::InputType::Differential); + self.calibrate(config::InputType::SingleEnded); + } + + /// Configure a channel for sampling. + /// It will make sure the sequence is at least as long as the `sequence` provided. + /// # Arguments + /// * `channel` - channel to configure + /// * `sequence` - where in the sequence to sample the channel. Also called rank in some STM docs/code + /// * `sample_time` - how long to sample for. See datasheet and ref manual to work out how long you need\ + /// to sample for at a given ADC clock frequency + pub fn configure_channel( + &mut self, + _channel: &CHANNEL, + sequence: config::Sequence, + sample_time: config::SampleTime, + ) where + CHANNEL: Channel, ID = u8>, + { + //Check the sequence is long enough + self.adc_reg.sqr1().modify(|r, w| unsafe { + let prev: config::Sequence = r.l().bits().into(); + if prev < sequence { + w.l().bits(sequence.into()) + } else { + w + } + }); + + let ch = CHANNEL::channel(); + let reg_i = u8::from(sequence) / 4; + let i = u8::from(sequence) % 4; + + //Set the channel in the right sequence field + match reg_i { + 0 => self + .adc_reg + .sqr1() + .modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 1 => self + .adc_reg + .sqr2() + .modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 2 => self + .adc_reg + .sqr3() + .modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 3 => self + .adc_reg + .sqr4() + .modify(|_, w| unsafe { w.sq(i).bits(ch) }), + _ => unreachable!(), + }; + + //Set the sample time for the channel + let st = u8::from(sample_time); + unsafe { + match ch { + 0..=9 => self.adc_reg.smpr1().modify(|_, w| w.smp(ch).bits(st)), + 10.. => self.adc_reg.smpr2().modify(|_, w| w.smp(ch - 10).bits(st)), + }; + } + } + /// Synchronously convert a single sample + /// Note that it reconfigures the adc sequence and doesn't restore it + pub fn convert(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16 + where + PIN: Channel, ID = u8>, + { + let saved_config = self.config; + unsafe { + self.adc_reg.cfgr().modify( + |_, w| { + w.dmaen() + .clear_bit() //Disable dma + .cont() + .clear_bit() //Disable continuous mode + .exten() + .bits(config::TriggerMode::Disabled.into()) + }, //Disable trigger + ); + } + self.adc_reg.ier().modify( + |_, w| w.eocie().clear_bit(), //Disable end of conversion interrupt + ); + + self.enable(); + self.reset_sequence(); + self.configure_channel(pin, config::Sequence::One, sample_time); + self.start_conversion(); + + //Wait for the sequence to complete + self.wait_for_conversion_sequence(); + + let result = self.current_sample(); + + self.disable(); + + //Reset the config + self.apply_config(saved_config); + + result + } + + /// Resets the end-of-conversion flag + #[inline(always)] + pub fn clear_end_of_conversion_flag(&mut self) { + self.adc_reg.isr().modify(|_, w| w.eoc().clear()); + } + + /// Block until the conversion is completed and return to configured + pub fn wait_for_conversion_sequence(&mut self) { + while !self.adc_reg.isr().read().eoc().bit_is_set() {} + } + + /// get current sample + #[inline(always)] + pub fn current_sample(&self) -> u16 { + self.adc_reg.dr().read().rdata().bits() + } + + /// Starts conversion sequence. Waits for the hardware to indicate it's actually started. + #[inline(always)] + pub fn start_conversion(&mut self) { + //Start conversion + self.adc_reg.cr().modify(|_, w| w.adstart().set_bit()); + } + + /// Cancels an ongoing conversion + #[inline(always)] + pub fn cancel_conversion(&mut self) { + self.adc_reg.cr().modify(|_, w| w.adstp().set_bit()); + while self.adc_reg.cr().read().adstart().bit_is_set() {} + } + + /// Returns if the Voltage Regulator is enabled + #[inline(always)] + pub fn is_vreg_enabled(&self) -> bool { + self.adc_reg.cr().read().advregen().bit_is_set() + } + + /// Returns if Deep Power Down is enabled + #[inline(always)] + pub fn is_deeppwd_enabled(&self) -> bool { + self.adc_reg.cr().read().deeppwd().bit_is_set() + } + + /// Returns if a conversion is active + #[inline(always)] + pub fn is_conversion_active(&self) -> bool { + self.adc_reg.cr().read().adstart().bit_is_set() + } + + /// Read overrun flag + #[inline(always)] + pub fn get_overrun_flag(&self) -> bool { + self.adc_reg.isr().read().ovr().bit() + } + + /// Resets the overrun flag + #[inline(always)] + pub fn clear_overrun_flag(&mut self) { + self.adc_reg.isr().modify(|_, w| w.ovr().clear()); + } +} + +impl AdcClaim for AdcCommon { + /// Runs calibration and applies the supplied config + /// # Arguments + /// + /// TODO: fix needing SYST + #[inline(always)] + fn claim(&self, adc: ADC, delay: &mut impl DelayNs) -> Adc { + let dynadc = DynamicAdc { + config: config::AdcConfig::default(), + adc_reg: adc, + calibrated_vdda: VDDA_CALIB, + }; + + let adc: Adc = Adc { + adc: dynadc, + _status: PhantomData, + }; + + adc.power_up(delay) + } + + /// claims and configures the Adc + #[inline(always)] + fn claim_and_configure( + &self, + adc: ADC, + config: config::AdcConfig, + delay: &mut impl DelayNs, + ) -> Adc { + let mut adc = self.claim(adc, delay); + adc.adc.config = config; + + // If the user specified a VDDA, use that over the internally determined value. + if let Some(vdda) = config.vdda { + adc.adc.calibrated_vdda = vdda; + } + + adc.enable() + } +} + +impl AdcCommon { + /// Enables the vbat internal channel + #[inline(always)] + pub fn enable_vbat(&mut self) { + self.reg.ccr().modify(|_, w| w.vbatsel().set_bit()); + } + + /// Enables the vbat internal channel + #[inline(always)] + pub fn disable_vbat(&mut self) { + self.reg.ccr().modify(|_, w| w.vbatsel().clear_bit()); + } + + /// Returns if the vbat internal channel is enabled + #[inline(always)] + pub fn is_vbat_enabled(&mut self) -> bool { + self.reg.ccr().read().vbatsel().bit_is_set() + } + + /// Enables the temp internal channel. + #[inline(always)] + pub fn enable_temperature(&mut self) { + self.reg.ccr().modify(|_, w| w.vsensesel().set_bit()); + } + + /// Disables the temp internal channel + #[inline(always)] + pub fn disable_temperature(&mut self) { + self.reg.ccr().modify(|_, w| w.vsensesel().clear_bit()); + } + + /// Returns if the temp internal channel is enabled + #[inline(always)] + pub fn is_temperature_enabled(&mut self) -> bool { + self.reg.ccr().read().vsensesel().bit_is_set() + } + + /// Enables the vref internal channel. + #[inline(always)] + pub fn enable_vref(&mut self) { + self.reg.ccr().modify(|_, w| w.vrefen().set_bit()); + } + + /// Disables the vref internal channel + #[inline(always)] + pub fn disable_vref(&mut self) { + self.reg.ccr().modify(|_, w| w.vrefen().clear_bit()); + } + + /// Returns if the vref internal channel is enabled + #[inline(always)] + pub fn is_vref_enabled(&mut self) -> bool { + self.reg.ccr().read().vrefen().bit_is_set() + } +} + +impl Adc { + /// Converts a sample value to millivolts using calibrated VDDA and configured resolution + #[inline(always)] + pub fn sample_to_millivolts(&self, sample: u16) -> u16 { + self.adc.sample_to_millivolts(sample) + } +} + +impl Adc { + /// Powers-up an powered-down Adc + #[inline(always)] + pub fn power_up(mut self, delay: &mut impl DelayNs) -> Adc { + self.adc.power_up(delay); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// Puts a Disabled Adc into Powered Mode + #[inline(always)] + pub fn power_down(mut adc: Adc) -> Self { + adc.adc.power_down(); + + Adc { + adc: adc.adc, + _status: PhantomData, + } + } + + /// Disables the Voltage Regulator and release the ADC + #[inline(always)] + pub fn release(self) -> ADC { + self.adc.release() + } + + /// Releases the Adc as a DynamicAdc. + /// While this is not unsafe; using methods while the Adc is in the wrong state will mess it up. + #[inline(always)] + pub fn into_dynamic_adc(self) -> DynamicAdc { + self.adc + } + + /// Retrieves the DynamicAdc. + /// This will put the adc in power down state. + #[inline(always)] + pub fn from_dynamic_adc(mut dynadc: DynamicAdc) -> Self { + if dynadc.is_conversion_active() { + dynadc.cancel_conversion(); + } + if dynadc.is_enabled() { + dynadc.disable(); + } + if dynadc.is_deeppwd_enabled() { + dynadc.disable_deeppwd_down(); + } + + dynadc.power_down(); + + Adc { + adc: dynadc, + _status: PhantomData, + } + } + + /// Enables the Deep Power Down Modus + #[inline(always)] + pub fn enable_deeppwd_down(&mut self) { + self.adc.enable_deeppwd_down() + } + + /// Disables the Deep Power Down Modus + #[inline(always)] + pub fn disable_deeppwd_down(&mut self) { + self.adc.disable_deeppwd_down() + } +} + +impl Adc { + //adc!(additionals: $adc_type => ($common_type)); + //adc!(additionals_checks: $adc_type => ($common_type)); + + /// Enables the adc + #[inline(always)] + pub fn enable(mut self) -> Adc { + self.adc.enable(); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// Enables the adc + #[inline(always)] + pub fn configure_and_enable( + mut self, + config: config::AdcConfig, + ) -> Adc { + self.adc.apply_config(config); + self.enable() + } + + /// enable the adc and configure for DMA. + /// panics if set to Dma::Disabled + #[inline(always)] + pub fn enable_dma(mut self, dma: config::Dma) -> Adc { + if let config::Dma::Disabled = dma { + panic!("Requesting Enabling DMA with DisableDma parameter"); + } + + self.adc.enable_dma(dma); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// Puts a disabled Adc into PoweredDown Mode + #[inline(always)] + pub fn power_down(mut self) -> Adc { + self.adc.power_down(); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// Sets the oversampling + #[inline(always)] + pub fn set_oversampling( + &mut self, + oversampling: config::OverSampling, + shift: config::OverSamplingShift, + ) { + self.adc.set_oversampling(oversampling, shift) + } + + /// Sets the sampling resolution + #[inline(always)] + pub fn set_resolution(&mut self, resolution: config::Resolution) { + self.adc.set_resolution(resolution) + } + + /// Sets the DR register alignment to left or right + #[inline(always)] + pub fn set_align(&mut self, align: config::Align) { + self.adc.set_align(align) + } + + /// Sets which external trigger to use and if it is disabled, rising, falling or both + #[inline(always)] + pub fn set_external_trigger( + &mut self, + (edge, extsel): (config::TriggerMode, ADC::ExternalTrigger), + ) { + self.adc.set_external_trigger((edge, extsel)) + } + + /// Sets auto delay to true or false + #[inline(always)] + pub fn set_auto_delay(&mut self, delay: bool) { + self.adc.set_auto_delay(delay) + } + + /// Enables and disables continuous mode + #[inline(always)] + pub fn set_continuous(&mut self, continuous: config::Continuous) { + self.adc.set_continuous(continuous) + } + + /// Set subgroup length, number of AD readings per trigger event (only relevant in Discontinuous mode) + pub fn set_subgroup_len(&mut self, subgroup_len: config::SubGroupLength) { + self.adc.set_subgroup_len(subgroup_len); + } + + /// Sets DMA to disabled, single or continuous + #[inline(always)] + pub fn set_dma(&mut self, dma: config::Dma) { + self.adc.set_dma(dma) + } + + /// Sets if the end-of-conversion behaviour. + /// The end-of-conversion interrupt occur either per conversion or for the whole sequence. + #[inline(always)] + pub fn set_end_of_conversion_interrupt(&mut self, eoc: config::Eoc) { + self.adc.set_end_of_conversion_interrupt(eoc) + } + + /// Enable/disable overrun interrupt + /// + /// This is triggered when the AD finishes a conversion before the last value was read by CPU/DMA + #[inline(always)] + pub fn set_overrun_interrupt(&mut self, enable: bool) { + self.adc.set_overrun_interrupt(enable) + } + + /// Sets the default sample time that is used for one-shot conversions. + /// [configure_channel](#method.configure_channel) and [start_conversion](#method.start_conversion) can be \ + /// used for configurations where different sampling times are required per channel. + #[inline(always)] + pub fn set_default_sample_time(&mut self, sample_time: config::SampleTime) { + self.adc.set_default_sample_time(sample_time) + } + + /// Sets the differential selection per channel. + #[inline(always)] + pub fn set_channel_input_type(&mut self, df: config::DifferentialSelection) { + self.adc.set_channel_input_type(df) + } + + /// Reset the sequence + #[inline(always)] + pub fn reset_sequence(&mut self) { + self.adc.reset_sequence() + } + + /// Returns the current sequence length. Primarily useful for configuring DMA. + #[inline(always)] + pub fn sequence_length(&mut self) -> u8 { + self.adc.sequence_length() + } + + /// Calibrate the adc for + #[inline(always)] + pub fn calibrate(&mut self, it: config::InputType) { + self.adc.calibrate(it) + } + + /// Calibrate the Adc for all Input Types + #[inline(always)] + pub fn calibrate_all(&mut self) { + self.adc.calibrate_all(); + } + + /// Configure a channel for sampling. + /// It will make sure the sequence is at least as long as the `sequence` provided. + /// # Arguments + /// * `channel` - channel to configure + /// * `sequence` - where in the sequence to sample the channel. Also called rank in some STM docs/code + /// * `sample_time` - how long to sample for. See datasheet and ref manual to work out how long you need\ + /// to sample for at a given ADC clock frequency + #[inline(always)] + pub fn configure_channel( + &mut self, + channel: &CHANNEL, + sequence: config::Sequence, + sample_time: config::SampleTime, + ) where + CHANNEL: Channel, ID = u8>, + { + self.adc.configure_channel(channel, sequence, sample_time) + } + + /// Synchronously convert a single sample + /// Note that it reconfigures the adc sequence and doesn't restore it + #[inline(always)] + pub fn convert(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16 + where + PIN: Channel, ID = u8>, + { + self.adc.convert(pin, sample_time) + } +} + +impl Adc { + //adc!(additionals_checks: $adc_type => ($common_type)); + + /// Disables the adc + #[inline(always)] + pub fn disable(mut self) -> Adc { + self.adc.disable(); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// Starts conversion sequence. Waits for the hardware to indicate it's actually started. + #[inline(always)] + pub fn start_conversion(mut self) -> Adc { + self.adc.clear_end_of_conversion_flag(); + self.adc.start_conversion(); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// Returns the current sample stored in the ADC data register + #[inline(always)] + pub fn current_sample(&self) -> u16 { + self.adc.current_sample() + } + + /// Synchronously convert a single sample + /// Note that it reconfigures the adc sequence and doesn't restore it + #[inline(always)] + pub fn convert(&mut self, pin: &PIN, sample_time: config::SampleTime) -> u16 + where + PIN: Channel, ID = u8>, + { + self.adc.reset_sequence(); + self.adc + .configure_channel(pin, config::Sequence::One, sample_time); + self.adc.start_conversion(); + + //Wait for the sequence to complete + self.adc.wait_for_conversion_sequence(); + + self.adc.current_sample() + } +} + +impl Conversion { + /// Wait in a potential infite loop untill the ADC has stopped the conversion. + /// Everytime an sample is retrieved 'func' is called. + /// Note: when the ADC has stopped the conversion, for the last sample, func is NOT run. + pub fn wait_untill_stopped(mut self, mut func: F) -> Adc + where + F: FnMut(u16, &Adc), + { + loop { + match self { + Conversion::Stopped(adc) => return adc, + Conversion::Active(adc) => { + self = adc.wait_for_conversion_sequence(); + if let Conversion::Active(adc) = self { + let sample = adc.current_sample(); + func(sample, &adc); + + self = Conversion::Active(adc); + } + } + } + } + } +} + +impl Adc { + /// Block until the conversion is completed and return to configured + pub fn wait_for_conversion_sequence(mut self) -> Conversion { + self.adc.wait_for_conversion_sequence(); + + if !self.adc.is_conversion_active() { + let inactive: Adc<_, Configured> = Adc { + adc: self.adc, + _status: PhantomData, + }; + + Conversion::Stopped(inactive) + } else { + Conversion::Active(self) + } + } + + /// Returns if a conversion has been completed + /// Calling this before `wait_for_conversion_sequence` + /// should make that function return immediatly + pub fn is_conversion_done(&self) -> bool { + !self.adc.is_conversion_active() + } + + /// Cancels an ongoing conversion + #[inline(always)] + pub fn cancel_conversion(mut self) -> Adc { + self.adc.cancel_conversion(); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// get current sample + #[inline(always)] + pub fn current_sample(&self) -> u16 { + self.adc.current_sample() + } + + /// clear end conversion flag + #[inline(always)] + pub fn clear_end_conversion_flag(&mut self) { + self.adc.clear_end_of_conversion_flag(); + } +} + +impl Adc { + /// Starts conversion sequence. Waits for the hardware to indicate it's actually started. + #[inline(always)] + pub fn start_conversion(&mut self) { + self.adc.start_conversion() + } + + /// Cancels an ongoing conversion + #[inline(always)] + pub fn cancel_conversion(&mut self) { + self.adc.cancel_conversion() + } + + /// Stop the Adc + #[inline(always)] + pub fn stop(&mut self) { + self.adc.disable() + } + + /// Disable the Adc + #[inline(always)] + pub fn disable(mut self) -> Adc { + self.adc.set_dma(config::Dma::Disabled); + self.adc.disable(); + + Adc { + adc: self.adc, + _status: PhantomData, + } + } + + /// Read overrun flag + #[inline(always)] + pub fn get_overrun_flag(&self) -> bool { + self.adc.get_overrun_flag() + } + + /// Resets the overrun flag + #[inline(always)] + pub fn clear_overrun_flag(&mut self) { + self.adc.clear_overrun_flag(); + } +} + +unsafe impl TargetAddress for Adc { + #[inline(always)] + fn address(&self) -> u32 { + self.adc.data_register_address() + } + + type MemSize = u16; + + const REQUEST_LINE: Option = Some(ADC::DMA_MUX_RESOURCE as u8); +} + +impl OneShot, u16, PIN> for Adc +where + PIN: Channel, ID = u8>, +{ + type Error = (); + + fn read(&mut self, pin: &mut PIN) -> nb::Result { + Ok(self.convert(pin, self.adc.config.default_sample_time)) + } +} diff --git a/src/adc/temperature.rs b/src/adc/temperature.rs new file mode 100644 index 00000000..138d487a --- /dev/null +++ b/src/adc/temperature.rs @@ -0,0 +1,86 @@ +use crate::signature::{VtempCal130, VtempCal30, VDDA_CALIB}; + +use super::config; + +/* + Currently unused but this is the formula for using temperature calibration: + Temperature in °C = ( ( (TS_CAL2_TEMP-TS_CAL1_TEMP) / (TS_CAL2-TS_CAL1) ) * (TS_DATA-TS_CAL1) ) + 30°C +*/ + +/// Core temperature internal signal +pub struct Temperature; +impl crate::stasis::Freeze for Temperature {} + +impl Temperature { + /// Precompute the inverse of `VTEMP_CAL_VREFANALOG`, in volts, + /// for floating point calculations + const INV_VREFANALOG_VOLTS: f32 = 1000. / VDDA_CALIB as f32; + /// Temperature at which temperature sensor has been calibrated in production + /// for data into [`VtempCal30`] (tolerance: +-5 DegC) (unit: DegC). + const VTEMP_CAL_T30: u16 = 30; + /// Temperature at which temperature sensor has been calibrated in production + /// for data into [`VtempCal130`] (tolerance: +-5 DegC) (unit: DegC). + const VTEMP_CAL_T130: u16 = 130; + + /// Convert a sample to 12 bits. Reference voltages were captured at 12 bits. + const fn to_12b(sample: u16, resolution: config::Resolution) -> u16 { + match resolution { + config::Resolution::Six => sample << 6, + config::Resolution::Eight => sample << 4, + config::Resolution::Ten => sample << 2, + config::Resolution::Twelve => sample, + } + } + + /// Convert a raw sample from `Temperature` to deg C. + /// + /// ## Arguments + /// * `sample`: ADC sample taken on the [`Temperature`] channel. + /// * `vdda`: Analog reference voltage (vref+) when the temperature + /// sample was taken, in volts. + /// * `resolution`: Configured ADC resolution. + #[inline(always)] + pub fn temperature_to_degrees_centigrade( + sample: u16, + vdda: f32, + resolution: config::Resolution, + ) -> f32 { + // Reference measurements were taken at 12 bits + let sample_12b = Self::to_12b(sample, resolution); + + // Normalize for the difference in VDDA + let sample_normalized = sample_12b as f32 * (vdda * Self::INV_VREFANALOG_VOLTS); + + ((sample_normalized - VtempCal30::get().read() as f32) + * ((Self::VTEMP_CAL_T130 - Self::VTEMP_CAL_T30) as f32)) + / ((VtempCal130::get().read() - VtempCal30::get().read()) as f32) + + Self::VTEMP_CAL_T30 as f32 + } + + /// Convert a raw sample from `Temperature` to deg C + /// + /// ## Arguments + /// * `sample`: ADC sample taken on the [`Temperature`] channel. + /// * `vdda`: Analog reference voltage (vref+) when the temperature + /// sample was taken, in millivolts. + /// * `resolution`: Configured ADC resolution. + #[inline(always)] + pub fn temperature_to_degrees_centigrade_coarse( + sample: u16, + vdda: u32, + resolution: config::Resolution, + ) -> i16 { + // Reference measurements were taken at 12 bits + let sample_12b = Self::to_12b(sample, resolution); + + // Normalize for the difference in VDDA + let sample_normalized = ((sample_12b as u32 * vdda) / VDDA_CALIB) as u16; + + let t = ((sample_normalized as i32 - VtempCal30::get().read() as i32) + * ((Self::VTEMP_CAL_T130 - Self::VTEMP_CAL_T30) as i32)) + / ((VtempCal130::get().read() - VtempCal30::get().read()) as i32) + + Self::VTEMP_CAL_T30 as i32; + + t as i16 + } +} diff --git a/src/can.rs b/src/can.rs index 398916a4..dbfef79d 100644 --- a/src/can.rs +++ b/src/can.rs @@ -2,7 +2,6 @@ //! use crate::rcc::{self, Rcc}; -use fdcan; mod sealed { /// A TX pin configured for CAN communication @@ -110,7 +109,6 @@ mod fdcan1 { } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", diff --git a/src/dma/transfer.rs b/src/dma/transfer.rs index e5c6e708..bf1828a7 100644 --- a/src/dma/transfer.rs +++ b/src/dma/transfer.rs @@ -1,6 +1,9 @@ -use crate::dma::{ - traits, Channel, Direction, DmaDirection, MemoryToMemory, MemoryToPeripheral, - PeripheralToMemory, TargetAddress, +use crate::{ + adc, + dma::{ + traits, Channel, Direction, DmaDirection, MemoryToMemory, MemoryToPeripheral, + PeripheralToMemory, TargetAddress, + }, }; use core::{ marker::PhantomData, @@ -461,57 +464,23 @@ where } } -macro_rules! impl_adc_overrun { - ($($adc:ident, )*) => {$( - impl CircTransfer, BUF> - where - CHANNEL: Channel, - BUF: StaticWriteBuffer + Deref, - ::Target: Index, Output = [u16]> { - /// This is set when the AD finishes a conversion before the DMA has hade time to transfer the previous value - pub fn get_overrun_flag(&self) -> bool { - self.transfer.peripheral.get_overrun_flag() - } +impl + CircTransfer, BUF> +where + CHANNEL: Channel, + BUF: StaticWriteBuffer + Deref, + ::Target: Index, Output = [u16]>, +{ + /// This is set when the AD finishes a conversion before the DMA has hade time to transfer the previous value + pub fn get_overrun_flag(&self) -> bool { + self.transfer.peripheral.get_overrun_flag() + } - pub fn clear_overrun_flag(&mut self) { - self.transfer.peripheral.clear_overrun_flag(); - } - } - )*}; + pub fn clear_overrun_flag(&mut self) { + self.transfer.peripheral.clear_overrun_flag(); + } } -#[cfg(any( - feature = "stm32g431", - feature = "stm32g441", - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", -))] -impl_adc_overrun!(ADC1, ADC2,); - -#[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", -))] -impl_adc_overrun!(ADC3,); - -#[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", -))] -impl_adc_overrun!(ADC4, ADC5,); - macro_rules! impl_serial_timeout { ($($uart:ident, )*) => {$( impl CircTransfer, BUF> diff --git a/src/fdcan.rs b/src/fdcan.rs deleted file mode 100644 index 997e11b9..00000000 --- a/src/fdcan.rs +++ /dev/null @@ -1,1784 +0,0 @@ -#![deny(missing_docs)] - -//! FdCAN Operations - -/// Configuration of an FdCAN instance -pub mod config; -/// Filtering of CAN Messages -pub mod filter; -/// Header and info of transmitted and receiving frames -pub mod frame; -/// Standard and Extended Id -pub mod id; -/// Interrupt Line Information -pub mod interrupt; -mod message_ram; - -use id::{Id, IdReg}; - -use crate::rcc::Rcc; -use crate::stm32::fdcan::RegisterBlock; -use config::{ - ClockDivider, DataBitTiming, FdCanConfig, FrameTransmissionConfig, GlobalFilter, - NominalBitTiming, TimestampSource, -}; -use filter::{ - ActivateFilter as _, ExtendedFilter, ExtendedFilterSlot, StandardFilter, StandardFilterSlot, - EXTENDED_FILTER_MAX, STANDARD_FILTER_MAX, -}; -use frame::MergeTxFrameHeader; -use frame::{RxFrameInfo, TxFrameHeader}; -use interrupt::{Interrupt, InterruptLine, Interrupts}; - -use message_ram::MsgRamExt; -use message_ram::RxFifoElement; - -use core::cmp::Ord; -use core::convert::Infallible; -use core::convert::TryFrom; -use core::marker::PhantomData; -use core::ptr::NonNull; - -mod sealed { - /// A TX pin configured for CAN communication - pub trait Tx {} - /// An RX pin configured for CAN communication - pub trait Rx {} -} - -/// An FdCAN peripheral instance. -/// -/// This trait is meant to be implemented for a HAL-specific type that represent ownership of -/// the CAN peripheral (and any pins required by it, although that is entirely up to the HAL). -/// -/// # Safety -/// -/// It is only safe to implement this trait, when: -/// -/// * The implementing type has ownership of the peripheral, preventing any other accesses to the -/// register block. -/// * `REGISTERS` is a pointer to that peripheral's register block and can be safely accessed for as -/// long as ownership or a borrow of the implementing type is present. -pub unsafe trait Instance: MsgRamExt + crate::rcc::Instance { - /// Pointer to the instance's register block. - const REGISTERS: *mut RegisterBlock; -} - -/// Indicates if an Receive Overflow has occurred -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -pub enum ReceiveErrorOverflow { - /// No overflow has occurred - Normal(u8), - /// An overflow has occurred - Overflow(u8), -} - -///Error Counters -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -#[allow(dead_code)] -pub struct ErrorCounters { - /// General CAN error counter - can_errors: u8, - /// Receive CAN error counter - receive_err: ReceiveErrorOverflow, - /// Transmit CAN error counter - transmit_err: u8, -} - -/// Loopback Mode -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -enum LoopbackMode { - None, - Internal, - External, -} - -/// Bus Activity -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -pub enum Activity { - /// Node is Synchronizing - Synchronizing = 0b00, - /// Node is Idle - Idle = 0b01, - /// Node is receiver only - Receiver = 0b10, - /// Node is transmitter only - Transmitter = 0b11, -} -impl TryFrom for Activity { - type Error = (); - fn try_from(value: u8) -> Result { - match value { - 0b000 => Ok(Self::Synchronizing), - 0b001 => Ok(Self::Idle), - 0b010 => Ok(Self::Receiver), - 0b011 => Ok(Self::Transmitter), - _ => Err(()), - } - } -} - -/// Indicates the type of the last error which occurred on the CAN bus -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -pub enum LastErrorCode { - /// There has been no error since last read - NoError = 0b000, - /// More than 5 equal bits in sequence are not allowed - StuffError = 0b001, - /// a fixed format part of ta received frame had the wrong format - FormError = 0b010, - /// message tramsitted by this node was not acknowledged by another - AckError = 0b011, - /// During transmit, the node wanted to send a 1 but monitored a 0 - Bit1Error = 0b100, - /// During transmit, the node wanted to send a 0 but monitored a 1 - Bit0Error = 0b101, - /// CRC checksum of a received message was incorrect - CRCError = 0b110, - /// No CAN bus event detected since last read - NoChange = 0b111, -} -impl TryFrom for LastErrorCode { - type Error = (); - fn try_from(value: u8) -> Result { - match value { - 0b000 => Ok(Self::NoError), - 0b001 => Ok(Self::StuffError), - 0b010 => Ok(Self::FormError), - 0b011 => Ok(Self::AckError), - 0b100 => Ok(Self::Bit1Error), - 0b101 => Ok(Self::Bit0Error), - 0b110 => Ok(Self::CRCError), - 0b111 => Ok(Self::NoChange), - _ => Err(()), - } - } -} - -/// Some status indications regarding the FDCAN protocl -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -pub struct ProtocolStatus { - /// Type of current activity - activity: Activity, - /// Transmitter delay companstation - transmitter_delay_comp: u8, - /// But Off Status - bus_off_status: bool, - /// Shows if Error counters are aat their limit of 96 - error_warning: bool, - /// Shows if the node send and active error flag (false) or stays silent (true). - error_passive_state: bool, - /// Indicates te last type of error which occurred on the CAN bus. - last_error: LastErrorCode, -} - -/// Allows for Transmit Operations -pub trait Transmit {} -/// Allows for Receive Operations -pub trait Receive {} - -/// Allows for the FdCan Instance to be released or to enter ConfigMode -pub struct PoweredDownMode; -/// Allows for the configuration for the Instance -pub struct ConfigMode; -/// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without -/// affecting a running CAN system connected to the FDCAN_TX and FDCAN_RX pins. In this -/// mode, FDCAN_RX pin is disconnected from the FDCAN and FDCAN_TX pin is held -/// recessive. -pub struct InternalLoopbackMode; -impl Transmit for InternalLoopbackMode {} -impl Receive for InternalLoopbackMode {} -/// This mode is provided for hardware self-test. To be independent from external stimulation, -/// the FDCAN ignores acknowledge errors (recessive bit sampled in the acknowledge slot of a -/// data / remote frame) in Loop Back mode. In this mode the FDCAN performs an internal -/// feedback from its transmit output to its receive input. The actual value of the FDCAN_RX -/// input pin is disregarded by the FDCAN. The transmitted messages can be monitored at the -/// FDCAN_TX transmit pin. -pub struct ExternalLoopbackMode; -impl Transmit for ExternalLoopbackMode {} -impl Receive for ExternalLoopbackMode {} -/// The normal use of the FdCan instance after configurations -pub struct NormalOperationMode; -impl Transmit for NormalOperationMode {} -impl Receive for NormalOperationMode {} -/// In Restricted operation mode the node is able to receive data and remote frames and to give -/// acknowledge to valid frames, but it does not send data frames, remote frames, active error -/// frames, or overload frames. In case of an error condition or overload condition, it does not -/// send dominant bits, instead it waits for the occurrence of bus idle condition to resynchronize -/// itself to the CAN communication. The error counters for transmit and receive are frozen while -/// error logging (can_errors) is active. TODO: automatically enter in this mode? -pub struct RestrictedOperationMode; -impl Receive for RestrictedOperationMode {} -/// In Bus monitoring mode (for more details refer to ISO11898-1, 10.12 Bus monitoring), -/// the FDCAN is able to receive valid data frames and valid remote frames, but cannot start a -/// transmission. In this mode, it sends only recessive bits on the CAN bus. If the FDCAN is -/// required to send a dominant bit (ACK bit, overload flag, active error flag), the bit is -/// rerouted internally so that the FDCAN can monitor it, even if the CAN bus remains in recessive -/// state. In Bus monitoring mode the TXBRP register is held in reset state. The Bus monitoring -/// mode can be used to analyze the traffic on a CAN bus without affecting it by the transmission -/// of dominant bits. -pub struct BusMonitoringMode; -impl Receive for BusMonitoringMode {} -/// Test mode must be used for production tests or self test only. The software control for -/// FDCAN_TX pin interferes with all CAN protocol functions. It is not recommended to use test -/// modes for application. -pub struct TestMode; - -/// Interface to a FdCAN peripheral. -pub struct FdCan { - control: FdCanControl, -} - -impl FdCan -where - I: Instance, -{ - fn create_can(config: FdCanConfig, instance: I) -> FdCan { - FdCan { - control: FdCanControl { - config, - instance, - _mode: core::marker::PhantomData, - }, - } - } - - fn into_can_mode(self) -> FdCan { - FdCan { - control: FdCanControl { - config: self.control.config, - instance: self.control.instance, - _mode: core::marker::PhantomData, - }, - } - } - - /// Returns a reference to the peripheral instance. - /// - /// This allows accessing HAL-specific data stored in the instance type. - #[inline] - pub fn instance(&mut self) -> &mut I { - &mut self.control.instance - } - - #[inline] - fn registers(&self) -> &RegisterBlock { - unsafe { &*I::REGISTERS } - } - - #[inline] - fn msg_ram_mut(&mut self) -> &mut message_ram::RegisterBlock { - self.instance().msg_ram_mut() - } - - #[inline] - fn reset_msg_ram(&mut self) { - self.msg_ram_mut().reset(); - } - - #[inline] - fn enter_init_mode(&mut self) { - let can = self.registers(); - - can.cccr().modify(|_, w| w.init().set_bit()); - while can.cccr().read().init().bit_is_clear() {} - can.cccr().modify(|_, w| w.cce().set_bit()); - } - - /// Returns the current FDCAN config settings - #[inline] - pub fn get_config(&self) -> FdCanConfig { - self.control.config - } - - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - #[inline] - fn set_loopback_mode(&mut self, mode: LoopbackMode) { - let (test, mon, lbck) = match mode { - LoopbackMode::None => (false, false, false), - LoopbackMode::Internal => (true, true, true), - LoopbackMode::External => (true, false, true), - }; - - self.set_test_mode(test); - self.set_bus_monitoring_mode(mon); - - let can = self.registers(); - can.test().modify(|_, w| w.lbck().bit(lbck)); - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - #[inline] - fn set_bus_monitoring_mode(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.mon().bit(enabled)); - } - - #[inline] - fn set_restricted_operations(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.asm().bit(enabled)); - } - - #[inline] - fn set_normal_operations(&mut self, _enabled: bool) { - self.set_loopback_mode(LoopbackMode::None); - } - - #[inline] - fn set_test_mode(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.test().bit(enabled)); - } - - #[inline] - fn set_power_down_mode(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.csr().bit(enabled)); - while can.cccr().read().csa().bit() != enabled {} - } - - /// Enable/Disable the specific Interrupt Line - #[inline] - pub fn enable_interrupt_line(&mut self, line: InterruptLine, enabled: bool) { - let can = self.registers(); - match line { - InterruptLine::_0 => can.ile().modify(|_, w| w.eint1().bit(enabled)), - InterruptLine::_1 => can.ile().modify(|_, w| w.eint0().bit(enabled)), - } - } - - /// Starts listening for a CAN interrupt. - #[inline] - pub fn enable_interrupt(&mut self, interrupt: Interrupt) { - self.enable_interrupts(Interrupts::from_bits_truncate(interrupt as u32)) - } - - /// Starts listening for a set of CAN interrupts. - #[inline] - pub fn enable_interrupts(&mut self, interrupts: Interrupts) { - self.registers() - .ie - .modify(|r, w| unsafe { w.bits(r.bits() | interrupts.bits()) }) - } - - /// Stops listening for a CAN interrupt. - pub fn disable_interrupt(&mut self, interrupt: Interrupt) { - self.disable_interrupts(Interrupts::from_bits_truncate(interrupt as u32)) - } - - /// Stops listening for a set of CAN interrupts. - #[inline] - pub fn disable_interrupts(&mut self, interrupts: Interrupts) { - self.registers() - .ie - .modify(|r, w| unsafe { w.bits(r.bits() & !interrupts.bits()) }) - } - - /// Retrieve the CAN error counters - #[inline] - pub fn error_counters(&self) -> ErrorCounters { - self.control.error_counters() - } - - /// Set an Standard Address CAN filter into slot 'id' - #[inline] - pub fn set_standard_filter(&mut self, slot: StandardFilterSlot, filter: StandardFilter) { - self.msg_ram_mut().filters.flssa[slot as usize].activate(filter); - } - - /// Set an array of Standard Address CAN filters and overwrite the current set - pub fn set_standard_filters( - &mut self, - filters: &[StandardFilter; STANDARD_FILTER_MAX as usize], - ) { - for (i, f) in filters.iter().enumerate() { - self.msg_ram_mut().filters.flssa[i].activate(*f); - } - } - - /// Set an Extended Address CAN filter into slot 'id' - #[inline] - pub fn set_extended_filter(&mut self, slot: ExtendedFilterSlot, filter: ExtendedFilter) { - self.msg_ram_mut().filters.flesa[slot as usize].activate(filter); - } - - /// Set an array of Extended Address CAN filters and overwrite the current set - pub fn set_extended_filters( - &mut self, - filters: &[ExtendedFilter; EXTENDED_FILTER_MAX as usize], - ) { - for (i, f) in filters.iter().enumerate() { - self.msg_ram_mut().filters.flesa[i].activate(*f); - } - } - - /// Retrieve the current protocol status - pub fn get_protocol_status(&self) -> ProtocolStatus { - let psr = self.registers().psr().read(); - ProtocolStatus { - activity: Activity::try_from(0 /*psr.act().bits()*/).unwrap(), //TODO: stm32g4 does not allow reading from this register - transmitter_delay_comp: psr.tdcv().bits(), - bus_off_status: psr.bo().bit_is_set(), - error_warning: psr.ew().bit_is_set(), - error_passive_state: psr.ep().bit_is_set(), - last_error: LastErrorCode::try_from(psr.lec().bits()).unwrap(), - } - } - - /// Check if the interrupt is triggered - #[inline] - pub fn has_interrupt(&mut self, interrupt: Interrupt) -> bool { - self.control.has_interrupt(interrupt) - } - - /// Clear specified interrupt - #[inline] - pub fn clear_interrupt(&mut self, interrupt: Interrupt) { - self.control.clear_interrupt(interrupt) - } - - /// Clear specified interrupts - #[inline] - pub fn clear_interrupts(&mut self, interrupts: Interrupts) { - self.control.clear_interrupts(interrupts) - } - - /// Splits this `FdCan` instance into transmitting and receiving halves, by reference. - #[inline] - #[allow(clippy::type_complexity)] - fn split_by_ref_generic( - &mut self, - ) -> ( - &mut FdCanControl, - &mut Tx, - &mut Rx, - &mut Rx, - ) { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - let tx = unsafe { Tx::conjure_by_ref() }; - let rx0 = unsafe { Rx::conjure_by_ref() }; - let rx1 = unsafe { Rx::conjure_by_ref() }; - (&mut self.control, tx, rx0, rx1) - } - - /// Consumes this `FdCan` instance and splits it into transmitting and receiving halves. - #[inline] - #[allow(clippy::type_complexity)] - fn split_generic( - self, - ) -> ( - FdCanControl, - Tx, - Rx, - Rx, - ) { - // Safety: We must be carefull to not let them use each others registers - unsafe { (self.control, Tx::conjure(), Rx::conjure(), Rx::conjure()) } - } - - /// Combines an FdCanControl, Tx and the two Rx instances back into an FdCan instance - #[inline] - #[allow(clippy::type_complexity)] - pub fn combine( - t: ( - FdCanControl, - Tx, - Rx, - Rx, - ), - ) -> Self { - Self::create_can(t.0.config, t.0.instance) - } -} - -/// Select an FDCAN Clock Source -pub enum FdCanClockSource { - /// Select HSE as the FDCAN clock source - HSE = 0b00, - /// Select PLL "Q" clock as the FDCAN clock source - PLLQ = 0b01, - /// Select "P" clock as the FDCAN clock source - PCLK = 0b10, - //Reserved = 0b10, -} - -impl FdCan -where - I: Instance, -{ - /// Creates a CAN interface. - /// - /// Sets the FDCAN clock to the P clock if no FDCAN clock has been configured - /// If one has been configured, it will leave it as is. - #[inline] - pub fn new(can_instance: I, _tx: TX, _rx: RX, rcc: &Rcc) -> Self - where - TX: sealed::Tx, - RX: sealed::Rx, - { - I::enable(&rcc.rb); - - if rcc.rb.ccipr().read().fdcansel().is_hse() { - // Select P clock as FDCAN clock source - rcc.rb.ccipr().modify(|_, w| { - // This is sound, as `FdCanClockSource` only contains valid values for this field. - unsafe { - w.fdcansel().bits(FdCanClockSource::PCLK as u8); - } - - w - }); - } - //TODO: Set Speed to VeryHigh? - - let can = Self::create_can(FdCanConfig::default(), can_instance); - let reg = can.registers(); - assert!(reg.endn().read().bits() == 0x87654321_u32); - can - } - - /// Creates a CAN interface. - /// - /// Sets the FDCAN clock to the selected clock source - /// Note that this is shared across all instances. - /// Do not call this if there is allready an active FDCAN instance. - #[inline] - pub fn new_with_clock_source( - can_instance: I, - _tx: TX, - _rx: RX, - rcc: &Rcc, - clock_source: FdCanClockSource, - ) -> Self - where - TX: sealed::Tx, - RX: sealed::Rx, - { - rcc.rb.ccipr().modify(|_, w| { - // This is sound, as `FdCanClockSource` only contains valid values for this field. - unsafe { - w.fdcansel().bits(clock_source as u8); - } - - w - }); - - Self::new(can_instance, _tx, _rx, rcc) - } - - /// Moves out of PoweredDownMode and into ConfigMode - #[inline] - pub fn into_config_mode(mut self) -> FdCan { - self.set_power_down_mode(false); - self.enter_init_mode(); - - self.reset_msg_ram(); - - let can = self.registers(); - - // Framework specific settings are set here. // - - // set TxBuffer to Queue Mode; - // TODO: don't require this. - // can.txbc().write(|w| w.tfqm().set_bit()); - //FIXME: stm32g4 has the wrong layout here! - //We should be able to use the above, - //But right now, we just set the 24th bit. - can.txbc().write(|w| unsafe { w.bits(1_u32 << 24) }); - - // set standard filters list size to 28 - // set extended filters list size to 8 - // REQUIRED: we use the memory map as if these settings are set - // instead of re-calculating them. - can.rxgfc().modify(|_, w| unsafe { - w.lse() - .bits(EXTENDED_FILTER_MAX) - .lss() - .bits(STANDARD_FILTER_MAX) - }); - for fid in 0..STANDARD_FILTER_MAX { - self.set_standard_filter((fid as u8).into(), StandardFilter::disable()); - } - for fid in 0..EXTENDED_FILTER_MAX { - self.set_extended_filter(fid.into(), ExtendedFilter::disable()); - } - - self.into_can_mode() - } - - /// Disables the CAN interface and returns back the raw peripheral it was created from. - #[inline] - pub fn free(mut self) -> I { - self.disable_interrupts(Interrupts::all()); - - //TODO check this! - self.enter_init_mode(); - self.set_power_down_mode(true); - self.control.instance - } -} - -impl FdCan -where - I: Instance, -{ - #[inline] - fn leave_init_mode(&mut self) { - self.apply_config(self.control.config); - - let can = self.registers(); - can.cccr().modify(|_, w| w.cce().clear_bit()); - can.cccr().modify(|_, w| w.init().clear_bit()); - while can.cccr().read().init().bit_is_set() {} - } - - /// Moves out of ConfigMode and into InternalLoopbackMode - #[inline] - pub fn into_internal_loopback(mut self) -> FdCan { - self.set_loopback_mode(LoopbackMode::Internal); - self.leave_init_mode(); - - self.into_can_mode() - } - - /// Moves out of ConfigMode and into ExternalLoopbackMode - #[inline] - pub fn into_external_loopback(mut self) -> FdCan { - self.set_loopback_mode(LoopbackMode::External); - self.leave_init_mode(); - - self.into_can_mode() - } - - /// Moves out of ConfigMode and into RestrictedOperationMode - #[inline] - pub fn into_restricted(mut self) -> FdCan { - self.set_restricted_operations(true); - self.leave_init_mode(); - - self.into_can_mode() - } - - /// Moves out of ConfigMode and into NormalOperationMode - #[inline] - pub fn into_normal(mut self) -> FdCan { - self.set_normal_operations(true); - self.leave_init_mode(); - - self.into_can_mode() - } - - /// Moves out of ConfigMode and into BusMonitoringMode - #[inline] - pub fn into_bus_monitoring(mut self) -> FdCan { - self.set_bus_monitoring_mode(true); - self.leave_init_mode(); - - self.into_can_mode() - } - - /// Moves out of ConfigMode and into Testmode - #[inline] - pub fn into_test_mode(mut self) -> FdCan { - self.set_test_mode(true); - self.leave_init_mode(); - - self.into_can_mode() - } - - /// Moves out of ConfigMode and into PoweredDownmode - #[inline] - pub fn into_powered_down(mut self) -> FdCan { - self.set_power_down_mode(true); - self.leave_init_mode(); - - self.into_can_mode() - } - - /// Applies the settings of a new FdCanConfig - /// See `[FdCanConfig]` for more information - #[inline] - pub fn apply_config(&mut self, config: FdCanConfig) { - self.set_data_bit_timing(config.dbtr); - self.set_nominal_bit_timing(config.nbtr); - self.set_automatic_retransmit(config.automatic_retransmit); - self.set_transmit_pause(config.transmit_pause); - self.set_frame_transmit(config.frame_transmit); - self.set_interrupt_line_config(config.interrupt_line_config); - self.set_non_iso_mode(config.non_iso_mode); - self.set_edge_filtering(config.edge_filtering); - self.set_protocol_exception_handling(config.protocol_exception_handling); - self.set_global_filter(config.global_filter); - } - - /// Configures the bit timings. - /// - /// You can use to calculate the `btr` parameter. Enter - /// parameters as follows: - /// - /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). - /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). - /// - *Sample Point*: Should normally be left at the default value of 87.5%. - /// - *SJW*: Should normally be left at the default value of 1. - /// - /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` - /// parameter to this method. - #[inline] - pub fn set_nominal_bit_timing(&mut self, btr: NominalBitTiming) { - self.control.config.nbtr = btr; - - let can = self.registers(); - can.nbtp().write(|w| unsafe { - w.nbrp() - .bits(btr.nbrp() - 1) - .ntseg1() - .bits(btr.ntseg1() - 1) - .ntseg2() - .bits(btr.ntseg2() - 1) - .nsjw() - .bits(btr.nsjw() - 1) - }); - } - - /// Configures the data bit timings for the FdCan Variable Bitrates. - /// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS. - #[inline] - pub fn set_data_bit_timing(&mut self, btr: DataBitTiming) { - self.control.config.dbtr = btr; - - let can = self.registers(); - can.dbtp().write(|w| unsafe { - w.dbrp() - .bits(btr.dbrp() - 1) - .dtseg1() - .bits(btr.dtseg1() - 1) - .dtseg2() - .bits(btr.dtseg2() - 1) - .dsjw() - .bits(btr.dsjw() - 1) - }); - } - - /// Enables or disables automatic retransmission of messages - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// util it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - #[inline] - pub fn set_automatic_retransmit(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.dar().bit(!enabled)); - self.control.config.automatic_retransmit = enabled; - } - - /// Configures the transmit pause feature - /// See `[FdCanConfig]` for more information - #[inline] - pub fn set_transmit_pause(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.dar().bit(!enabled)); - self.control.config.transmit_pause = enabled; - } - - /// Configures non-iso mode - /// See `[FdCanConfig]` for more information - #[inline] - pub fn set_non_iso_mode(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.niso().bit(enabled)); - self.control.config.non_iso_mode = enabled; - } - - /// Configures edge filtering - /// See `[FdCanConfig]` for more information - #[inline] - pub fn set_edge_filtering(&mut self, enabled: bool) { - let can = self.registers(); - can.cccr().modify(|_, w| w.efbi().bit(enabled)); - self.control.config.edge_filtering = enabled; - } - - /// Configures frame transmission mode - /// See `[FdCanConfig]` for more information - #[inline] - pub fn set_frame_transmit(&mut self, fts: FrameTransmissionConfig) { - let (fdoe, brse) = match fts { - FrameTransmissionConfig::ClassicCanOnly => (false, false), - FrameTransmissionConfig::AllowFdCan => (true, false), - FrameTransmissionConfig::AllowFdCanAndBRS => (true, true), - }; - - let can = self.registers(); - can.cccr().modify(|_, w| w.fdoe().bit(fdoe).brse().bit(brse)); - - self.control.config.frame_transmit = fts; - } - - /// Configures the interrupt lines - /// See `[FdCanConfig]` for more information - #[inline] - pub fn set_interrupt_line_config(&mut self, l0int: Interrupts) { - let can = self.registers(); - - can.ils().modify(|_, w| unsafe { w.bits(l0int.bits()) }); - - self.control.config.interrupt_line_config = l0int; - } - - /// Sets the protocol exception handling on/off - #[inline] - pub fn set_protocol_exception_handling(&mut self, enabled: bool) { - let can = self.registers(); - - can.cccr().modify(|_, w| w.pxhd().bit(!enabled)); - - self.control.config.protocol_exception_handling = enabled; - } - - /// Sets the General FdCAN clock divider for this instance - //TODO: ?clock divider is a shared register? - #[inline] - pub fn set_clock_divider(&mut self, div: ClockDivider) { - let can = self.registers(); - - can.ckdiv().write(|w| unsafe { w.pdiv().bits(div as u8) }); - - self.control.config.clock_divider = div; - } - - /// Configures and resets the timestamp counter - #[inline] - pub fn set_timestamp_counter_source(&mut self, select: TimestampSource) { - let (tcp, tss) = match select { - TimestampSource::None => (0, 0b00), - TimestampSource::Prescaler(p) => (p as u8, 0b01), - TimestampSource::FromTIM3 => (0, 0b10), - }; - self.registers() - .tscc - .write(|w| unsafe { w.tcp().bits(tcp).tss().bits(tss) }); - - self.control.config.timestamp_source = select; - } - - /// Configures the global filter settings - #[inline] - pub fn set_global_filter(&mut self, filter: GlobalFilter) { - self.registers().rxgfc().modify(|_, w| { - unsafe { - w.anfs() - .bits(filter.handle_standard_frames as u8) - .anfe() - .bits(filter.handle_extended_frames as u8) - } - .rrfs() - .bit(filter.reject_remote_standard_frames) - .rrfe() - .bit(filter.reject_remote_extended_frames) - }); - } - - /// Returns the current FdCan timestamp counter - #[inline] - pub fn timestamp(&self) -> u16 { - self.control.timestamp() - } -} - -impl FdCan -where - I: Instance, -{ - /// Returns out of InternalLoopbackMode and back into ConfigMode - #[inline] - pub fn into_config_mode(mut self) -> FdCan { - self.set_loopback_mode(LoopbackMode::None); - self.enter_init_mode(); - - self.into_can_mode() - } -} - -impl FdCan -where - I: Instance, -{ - /// Returns out of ExternalLoopbackMode and back into ConfigMode - #[inline] - pub fn into_config_mode(mut self) -> FdCan { - self.set_loopback_mode(LoopbackMode::None); - self.enter_init_mode(); - - self.into_can_mode() - } -} - -impl FdCan -where - I: Instance, -{ - /// Returns out of NormalOperationMode and back into ConfigMode - #[inline] - pub fn into_config_mode(mut self) -> FdCan { - self.set_normal_operations(false); - self.enter_init_mode(); - - self.into_can_mode() - } -} - -impl FdCan -where - I: Instance, -{ - /// Returns out of RestrictedOperationMode and back into ConfigMode - #[inline] - pub fn into_config_mode(mut self) -> FdCan { - self.set_restricted_operations(false); - self.enter_init_mode(); - - self.into_can_mode() - } -} - -impl FdCan -where - I: Instance, -{ - /// Returns out of BusMonitoringMode and back into ConfigMode - #[inline] - pub fn into_config_mode(mut self) -> FdCan { - self.set_bus_monitoring_mode(false); - self.enter_init_mode(); - - self.into_can_mode() - } -} - -/// states of the test.tx register -pub enum TestTransmitPinState { - /// CAN core has control (default) - CoreHasControl = 0b00, - /// Sample point can be monitored - ShowSamplePoint = 0b01, - /// Set to Dominant (0) Level - SetDominant = 0b10, - /// Set to Recessive (1) Level - SetRecessive = 0b11, -} - -impl FdCan -where - I: Instance, -{ - /// Returns out of TestMode and back into ConfigMode - #[inline] - pub fn into_config_mode(mut self) -> FdCan { - self.set_test_mode(false); - self.enter_init_mode(); - - self.into_can_mode() - } - - /// Gets the state of the receive pin to either Dominant (false), or Recessive (true) - pub fn get_receive_pin(&mut self) -> bool { - let can = self.registers(); - - can.test().read().rx().bit_is_set() - } - - /// Sets the state of the transmit pin according to TestTransmitPinState - pub fn set_transmit_pin(&mut self, state: TestTransmitPinState) { - let can = self.registers(); - - //SAFE: state has all possible values, and this can only occur in TestMode - can.test().modify(|_, w| unsafe { w.tx().bits(state as u8) }); - } -} - -impl FdCan -where - I: Instance, - M: Transmit + Receive, -{ - /// Splits this `FdCan` instance into transmitting and receiving halves, by reference. - #[inline] - #[allow(clippy::type_complexity)] - pub fn split_by_ref( - &mut self, - ) -> ( - &mut FdCanControl, - &mut Tx, - &mut Rx, - &mut Rx, - ) { - self.split_by_ref_generic() - } - - /// Consumes this `FdCan` instance and splits it into transmitting and receiving halves. - #[allow(clippy::type_complexity)] - pub fn split( - self, - ) -> ( - FdCanControl, - Tx, - Rx, - Rx, - ) { - self.split_generic() - } -} - -impl FdCan -where - I: Instance, - M: Transmit, -{ - /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. - /// - /// Frames are transmitted to the bus based on their priority (identifier). - /// Transmit order is preserved for frames with identical identifiers. - /// If all transmit mailboxes are full, this overwrites the mailbox with - /// the lowest priority. - #[inline] - pub fn transmit( - &mut self, - frame: TxFrameHeader, - write: &mut WTX, - ) -> nb::Result, Infallible> - where - WTX: FnMut(&mut [u32]), - { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::::conjure().transmit(frame, write) } - } - - /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. - /// - /// Frames are transmitted to the bus based on their priority (identifier). - /// Transmit order is preserved for frames with identical identifiers. - /// If all transmit mailboxes are full, `pending` is called with the mailbox, - /// header and data of the to-be-replaced frame. - pub fn transmit_preserve( - &mut self, - frame: TxFrameHeader, - write: &mut WTX, - pending: &mut PTX, - ) -> nb::Result, Infallible> - where - PTX: FnMut(Mailbox, TxFrameHeader, &[u32]) -> P, - WTX: FnMut(&mut [u32]), - { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::::conjure().transmit_preserve(frame, write, pending) } - } - - /// Returns `true` if no frame is pending for transmission. - #[inline] - pub fn is_transmitter_idle(&self) -> bool { - // Safety: Read-only operation. - unsafe { Tx::::conjure().is_idle() } - } - - /// Attempts to abort the sending of a frame that is pending in a mailbox. - /// - /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be - /// aborted, this function has no effect and returns `false`. - /// - /// If there is a frame in the provided mailbox, and it is canceled successfully, this function - /// returns `true`. - #[inline] - pub fn abort(&mut self, mailbox: Mailbox) -> bool { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::::conjure().abort(mailbox) } - } -} - -impl FdCan -where - I: Instance, - M: Receive, -{ - /// Returns a received frame from FIFO_0 if available. - #[inline] - pub fn receive0( - &mut self, - receive: &mut RECV, - ) -> nb::Result, Infallible> - where - RECV: FnMut(RxFrameInfo, &[u32]) -> R, - { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Rx::::conjure().receive(receive) } - } - - /// Returns a received frame from FIFO_1 if available. - #[inline] - pub fn receive1( - &mut self, - receive: &mut RECV, - ) -> nb::Result, Infallible> - where - RECV: FnMut(RxFrameInfo, &[u32]) -> R, - { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Rx::::conjure().receive(receive) } - } -} - -/// FdCanControl Struct -/// Used to house some information during an FdCan split. -/// and can be used for some generic information retrieval during operation. -pub struct FdCanControl -where - I: Instance, -{ - config: FdCanConfig, - instance: I, - _mode: PhantomData, -} -impl FdCanControl -where - I: Instance, -{ - #[inline] - fn registers(&self) -> &RegisterBlock { - unsafe { &*I::REGISTERS } - } - - /// Returns the current error counters - #[inline] - pub fn error_counters(&self) -> ErrorCounters { - let can = self.registers(); - let ecr = can.ecr().read(); - let cel: u8 = ecr.cel().bits(); - let rp: bool = ecr.rp().bit(); - let rec: u8 = ecr.rec().bits(); - let tec: u8 = ecr.tec().bits(); - - ErrorCounters { - can_errors: cel, - transmit_err: tec, - receive_err: match rp { - false => ReceiveErrorOverflow::Normal(rec), - true => ReceiveErrorOverflow::Overflow(rec), - }, - } - } - - /// Returns the current FdCan Timestamp counter - #[inline] - pub fn timestamp(&self) -> u16 { - self.registers().tscv().read().tsc().bits() - } - - /// Check if the interrupt is triggered - #[inline] - pub fn has_interrupt(&mut self, interrupt: Interrupt) -> bool { - let can = self.registers(); - can.ir().read().bits() & (interrupt as u32) > 0 - } - - /// Clear specified interrupt - #[inline] - pub fn clear_interrupt(&mut self, interrupt: Interrupt) { - let can = self.registers(); - can.ir().write(|w| unsafe { w.bits(interrupt as u32) }); - } - - /// Clear specified interrupts - #[inline] - pub fn clear_interrupts(&mut self, interrupts: Interrupts) { - let can = self.registers(); - can.ir().write(|w| unsafe { w.bits(interrupts.bits()) }); - } -} - -/// Interface to the CAN transmitter part. -pub struct Tx { - _can: PhantomData, - _mode: PhantomData, -} - -impl Tx -where - I: Instance, -{ - #[inline] - unsafe fn conjure() -> Self { - Self { - _can: PhantomData, - _mode: PhantomData, - } - } - - /// Creates a `&mut Self` out of thin air. - /// - /// This is only safe if it is the only way to access a `Tx`. - #[inline] - unsafe fn conjure_by_ref<'a>() -> &'a mut Self { - // Cause out of bounds access when `Self` is not zero-sized. - #[allow(clippy::unnecessary_operation)] - [()][core::mem::size_of::()]; - - // Any aligned pointer is valid for ZSTs. - &mut *NonNull::dangling().as_ptr() - } - - #[inline] - fn registers(&self) -> &RegisterBlock { - unsafe { &*I::REGISTERS } - } - - #[inline] - fn tx_msg_ram(&self) -> &message_ram::Transmit { - unsafe { &(*I::MSG_RAM).transmit } - } - - #[inline] - fn tx_msg_ram_mut(&mut self) -> &mut message_ram::Transmit { - unsafe { &mut (*I::MSG_RAM).transmit } - } - - /// Puts a CAN frame in a transmit mailbox for transmission on the bus. - /// - /// Frames are transmitted to the bus based on their priority (identifier). Transmit order is - /// preserved for frames with identical identifiers. - /// - /// If all transmit mailboxes are full, a higher priority frame can replace a lower-priority - /// frame, which is returned via the closure 'pending'. If 'pending' is called; it's return value - /// is returned via Option

, if it is not, None is returned. - /// If there are only higher priority frames in the queue, this returns Err::WouldBlock - pub fn transmit( - &mut self, - frame: TxFrameHeader, - write: &mut WTX, - ) -> nb::Result, Infallible> - where - WTX: FnMut(&mut [u32]), - { - self.transmit_preserve(frame, write, &mut |_, _, _| ()) - } - - /// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can - /// be preserved. - pub fn transmit_preserve( - &mut self, - frame: TxFrameHeader, - write: &mut WTX, - pending: &mut PTX, - ) -> nb::Result, Infallible> - where - PTX: FnMut(Mailbox, TxFrameHeader, &[u32]) -> P, - WTX: FnMut(&mut [u32]), - { - let can = self.registers(); - let queue_is_full = self.tx_queue_is_full(); - - let id = frame.into(); - - // If the queue is full, - // Discard the first slot with a lower priority message - let (idx, pending_frame) = if queue_is_full { - if self.is_available(Mailbox::_0, id) { - ( - Mailbox::_0, - self.abort_pending_mailbox(Mailbox::_0, pending), - ) - } else if self.is_available(Mailbox::_1, id) { - ( - Mailbox::_1, - self.abort_pending_mailbox(Mailbox::_1, pending), - ) - } else if self.is_available(Mailbox::_2, id) { - ( - Mailbox::_2, - self.abort_pending_mailbox(Mailbox::_2, pending), - ) - } else { - // For now we bail when there is no lower priority slot available - // Can this lead to priority inversion? - return Err(nb::Error::WouldBlock); - } - } else { - // Read the Write Pointer - let idx = can.txfqs().read().tfqpi().bits(); - - (Mailbox::new(idx), None) - }; - - self.write_mailbox(idx, frame, write); - - Ok(pending_frame) - } - - /// Returns if the tx queue is able to accept new messages without having to cancel an existing one - #[inline] - pub fn tx_queue_is_full(&self) -> bool { - self.registers().txfqs().read().tfqf().bit() - } - - /// Returns `Ok` when the mailbox is free or if it contains pending frame with a - /// lower priority (higher ID) than the identifier `id`. - #[inline] - fn is_available(&self, idx: Mailbox, id: IdReg) -> bool { - if self.has_pending_frame(idx) { - //read back header section - let header: TxFrameHeader = (&self.tx_msg_ram().tbsa[idx as usize].header).into(); - let old_id: IdReg = header.into(); - - id > old_id - } else { - true - } - } - - #[inline] - fn write_mailbox(&mut self, idx: Mailbox, tx_header: TxFrameHeader, transmit: TX) -> R - where - TX: FnOnce(&mut [u32]) -> R, - { - let tx_ram = self.tx_msg_ram_mut(); - - // Clear mail slot; mainly for debugging purposes. - tx_ram.tbsa[idx as usize].reset(); - - // Calculate length of data in words - let data_len = ((tx_header.len as usize) + 3) / 4; - - //set header section - tx_ram.tbsa[idx as usize].header.merge(tx_header); - - //set data - let result = transmit(&mut tx_ram.tbsa[idx as usize].data[0..data_len]); - - // Set as ready to transmit - self.registers() - .txbar - .modify(|r, w| unsafe { w.ar().bits(r.ar().bits() | 1 << (idx as u32)) }); - - result - } - - #[inline] - fn abort_pending_mailbox(&mut self, idx: Mailbox, pending: PTX) -> Option - where - PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R, - { - if self.abort(idx) { - let tx_ram = self.tx_msg_ram(); - - //read back header section - let header = (&tx_ram.tbsa[idx as usize].header).into(); - Some(pending(idx, header, &tx_ram.tbsa[idx as usize].data)) - } else { - // Abort request failed because the frame was already sent (or being sent) on - // the bus. All mailboxes are now free. This can happen for small prescaler - // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR - // has preempted the execution. - None - } - } - - /// Attempts to abort the sending of a frame that is pending in a mailbox. - /// - /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be - /// aborted, this function has no effect and returns `false`. - /// - /// If there is a frame in the provided mailbox, and it is canceled successfully, this function - /// returns `true`. - #[inline] - fn abort(&mut self, idx: Mailbox) -> bool { - let can = self.registers(); - - // Check if there is a request pending to abort - if self.has_pending_frame(idx) { - let idx: u8 = idx.into(); - let idx = 1u8 << idx; - - // Abort Request - can.txbcr().write(|w| unsafe { w.cr().bits(idx) }); - - // Wait for the abort request to be finished. - loop { - if can.txbcf().read().cf().bits() & idx != 0 { - // Return false when a transmission has occured - break can.txbto().read().to().bits() & idx == 0; - } - } - } else { - false - } - } - - #[inline] - fn has_pending_frame(&self, idx: Mailbox) -> bool { - let can = self.registers(); - let idx: u8 = idx.into(); - let idx = 1u8 << idx; - - can.txbrp().read().trp().bits() & idx != 0 - } - - /// Returns `true` if no frame is pending for transmission. - #[inline] - pub fn is_idle(&self) -> bool { - let can = self.registers(); - can.txbrp().read().trp().bits() == 0x0 - } - - /// Clears the transmission complete flag. - #[inline] - pub fn clear_transmission_completed_flag(&mut self) { - let can = self.registers(); - can.ir().write(|w| w.tc().set_bit()); - } - - /// Clears the transmission cancelled flag. - #[inline] - pub fn clear_transmission_cancelled_flag(&mut self) { - let can = self.registers(); - can.ir().write(|w| w.tcf().set_bit()); - } -} - -#[doc(hidden)] -pub trait FifoNr: crate::sealed::Sealed { - const NR: usize; -} -#[doc(hidden)] -pub struct Fifo0; -impl crate::sealed::Sealed for Fifo0 {} -impl FifoNr for Fifo0 { - const NR: usize = 0; -} -#[doc(hidden)] -pub struct Fifo1; -impl crate::sealed::Sealed for Fifo1 {} -impl FifoNr for Fifo1 { - const NR: usize = 1; -} - -/// Notes whether an overrun has occurred. -/// Since both arms contain T, this can be 'unwrap'ed without causing a panic. -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -pub enum ReceiveOverrun { - /// No overrun has occured - NoOverrun(T), - /// an overrun has occurered - Overrun(T), -} -impl ReceiveOverrun { - /// unwraps itself and returns T - /// in contradiction to Option::unwrap, this does not panic - /// since both elements contain T. - #[inline] - pub fn unwrap(self) -> T { - match self { - ReceiveOverrun::NoOverrun(t) | ReceiveOverrun::Overrun(t) => t, - } - } -} - -/// Interface to the CAN receiver part. -pub struct Rx -where - FIFONR: FifoNr, -{ - _can: PhantomData, - _mode: PhantomData, - _nr: PhantomData, -} - -impl Rx -where - FIFONR: FifoNr, - I: Instance, -{ - #[inline] - unsafe fn conjure() -> Self { - Self { - _can: PhantomData, - _mode: PhantomData, - _nr: PhantomData, - } - } - - /// Creates a `&mut Self` out of thin air. - /// - /// This is only safe if it is the only way to access an `Rx`. - #[inline] - unsafe fn conjure_by_ref<'a>() -> &'a mut Self { - // Cause out of bounds access when `Self` is not zero-sized. - #[allow(clippy::unnecessary_operation)] - [()][core::mem::size_of::()]; - - // Any aligned pointer is valid for ZSTs. - &mut *NonNull::dangling().as_ptr() - } - - /// Returns a received frame if available. - /// - /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive( - &mut self, - receive: &mut RECV, - ) -> nb::Result, Infallible> - where - RECV: FnMut(RxFrameInfo, &[u32]) -> R, - { - if !self.rx_fifo_is_empty() { - let mbox = self.get_rx_mailbox(); - let idx: usize = mbox.into(); - let mailbox: &RxFifoElement = &self.rx_msg_ram().fxsa[idx]; - - let header: RxFrameInfo = (&mailbox.header).into(); - let word_len = (header.len + 3) / 4; - let result = Ok(receive(header, &mailbox.data[0..word_len as usize])); - self.release_mailbox(mbox); - - if self.has_overrun() { - result.map(ReceiveOverrun::Overrun) - } else { - result.map(ReceiveOverrun::NoOverrun) - } - } else { - Err(nb::Error::WouldBlock) - } - } - - #[inline] - fn registers(&self) -> &RegisterBlock { - unsafe { &*I::REGISTERS } - } - - #[inline] - fn rx_msg_ram(&self) -> &message_ram::Receive { - unsafe { &(&(*I::MSG_RAM).receive)[FIFONR::NR] } - } - - #[inline] - fn has_overrun(&self) -> bool { - let can = self.registers(); - match FIFONR::NR { - 0 => can.rxf0s().read().rf0l().bit(), - 1 => can.rxf1s().read().rf1l().bit(), - _ => unreachable!(), - } - } - - /// Returns if the fifo contains any new messages. - #[inline] - pub fn rx_fifo_is_empty(&self) -> bool { - let can = self.registers(); - match FIFONR::NR { - 0 => can.rxf0s().read().f0fl().bits() == 0, - 1 => can.rxf1s().read().f1fl().bits() == 0, - _ => unreachable!(), - } - } - - #[inline] - fn release_mailbox(&mut self, idx: Mailbox) { - unsafe { - (*I::MSG_RAM).receive[FIFONR::NR].fxsa[idx as u8 as usize].reset(); - } - - let can = self.registers(); - match FIFONR::NR { - 0 => can.rxf0a().write(|w| unsafe { w.f0ai().bits(idx.into()) }), - 1 => can.rxf1a().write(|w| unsafe { w.f1ai().bits(idx.into()) }), - _ => unreachable!(), - } - } - - #[inline] - fn get_rx_mailbox(&self) -> Mailbox { - let can = self.registers(); - let idx = match FIFONR::NR { - 0 => can.rxf0s().read().f0gi().bits(), - 1 => can.rxf1s().read().f1gi().bits(), - _ => unreachable!(), - }; - Mailbox::new(idx) - } -} - -/// The three mailboxes. -/// These are used for the transmit queue -/// and the two Receive FIFOs -#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] -pub enum Mailbox { - /// Transmit mailbox 0 - _0 = 0, - /// Transmit mailbox 1 - _1 = 1, - /// Transmit mailbox 2 - _2 = 2, -} -impl Mailbox { - #[inline] - fn new(idx: u8) -> Self { - match idx & 0b11 { - 0 => Mailbox::_0, - 1 => Mailbox::_1, - 2 => Mailbox::_2, - _ => unreachable!(), - } - } -} -impl From for u8 { - #[inline] - fn from(m: Mailbox) -> Self { - m as u8 - } -} -impl From for usize { - #[inline] - fn from(m: Mailbox) -> Self { - m as u8 as usize - } -} - -mod impls { - use super::sealed; - - /// Implements sealed::{Tx,Rx} for pins associated with a CAN peripheral - macro_rules! pins { - ($PER:ident => - (tx: [ $($( #[ $pmetatx:meta ] )* $tx:ident<$txaf:ident>),+ $(,)? ], - rx: [ $($( #[ $pmetarx:meta ] )* $rx:ident<$rxaf:ident>),+ $(,)? ])) => { - $( - $( #[ $pmetatx ] )* - impl super::sealed::Tx<$PER> for $tx> {} - )+ - $( - $( #[ $pmetarx ] )* - impl super::sealed::Rx<$PER> for $rx> {} - )+ - }; - } - - mod fdcan1 { - use crate::fdcan; - use crate::fdcan::message_ram; - use crate::gpio::{ - gpioa::{PA11, PA12}, - gpiob::{PB8, PB9}, - gpiod::{PD0, PD1}, - AF9, - }; - use crate::stm32; - use crate::stm32::FDCAN1; - - // All STM32G4 models with CAN support these pins - pins! { - FDCAN1 => ( - tx: [ - PA12, - PB9, - PD1, - ], - rx: [ - PA11, - PB8, - PD0, - ] - ) - } - - unsafe impl message_ram::MsgRamExt for FDCAN1 { - const MSG_RAM: *mut message_ram::RegisterBlock = (0x4000_a400 as *mut _); - } - unsafe impl fdcan::Instance for FDCAN1 { - const REGISTERS: *mut stm32::fdcan::RegisterBlock = FDCAN1::ptr() as *mut _; - } - } - - #[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", - ))] - mod fdcan2 { - use crate::fdcan; - use crate::fdcan::message_ram; - use crate::gpio::{ - gpiob::{PB12, PB13, PB5, PB6}, - AF9, - }; - use crate::stm32::{self, FDCAN2}; - - pins! { - FDCAN2 => ( - tx: [ - PB6, - PB13, - ], - rx: [ - PB5, - PB12, - ]) - } - - unsafe impl fdcan::Instance for FDCAN2 { - const REGISTERS: *mut stm32::fdcan::RegisterBlock = FDCAN2::ptr() as *mut _; - } - - unsafe impl message_ram::MsgRamExt for FDCAN2 { - const MSG_RAM: *mut message_ram::RegisterBlock = (0x4000_a750 as *mut _); - } - } - - #[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - ))] - mod fdcan3 { - use crate::fdcan; - use crate::fdcan::message_ram; - use crate::gpio::{ - gpioa::{PA15, PA8}, - gpiob::{PB3, PB4}, - AF11, - }; - use crate::stm32::{self, FDCAN3}; - - pins! { - FDCAN3 => ( - tx: [ - PA15, - PB4, - ], - rx: [ - PA8, - PB3, - ]) - } - - unsafe impl fdcan::Instance for FDCAN3 { - const REGISTERS: *mut stm32::fdcan::RegisterBlock = FDCAN3::ptr() as *mut _; - } - - unsafe impl message_ram::MsgRamExt for FDCAN3 { - const MSG_RAM: *mut message_ram::RegisterBlock = (0x4000_aaa0 as *mut _); - } - } -} diff --git a/src/flash.rs b/src/flash.rs index a7afb799..5da63b3b 100644 --- a/src/flash.rs +++ b/src/flash.rs @@ -436,7 +436,6 @@ impl Parts { } } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", diff --git a/src/i2c.rs b/src/i2c.rs index 6567d08d..7132f211 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -5,7 +5,6 @@ use embedded_hal_old::blocking::i2c::{Read, Write, WriteRead}; use crate::gpio::{gpioa::*, gpiob::*, gpioc::*, gpiof::*}; #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -15,7 +14,6 @@ use crate::gpio::{gpiog::*, AF3}; use crate::gpio::{AlternateOD, AF2, AF4, AF8}; use crate::rcc::{Enable, GetBusFreq, Rcc, RccBus, Reset}; #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -487,7 +485,6 @@ i2c!( PA9>, PC4>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -505,7 +502,6 @@ i2c!( PC11>, PC9>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -513,7 +509,6 @@ i2c!( ))] PF4>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -525,7 +520,6 @@ i2c!( PA8>, PC8>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -533,7 +527,6 @@ i2c!( ))] PF3>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -544,7 +537,6 @@ i2c!( ); #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", diff --git a/src/lib.rs b/src/lib.rs index 16bfe5d2..470aa878 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ #[cfg(not(any( feature = "stm32g431", feature = "stm32g441", - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -25,9 +24,6 @@ compile_error!( stm32g4a1" ); -extern crate bare_metal; -extern crate void; - pub extern crate cortex_m; pub extern crate nb; pub extern crate stm32g4; @@ -42,9 +38,6 @@ pub use stm32g4::stm32g431 as stm32; #[cfg(feature = "stm32g441")] pub use stm32g4::stm32g441 as stm32; -#[cfg(feature = "stm32g471")] -pub use stm32g4::stm32g471 as stm32; - #[cfg(feature = "stm32g473")] pub use stm32g4::stm32g473 as stm32; @@ -71,6 +64,7 @@ pub use crate::stm32::interrupt; pub mod adc; pub mod bb; +#[cfg(feature = "can")] pub mod can; pub mod comparator; #[cfg(feature = "cordic")] diff --git a/src/opamp.rs b/src/opamp.rs index aa89a17f..5679c161 100644 --- a/src/opamp.rs +++ b/src/opamp.rs @@ -806,7 +806,7 @@ opamps! { }, } -#[cfg(any(feature = "stm32g471", feature = "stm32g491", feature = "stm32g4a1"))] +#[cfg(any(feature = "stm32g491", feature = "stm32g4a1"))] opamps! { Opamp1 => opamp1: { vinm0: crate::gpio::gpioa::PA3, diff --git a/src/pwm.rs b/src/pwm.rs index c5c86e07..b52d9598 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -184,7 +184,6 @@ use crate::stm32::RCC; ))] use crate::stm32::TIM20; #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -197,7 +196,6 @@ use crate::rcc::{Enable, GetBusFreq, Rcc, Reset}; use crate::time::{ExtU32, Hertz, NanoSecond, RateExtU32}; #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -205,7 +203,6 @@ use crate::time::{ExtU32, Hertz, NanoSecond, RateExtU32}; ))] use crate::gpio::gpiog::*; #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -573,7 +570,6 @@ pins! { PA1>, PB15>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -766,7 +762,6 @@ pins! { PB9>, PD15>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -782,7 +777,6 @@ pins! { BRK2: [] } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -1455,7 +1449,6 @@ tim_hal! { TIM4: (tim4, u16, 16, DIR: cms), } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -1767,7 +1760,6 @@ tim_pin_hal! { TIM4: (C4, cc4e, cc4p, ccmr2_output, oc4pe, oc4m, ccr4, u16), } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", diff --git a/src/rcc/enable.rs b/src/rcc/enable.rs index e28bd237..1d3523eb 100644 --- a/src/rcc/enable.rs +++ b/src/rcc/enable.rs @@ -74,8 +74,7 @@ bus! { GPIOE => (AHB2, 4), GPIOF => (AHB2, 5), GPIOG => (AHB2, 6), - ADC1 => (AHB2, 13), - ADC2 => (AHB2, 13), + ADC12_COMMON => (AHB2, 13), DAC1 => (AHB2, 16), DAC2 => (AHB2, 17), DAC3 => (AHB2, 18), @@ -84,7 +83,6 @@ bus! { } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -93,18 +91,7 @@ bus! { feature = "stm32g4a1", ))] bus! { - ADC3 => (AHB2, 14), -} - -#[cfg(any( - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484" -))] -bus! { - ADC4 => (AHB2, 14), - ADC5 => (AHB2, 14), + ADC345_COMMON => (AHB2, 14), } #[cfg(any(feature = "stm32g431", feature = "stm32g441", feature = "stm32g484",))] @@ -147,7 +134,6 @@ bus! { } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -170,7 +156,6 @@ bus! { } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -182,7 +167,6 @@ bus! { } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -207,7 +191,6 @@ bus! { } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", diff --git a/src/serial/usart.rs b/src/serial/usart.rs index b71ef231..09288991 100644 --- a/src/serial/usart.rs +++ b/src/serial/usart.rs @@ -845,7 +845,7 @@ tx: [ (PB6, AF7), (PC4, AF7), (PE0, AF7), - #[cfg(any(feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", feature = "stm32g484"))] + #[cfg(any(feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", feature = "stm32g484"))] (PG9, AF7), ], rx: [ @@ -910,14 +910,14 @@ uart_shared!(LPUART1, LPUART1_RX, LPUART1_TX, (PA2, AF12), (PB11, AF8), (PC1, AF8), - #[cfg(any(feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", feature = "stm32g484"))] + #[cfg(any(feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", feature = "stm32g484"))] (PG7, AF8), ], rx: [ (PA3, AF12), (PB10, AF8), (PC0, AF8), - #[cfg(any(feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", feature = "stm32g484"))] + #[cfg(any(feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", feature = "stm32g484"))] (PG8, AF8), ] ); diff --git a/src/spi.rs b/src/spi.rs index ef3c7839..19bd2fef 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -3,7 +3,6 @@ use crate::dma::traits::TargetAddress; use crate::dma::MemoryToPeripheral; use crate::gpio::{gpioa::*, gpiob::*, gpioc::*, gpiof::*, Alternate, AF5, AF6}; #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -12,7 +11,6 @@ use crate::gpio::{gpioa::*, gpiob::*, gpioc::*, gpiof::*, Alternate, AF5, AF6}; use crate::gpio::{gpioe::*, gpiog::*}; use crate::rcc::{Enable, GetBusFreq, Rcc, RccBus, Reset}; #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -382,7 +380,6 @@ spi!( PA5>, PB3>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -394,7 +391,6 @@ spi!( PA6>, PB4>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -406,7 +402,6 @@ spi!( PA7>, PB5>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -444,7 +439,6 @@ spi!( PB3>, PC10>, #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", @@ -464,7 +458,6 @@ spi!( ); #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", diff --git a/src/timer.rs b/src/timer.rs index dc92dc9f..c2a071a5 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -387,7 +387,6 @@ hal_ext_trgo! { } #[cfg(any( - feature = "stm32g471", feature = "stm32g473", feature = "stm32g474", feature = "stm32g483", diff --git a/tests/nucleo-g474.rs b/tests/nucleo-g474.rs index f643411f..5188a6ae 100644 --- a/tests/nucleo-g474.rs +++ b/tests/nucleo-g474.rs @@ -35,7 +35,7 @@ mod tests { use fixed::types::I1F15; use fugit::RateExtU32; use stm32g4xx_hal::{ - adc::{self, AdcClaim, Temperature, Vref}, + adc::{self, temperature::Temperature, AdcClaim, AdcCommonExt, Vref}, cordic::{ op::{dynamic::Mode, Magnitude, SinCos, Sqrt}, prec::P60, @@ -149,8 +149,8 @@ mod tests { let max: MicrosDurationU32 = 505u32.micros(); debug!("Awaiting first rising edge..."); - let duration_until_lo = await_lo(&gpioa, max).unwrap(); - let first_lo_duration = await_hi(&gpioa, max).unwrap(); + let duration_until_lo = await_lo(gpioa, max).unwrap(); + let first_lo_duration = await_hi(gpioa, max).unwrap(); let mut hi_duration = 0.micros(); let mut lo_duration = 0.micros(); @@ -158,7 +158,7 @@ mod tests { for _ in 0..10 { // Make sure the timer half periods are within 495-505us - hi_duration = await_lo(&gpioa, max).unwrap(); + hi_duration = await_lo(gpioa, max).unwrap(); assert!( hi_duration > min && hi_duration < max, "hi: {} < {} < {}", @@ -167,7 +167,7 @@ mod tests { max ); - lo_duration = await_hi(&gpioa, max).unwrap(); + lo_duration = await_hi(gpioa, max).unwrap(); assert!( lo_duration > min && lo_duration < max, "lo: {} < {} < {}", @@ -234,15 +234,14 @@ mod tests { // TODO: Is it ok to steal these? let cp = unsafe { stm32::CorePeripherals::steal() }; let dp = unsafe { stm32::Peripherals::steal() }; - let rcc = dp.RCC.constrain(); + let mut rcc = dp.RCC.constrain(); let mut delay = cp.SYST.delay(&rcc.clocks); - let mut adc = dp - .ADC1 - .claim(adc::ClockSource::SystemClock, &rcc, &mut delay, true); + let mut adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim(dp.ADC1, &mut delay); - adc.enable_temperature(&dp.ADC12_COMMON); - adc.enable_vref(&dp.ADC12_COMMON); + adc12_common.enable_temperature(); + adc12_common.enable_vref(); let sample_time = adc::config::SampleTime::Cycles_640_5; let vref = adc.convert(&Vref, sample_time); @@ -262,7 +261,79 @@ mod tests { adc::config::Resolution::Twelve, ); debug!("temp: {}°C", temp); - assert!((20.0..30.0).contains(&temp), "20.0 < {} < 30.0", temp); + assert!((20.0..35.0).contains(&temp), "20.0 < {} < 35.0", temp); + } + + #[test] + fn adc_dma() { + use super::*; + use hal::{ + adc, + dma::{channel::DMAExt, config::DmaConfig, TransferExt}, + }; + + // TODO: Is it ok to steal these? + let cp = unsafe { stm32::CorePeripherals::steal() }; + let dp = unsafe { stm32::Peripherals::steal() }; + let mut rcc = dp.RCC.constrain(); + let mut delay = cp.SYST.delay(&rcc.clocks); + + let channels = dp.DMA1.split(&rcc); + let config = DmaConfig::default() + .transfer_complete_interrupt(false) + .circular_buffer(false) + .memory_increment(true); + + let mut adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let mut adc = adc12_common.claim(dp.ADC1, &mut delay); + + let sample_time = adc::config::SampleTime::Cycles_640_5; + + adc12_common.enable_vref(); + adc12_common.enable_temperature(); + adc.set_continuous(adc::config::Continuous::Single); + adc.reset_sequence(); + adc.configure_channel(&Vref, adc::config::Sequence::One, sample_time); + adc.configure_channel(&Temperature, adc::config::Sequence::Two, sample_time); + + defmt::info!("Setup DMA"); + let first_buffer = cortex_m::singleton!(: [u16; 2] = [0; 2]).unwrap(); + let mut transfer = channels.ch1.into_peripheral_to_memory_transfer( + adc.enable_dma(adc::config::Dma::Single), + &mut first_buffer[..], + config, + ); + + transfer.start(|adc| adc.start_conversion()); + + defmt::info!("Wait for Conversion"); + while !transfer.get_transfer_complete_flag() {} + defmt::info!("Conversion Done"); + + transfer.pause(|adc| adc.cancel_conversion()); + let (_ch1, adc, first_buffer) = transfer.free(); + let _adc = adc.disable(); + + let vref_reading = first_buffer[0]; + let temperature_reading = first_buffer[1]; + + let vref_cal = VrefCal::get().read(); + let vdda = VDDA_CALIB * vref_cal as u32 / vref_reading as u32; + debug!("vdda: {}mV", vdda); + assert!((3200..3400).contains(&vdda)); + + let vref = + Vref::sample_to_millivolts_ext(vref_reading, vdda, adc::config::Resolution::Twelve); + debug!("vref: {}mV", vref); + assert!((1182..1232).contains(&vref)); // From G474 datasheet + + let temp = Temperature::temperature_to_degrees_centigrade( + temperature_reading, + vdda as f32 / 1000., + adc::config::Resolution::Twelve, + ); + debug!("temp: {}°C", temp); + assert!((20.0..35.0).contains(&temp), "20.0 < {} < 35.0", temp); } #[test] @@ -287,11 +358,11 @@ mod tests { dac.set_value(0); delay.delay_ms(1); - assert!(is_pax_low(&gpioa, 4)); + assert!(is_pax_low(gpioa, 4)); dac.set_value(4095); delay.delay_ms(1); - assert!(!is_pax_low(&gpioa, 4)); + assert!(!is_pax_low(gpioa, 4)); } } diff --git a/tests/nucleo-g474_w_jumpers.rs b/tests/nucleo-g474_w_jumpers.rs index fad0ae51..f5eec361 100644 --- a/tests/nucleo-g474_w_jumpers.rs +++ b/tests/nucleo-g474_w_jumpers.rs @@ -1,12 +1,12 @@ #![no_std] #![no_main] -/// Requires a jumper from A1<->A2 (arduino naming) aka PA1<->PA4 +// Requires a jumper from A1<->A2 (arduino naming) aka PA1<->PA4 #[path = "../examples/utils/mod.rs"] mod utils; -use stm32g4xx_hal::adc::{self, AdcClaim}; +use stm32g4xx_hal::adc::{self, AdcClaim, AdcCommonExt}; use stm32g4xx_hal::comparator::{self, ComparatorSplit}; use stm32g4xx_hal::dac::{self, DacExt, DacOut}; use stm32g4xx_hal::delay::{self, SYSTDelayExt}; @@ -282,9 +282,8 @@ fn setup_opamp_comp_dac() -> Peripherals { let mut rcc = dp.RCC.constrain(); let mut delay = cp.SYST.delay(&rcc.clocks); - let adc = dp - .ADC1 - .claim(adc::ClockSource::SystemClock, &rcc, &mut delay, true); + let adc12_common = dp.ADC12_COMMON.claim(Default::default(), &mut rcc); + let adc = adc12_common.claim(dp.ADC1, &mut delay); let gpioa = dp.GPIOA.split(&mut rcc); let pa1 = gpioa.pa1.into_analog();