From cf84bb2d0e1a4e6ca38d4c908bafc6ccd49f1cd1 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 27 Aug 2025 14:55:57 +0200 Subject: [PATCH 01/12] Add src/adc.rs from H7 hal --- src/adc.rs | 1141 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + 2 files changed, 1144 insertions(+) create mode 100644 src/adc.rs diff --git a/src/adc.rs b/src/adc.rs new file mode 100644 index 0000000..a891014 --- /dev/null +++ b/src/adc.rs @@ -0,0 +1,1141 @@ +//! Analog to Digital Converter (ADC) +//! +//! ADC1 and ADC2 share a reset line. To initialise both of them, use the +//! [`adc12`] method. +//! +//! # Examples +//! +//! - [Reading a voltage using ADC1](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc.rs) +//! - [Reading a temperature using ADC3](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/temperature.rs) +//! - [Using ADC1 and ADC2 together](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc12.rs) +//! - [Using ADC1 and ADC2 in parallel](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc12_parallel.rs) +//! - [Using ADC1 through DMA](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc_dma.rs) + +use embedded_hal_02::blocking::delay::DelayUs; + +use core::convert::Infallible; +use core::marker::PhantomData; + +use nb::block; + +use crate::stm32::ADC12_COMMON; +use crate::stm32::{ADC1, ADC2}; +#[cfg(not(feature = "rm0455"))] +use crate::stm32::{ADC3, ADC3_COMMON}; + +#[cfg(feature = "rm0455")] +use crate::stm32::adc12_common::ccr::PRESC_A; +#[cfg(not(feature = "rm0455"))] +use crate::stm32::adc3_common::ccr::PRESC_A; + +use crate::gpio::{self, Analog}; +use crate::pwr::{current_vos, VoltageScale}; +use crate::rcc::rec::AdcClkSelGetter; +use crate::rcc::{rec, CoreClocks, ResetEnable}; +use crate::time::Hertz; + +#[cfg(any(feature = "rm0433", feature = "rm0399"))] +pub type Resolution = crate::stm32::adc3::cfgr::RES_A; +#[cfg(any(feature = "rm0455", feature = "rm0468"))] +pub type Resolution = crate::stm32::adc1::cfgr::RES_A; + +trait NumberOfBits { + fn number_of_bits(&self) -> u32; +} + +impl NumberOfBits for Resolution { + fn number_of_bits(&self) -> u32 { + match *self { + Resolution::EightBit => 8, + Resolution::TenBit => 10, + Resolution::TwelveBit => 12, + Resolution::FourteenBit => 14, + _ => 16, + } + } +} + +/// Enabled ADC (type state) +pub struct Enabled; +/// Disabled ADC (type state) +pub struct Disabled; + +pub trait ED {} +impl ED for Enabled {} +impl ED for Disabled {} + +pub struct Adc { + rb: ADC, + sample_time: AdcSampleTime, + resolution: Resolution, + lshift: AdcLshift, + clock: Hertz, + current_channel: Option, + _enabled: PhantomData, +} + +/// ADC DMA modes +/// +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AdcDmaMode { + OneShot, + Circular, +} + +/// ADC sampling time +/// +/// Options for the sampling time, each is T + 0.5 ADC clock cycles. +// +// Refer to RM0433 Rev 7 - Chapter 25.4.13 +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(non_camel_case_types)] +pub enum AdcSampleTime { + /// 1.5 cycles sampling time + T_1, + /// 2.5 cycles sampling time + T_2, + /// 8.5 cycles sampling time + T_8, + /// 16.5 cycles sampling time + T_16, + /// 32.5 cycles sampling time + #[default] + T_32, + /// 64.5 cycles sampling time + T_64, + /// 387.5 cycles sampling time + T_387, + /// 810.5 cycles sampling time + T_810, +} + +impl AdcSampleTime { + /// Returns the number of half clock cycles represented by this sampling time + fn clock_cycles_x2(&self) -> u32 { + let x = match self { + AdcSampleTime::T_1 => 1, + AdcSampleTime::T_2 => 2, + AdcSampleTime::T_8 => 8, + AdcSampleTime::T_16 => 16, + AdcSampleTime::T_32 => 32, + AdcSampleTime::T_64 => 64, + AdcSampleTime::T_387 => 387, + AdcSampleTime::T_810 => 810, + }; + (2 * x) + 1 + } +} + +// Refer to RM0433 Rev 7 - Chapter 25.4.13 +impl From for u8 { + fn from(val: AdcSampleTime) -> u8 { + match val { + AdcSampleTime::T_1 => 0b000, + AdcSampleTime::T_2 => 0b001, + AdcSampleTime::T_8 => 0b010, + AdcSampleTime::T_16 => 0b011, + AdcSampleTime::T_32 => 0b100, + AdcSampleTime::T_64 => 0b101, + AdcSampleTime::T_387 => 0b110, + AdcSampleTime::T_810 => 0b111, + } + } +} + +/// ADC LSHIFT\[3:0\] of the converted value +/// +/// Only values in range of 0..=15 are allowed. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcLshift(u8); + +impl AdcLshift { + pub fn new(lshift: u8) -> Self { + if lshift > 15 { + panic!("LSHIFT[3:0] must be in range of 0..=15"); + } + + AdcLshift(lshift) + } + + pub fn value(self) -> u8 { + self.0 + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcCalOffset(u16); + +impl AdcCalOffset { + pub fn value(self) -> u16 { + self.0 + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcCalLinear([u32; 6]); + +impl AdcCalLinear { + pub fn value(self) -> [u32; 6] { + self.0 + } +} + +macro_rules! adc_pins { + ($ADC:ident, $($input:ty => $chan:expr),+ $(,)*) => { + $( + impl embedded_hal_02::adc::Channel<$ADC> for $input { + type ID = u8; + + fn channel() -> u8 { + $chan + } + } + )+ + }; +} + +macro_rules! adc_internal { + ([$INT_ADC:ident, $INT_ADC_COMMON:ident]; $($input:ty => ($chan:expr, $en:ident)),+ $(,)*) => { + $( + impl $input { + pub fn new() -> Self { + Self {} + } + + /// Enables the internal voltage/sensor + /// ADC must be disabled. + pub fn enable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { + + let common = unsafe { &*$INT_ADC_COMMON::ptr() }; + + common.ccr.modify(|_, w| w.$en().enabled()); + } + /// Disables the internal voltage/sdissor + /// ADC must be disabled. + pub fn disable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { + + let common = unsafe { &*$INT_ADC_COMMON::ptr() }; + + common.ccr.modify(|_, w| w.$en().disabled()); + } + } + + adc_pins!($INT_ADC, $input => $chan); + )+ + }; +} + +/// Vref internal signal +#[derive(Default)] +pub struct Vrefint; +/// Vbat internal signal +#[derive(Default)] +pub struct Vbat; +/// Internal temperature sensor +#[derive(Default)] +pub struct Temperature; + +// Not implementing Pxy_C adc pins +// Just implmenting INPx pins (INNx defaulting to V_ref-) +// +// Refer to DS12110 Rev 7 - Chapter 5 (Table 9) +adc_pins!(ADC1, + // 0, 1 are Pxy_C pins + gpio::PF11 => 2, + gpio::PA6 => 3, + gpio::PC4 => 4, + gpio::PB1 => 5, + gpio::PF12 => 6, + gpio::PA7 => 7, + gpio::PC5 => 8, + gpio::PB0 => 9, + gpio::PC0 => 10, + gpio::PC1 => 11, + gpio::PC2 => 12, + gpio::PC3 => 13, + gpio::PA2 => 14, + gpio::PA3 => 15, + gpio::PA0 => 16, + gpio::PA1 => 17, + gpio::PA4 => 18, + gpio::PA5 => 19, +); + +adc_pins!(ADC2, + // 0, 1 are Pxy_C pins + gpio::PF13 => 2, + gpio::PA6 => 3, + gpio::PC4 => 4, + gpio::PB1 => 5, + gpio::PF14 => 6, + gpio::PA7 => 7, + gpio::PC5 => 8, + gpio::PB0 => 9, + gpio::PC0 => 10, + gpio::PC1 => 11, + gpio::PC2 => 12, + gpio::PC3 => 13, + gpio::PA2 => 14, + gpio::PA3 => 15, + // 16, 17 are dac_outX + gpio::PA4 => 18, + gpio::PA5 => 19, +); + +#[cfg(feature = "rm0455")] +adc_internal!( + [ADC2, ADC12_COMMON]; + + Vbat => (14, vbaten), + Temperature => (18, vsenseen), + Vrefint => (19, vrefen) +); + +// -------- ADC3 -------- + +#[cfg(any(feature = "rm0433", feature = "rm0399"))] +adc_pins!(ADC3, + // 0, 1 are Pxy_C pins + gpio::PF9 => 2, + gpio::PF7 => 3, + gpio::PF5 => 4, + gpio::PF3 => 5, + gpio::PF10 => 6, + gpio::PF8 => 7, + gpio::PF6 => 8, + gpio::PF4 => 9, + gpio::PC0 => 10, + gpio::PC1 => 11, + gpio::PC2 => 12, + gpio::PH2 => 13, + gpio::PH3 => 14, + gpio::PH4 => 15, + gpio::PH5 => 16, +); +#[cfg(any(feature = "rm0433", feature = "rm0399"))] +adc_internal!( + [ADC3, ADC3_COMMON]; + + Vbat => (17, vbaten), + Temperature => (18, vsenseen), + Vrefint => (19, vrefen) +); + +// ADC 3 not present on RM0455 parts + +#[cfg(feature = "rm0468")] +adc_pins!(ADC3, + // 0, 1 are Pxy_C pins + gpio::PF9 => 2, + gpio::PF7 => 3, + gpio::PF5 => 4, + gpio::PF3 => 5, + gpio::PF10 => 6, + gpio::PF8 => 7, + gpio::PF6 => 8, + gpio::PF4 => 9, + gpio::PC0 => 10, + gpio::PC1 => 11, + gpio::PC2 => 12, + gpio::PH2 => 13, + gpio::PH3 => 14, + gpio::PH4 => 15, + // Although ADC3_INP16 appears in device datasheets (on PH5), RM0468 Rev 7 + // Figure 231 does not show ADC3_INP16 +); +#[cfg(feature = "rm0468")] +adc_internal!( + [ADC3, ADC3_COMMON]; + + Vbat => (16, vbaten), + Temperature => (17, vsenseen), + Vrefint => (18, vrefen) +); + +pub trait AdcExt: Sized { + type Rec: ResetEnable; + + fn adc( + self, + f_adc: impl Into, + delay: &mut impl DelayUs, + prec: Self::Rec, + clocks: &CoreClocks, + ) -> Adc; +} + +/// Stored ADC config can be restored using the `Adc::restore_cfg` method +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct StoredConfig(AdcSampleTime, Resolution, AdcLshift); + +#[cfg(feature = "defmt")] +impl defmt::Format for StoredConfig { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "StoredConfig({:?}, {:?}, {:?})", + self.0, + defmt::Debug2Format(&self.1), + self.2 + ) + } +} + +/// Returns the frequency of the current adc_ker_ck +/// +/// # Panics +/// +/// Panics if the kernel clock is not running +fn kernel_clk_unwrap( + prec: &impl AdcClkSelGetter, + clocks: &CoreClocks, +) -> Hertz { + match prec.get_kernel_clk_mux() { + Some(rec::AdcClkSel::Pll2P) => { + clocks.pll2_p_ck().expect("ADC: PLL2_P must be enabled") + } + Some(rec::AdcClkSel::Pll3R) => { + clocks.pll3_r_ck().expect("ADC: PLL3_R must be enabled") + } + Some(rec::AdcClkSel::Per) => { + clocks.per_ck().expect("ADC: PER clock must be enabled") + } + _ => unreachable!(), + } +} + +// ADC12 is a unique case where a single reset line is used to control two +// peripherals that have separate peripheral definitions in the SVD. + +/// Initialise ADC12 together +/// +/// Sets all configurable parameters to one-shot defaults, +/// performs a boot-time calibration. +pub fn adc12( + adc1: ADC1, + adc2: ADC2, + f_adc: impl Into, + delay: &mut impl DelayUs, + prec: rec::Adc12, + clocks: &CoreClocks, +) -> (Adc, Adc) { + // Consume ADC register block, produce ADC1/2 with default settings + let mut adc1 = Adc::::default_from_rb(adc1); + let mut adc2 = Adc::::default_from_rb(adc2); + + // Check adc_ker_ck_input + kernel_clk_unwrap(&prec, clocks); + + // Enable AHB clock + let prec = prec.enable(); + + // Power Down + adc1.power_down(); + adc2.power_down(); + + // Reset peripheral + let prec = prec.reset(); + + // Power Up, Preconfigure and Calibrate + adc1.power_up(delay); + adc2.power_up(delay); + let f_adc = adc1.configure_clock(f_adc.into(), prec, clocks); // ADC12_COMMON + adc2.clock = f_adc; + adc1.preconfigure(); + adc2.preconfigure(); + adc1.calibrate(); + adc2.calibrate(); + + (adc1, adc2) +} + +/// Free both ADC1 and ADC2 along with PREC. +/// +/// Since ADC1 and ADC2 are controlled together, they are freed together. +pub fn free_adc12( + adc1: Adc, + adc2: Adc, +) -> (ADC1, ADC2, rec::Adc12) { + ( + adc1.rb, + adc2.rb, + rec::Adc12 { + _marker: PhantomData, + }, + ) +} + +#[cfg(not(feature = "rm0455"))] +/// Freeing both the peripheral and PREC is possible for ADC3 +impl Adc { + /// Releases the ADC peripheral + pub fn free(self) -> (ADC3, rec::Adc3) { + ( + self.rb, + rec::Adc3 { + _marker: PhantomData, + }, + ) + } +} + +#[allow(unused_macros)] +macro_rules! adc_hal { + ($( + $ADC:ident, $ADC_COMMON:ident: ( + $adcX: ident, + $Rec:ident + $(, $ldordy:ident )* + ) + ),+ $(,)*) => { + $( + impl AdcExt<$ADC> for $ADC { + type Rec = rec::$Rec; + + fn adc(self, + f_adc: impl Into, + delay: &mut impl DelayUs, + prec: rec::$Rec, + clocks: &CoreClocks) -> Adc<$ADC, Disabled> + { + Adc::$adcX(self, f_adc, delay, prec, clocks) + } + } + + impl Adc<$ADC, Disabled> { + /// Initialise ADC + /// + /// Sets all configurable parameters to one-shot defaults, + /// performs a boot-time calibration. + pub fn $adcX(adc: $ADC, f_adc: impl Into, delay: &mut impl DelayUs, + prec: rec::$Rec, clocks: &CoreClocks + ) -> Self { + // Consume ADC register block, produce Self with default + // settings + let mut adc = Self::default_from_rb(adc); + + // Enable AHB clock + let prec = prec.enable(); + + // Power Down + adc.power_down(); + + // Reset peripheral + let prec = prec.reset(); + + // Power Up, Preconfigure and Calibrate + adc.power_up(delay); + adc.configure_clock(f_adc.into(), prec, clocks); + adc.preconfigure(); + adc.calibrate(); + + adc + } + /// Creates ADC with default settings + fn default_from_rb(rb: $ADC) -> Self { + Self { + rb, + sample_time: AdcSampleTime::default(), + resolution: Resolution::SixteenBit, + lshift: AdcLshift::default(), + clock: Hertz::from_raw(0), + current_channel: None, + _enabled: PhantomData, + } + } + /// Sets the clock configuration for this ADC. This is common + /// between ADC1 and ADC2, so the prec block is used to ensure + /// this method can only be called on one of the ADCs (or both, + /// using the [adc12](#method.adc12) method). + /// + /// Only `CKMODE[1:0]` = 0 is supported + fn configure_clock(&mut self, f_adc: Hertz, prec: rec::$Rec, clocks: &CoreClocks) -> Hertz { + let ker_ck = kernel_clk_unwrap(&prec, clocks); + + let max_ker_ck = match current_vos() { + // See RM0468 Rev 3 Table 56. + #[cfg(feature = "rm0468")] + VoltageScale::Scale0 | VoltageScale::Scale1 => 160_000_000, + #[cfg(feature = "rm0468")] + VoltageScale::Scale2 => 60_000_000, + #[cfg(feature = "rm0468")] + VoltageScale::Scale3 => 40_000_000, + + // See RM0433 Rev 7 Table 59. + #[cfg(not(feature = "rm0468"))] + VoltageScale::Scale0 | VoltageScale::Scale1 => 80_000_000, + #[cfg(not(feature = "rm0468"))] + VoltageScale::Scale2 | VoltageScale::Scale3 => 40_000_000 + }; + assert!(ker_ck.raw() <= max_ker_ck, + "Kernel clock violates maximum frequency defined in Reference Manual. \ + Can result in erroneous ADC readings"); + + let f_adc = self.configure_clock_unchecked(f_adc, prec, clocks); + + // Maximum ADC clock speed. With BOOST = 0 there is a no + // minimum frequency given in part datasheets + assert!(f_adc.raw() <= 50_000_000); + + f_adc + } + + /// No clock checks + fn configure_clock_unchecked(&mut self, f_adc: Hertz, prec: rec::$Rec, clocks: &CoreClocks) -> Hertz { + let ker_ck = kernel_clk_unwrap(&prec, clocks); + + // Target mux output. See RM0433 Rev 7 - Figure 136. + #[cfg(feature = "revision_v")] + let f_target = f_adc.raw() * 2; + + #[cfg(not(feature = "revision_v"))] + let f_target = f_adc.raw(); + + let (divider, presc) = match ker_ck.raw().div_ceil(f_target) { + 1 => (1, PRESC_A::Div1), + 2 => (2, PRESC_A::Div2), + 3..=4 => (4, PRESC_A::Div4), + 5..=6 => (6, PRESC_A::Div6), + 7..=8 => (8, PRESC_A::Div8), + 9..=10 => (10, PRESC_A::Div10), + 11..=12 => (12, PRESC_A::Div12), + 13..=16 => (16, PRESC_A::Div16), + 17..=32 => (32, PRESC_A::Div32), + 33..=64 => (64, PRESC_A::Div64), + 65..=128 => (128, PRESC_A::Div128), + 129..=256 => (256, PRESC_A::Div256), + _ => panic!("Selecting the ADC clock required a prescaler > 256, \ + which is not possible in hardware. Either increase the ADC \ + clock frequency or decrease the kernel clock frequency"), + }; + unsafe { &*$ADC_COMMON::ptr() }.ccr.modify(|_, w| w.presc().variant(presc)); + + // Calculate actual value. See RM0433 Rev 7 - Figure 136. + #[cfg(feature = "revision_v")] + let f_adc = Hertz::from_raw(ker_ck.raw() / (divider * 2)); + + // Calculate actual value Revison Y. See RM0433 Rev 7 - Figure 137. + #[cfg(not(feature = "revision_v"))] + let f_adc = Hertz::from_raw(ker_ck.raw() / divider); + + self.clock = f_adc; + f_adc + } + + /// Disables Deeppowerdown-mode and enables voltage regulator + /// + /// Note: After power-up, a [`calibration`](#method.calibrate) shall be run + pub fn power_up(&mut self, delay: &mut impl DelayUs) { + // Refer to RM0433 Rev 7 - Chapter 25.4.6 + self.rb.cr.modify(|_, w| + w.deeppwd().clear_bit() + .advregen().set_bit() + ); + delay.delay_us(10_u8); + + // check LDORDY bit if present + $( + #[cfg(feature = "revision_v")] + while { + let $ldordy = self.rb.isr.read().bits() & 0x1000; + $ldordy == 0 + }{} + )* + } + + /// Enables Deeppowerdown-mode and disables voltage regulator + /// + /// Note: This resets the [`calibration`](#method.calibrate) of the ADC + pub fn power_down(&mut self) { + // Refer to RM0433 Rev 7 - Chapter 25.4.6 + self.rb.cr.modify(|_, w| + w.deeppwd().set_bit() + .advregen().clear_bit() + ); + } + + /// Calibrates the ADC in single channel mode + /// + /// Note: The ADC must be disabled + pub fn calibrate(&mut self) { + // Refer to RM0433 Rev 7 - Chapter 25.4.8 + self.check_calibration_conditions(); + + // single channel (INNx equals to V_ref-) + self.rb.cr.modify(|_, w| + w.adcaldif().clear_bit() + .adcallin().set_bit() + ); + // calibrate + self.rb.cr.modify(|_, w| w.adcal().set_bit()); + while self.rb.cr.read().adcal().bit_is_set() {} + } + + fn check_calibration_conditions(&self) { + let cr = self.rb.cr.read(); + if cr.aden().bit_is_set() { + panic!("Cannot start calibration when the ADC is enabled"); + } + if cr.deeppwd().bit_is_set() { + panic!("Cannot start calibration when the ADC is in deeppowerdown-mode"); + } + if cr.advregen().bit_is_clear() { + panic!("Cannot start calibration when the ADC voltage regulator is disabled"); + } + } + + /// Configuration process prior to enabling the ADC + /// + /// Note: the ADC must be disabled + fn preconfigure(&mut self) { + self.configure_channels_dif_mode(); + } + + /// Sets channels to single ended mode + fn configure_channels_dif_mode(&mut self) { + self.rb.difsel.reset(); + } + + /// Configuration process immediately after enabling the ADC + fn configure(&mut self) { + // Single conversion mode, Software trigger + // Refer to RM0433 Rev 7 - Chapters 25.4.15, 25.4.19 + self.rb.cfgr.modify(|_, w| + w.cont().clear_bit() + .exten().disabled() + .discen().set_bit() + ); + + // Enables boost mode for highest possible clock frequency + // + // Refer to RM0433 Rev 7 - Chapter 25.4.3 + #[cfg(not(feature = "revision_v"))] + self.rb.cr.modify(|_, w| w.boost().set_bit()); + #[cfg(feature = "revision_v")] + self.rb.cr.modify(|_, w| { + if self.clock.raw() <= 6_250_000 { + w.boost().lt6_25() + } else if self.clock.raw() <= 12_500_000 { + w.boost().lt12_5() + } else if self.clock.raw() <= 25_000_000 { + w.boost().lt25() + } else { + w.boost().lt50() + } + }); + } + + /// Enable ADC + pub fn enable(mut self) -> Adc<$ADC, Enabled> { + // Refer to RM0433 Rev 7 - Chapter 25.4.9 + self.rb.isr.modify(|_, w| w.adrdy().set_bit()); + self.rb.cr.modify(|_, w| w.aden().set_bit()); + while self.rb.isr.read().adrdy().bit_is_clear() {} + self.rb.isr.modify(|_, w| w.adrdy().set_bit()); + + self.configure(); + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + lshift: self.lshift, + clock: self.clock, + current_channel: None, + _enabled: PhantomData, + } + } + } + + impl Adc<$ADC, Enabled> { + fn stop_regular_conversion(&mut self) { + self.rb.cr.modify(|_, w| w.adstp().set_bit()); + while self.rb.cr.read().adstp().bit_is_set() {} + } + + fn stop_injected_conversion(&mut self) { + self.rb.cr.modify(|_, w| w.jadstp().set_bit()); + while self.rb.cr.read().jadstp().bit_is_set() {} + } + + fn set_chan_smp(&mut self, chan: u8) { + let t = self.get_sample_time().into(); + if chan <= 9 { + self.rb.smpr1.modify(|_, w| match chan { + 0 => w.smp0().bits(t), + 1 => w.smp1().bits(t), + 2 => w.smp2().bits(t), + 3 => w.smp3().bits(t), + 4 => w.smp4().bits(t), + 5 => w.smp5().bits(t), + 6 => w.smp6().bits(t), + 7 => w.smp7().bits(t), + 8 => w.smp8().bits(t), + 9 => w.smp9().bits(t), + _ => unreachable!(), + }) + } else { + self.rb.smpr2.modify(|_, w| match chan { + 10 => w.smp10().bits(t), + 11 => w.smp11().bits(t), + 12 => w.smp12().bits(t), + 13 => w.smp13().bits(t), + 14 => w.smp14().bits(t), + 15 => w.smp15().bits(t), + 16 => w.smp16().bits(t), + 17 => w.smp17().bits(t), + 18 => w.smp18().bits(t), + 19 => w.smp19().bits(t), + _ => unreachable!(), + }) + } + } + + // This method starts a conversion sequence on the given channel + fn start_conversion_common(&mut self, chan: u8) { + self.check_conversion_conditions(); + + // Set LSHIFT[3:0] + self.rb.cfgr2.modify(|_, w| w.lshift().bits(self.get_lshift().value())); + + // Select channel (with preselection, refer to RM0433 Rev 7 - Chapter 25.4.12) + self.rb.pcsel.modify(|r, w| unsafe { w.pcsel().bits(r.pcsel().bits() | (1 << chan)) }); + self.set_chan_smp(chan); + self.rb.sqr1.modify(|_, w| unsafe { + w.sq1().bits(chan) + .l().bits(0) + }); + self.current_channel = Some(chan); + + // Perform conversion + self.rb.cr.modify(|_, w| w.adstart().set_bit()); + } + + /// Start conversion + /// + /// This method starts a conversion sequence on the given pin. + /// The value can be then read through the `read_sample` method. + // Refer to RM0433 Rev 7 - Chapter 25.4.16 + pub fn start_conversion(&mut self, _pin: &mut PIN) + where PIN: embedded_hal_02::adc::Channel<$ADC, ID = u8>, + { + let chan = PIN::channel(); + assert!(chan <= 19); + + // Set resolution + self.rb.cfgr.modify(|_, w| unsafe { w.res().bits(self.get_resolution().into()) }); + // Set discontinuous mode + self.rb.cfgr.modify(|_, w| w.cont().clear_bit().discen().set_bit()); + + self.start_conversion_common(chan); + } + + /// Start conversion in DMA mode + /// + /// This method starts a conversion sequence with DMA + /// enabled. The DMA mode selected depends on the [`AdcDmaMode`] specified. + pub fn start_conversion_dma(&mut self, _pin: &mut PIN, mode: AdcDmaMode) + where PIN: embedded_hal_02::adc::Channel<$ADC, ID = u8>, + { + let chan = PIN::channel(); + assert!(chan <= 19); + + // Set resolution + self.rb.cfgr.modify(|_, w| unsafe { w.res().bits(self.get_resolution().into()) }); + + + self.rb.cfgr.modify(|_, w| w.dmngt().bits(match mode { + AdcDmaMode::OneShot => 0b01, + AdcDmaMode::Circular => 0b11, + })); + + // Set continuous mode + self.rb.cfgr.modify(|_, w| w.cont().set_bit().discen().clear_bit() ); + + self.start_conversion_common(chan); + } + + + /// Read sample + /// + /// `nb::Error::WouldBlock` in case the conversion is still + /// progressing. + // Refer to RM0433 Rev 7 - Chapter 25.4.16 + pub fn read_sample(&mut self) -> nb::Result { + let chan = self.current_channel.expect("No channel was selected, use start_conversion first"); + + // Check if the conversion is finished + if self.rb.isr.read().eoc().bit_is_clear() { + return Err(nb::Error::WouldBlock); + } + + // Disable preselection of this channel, refer to RM0433 Rev 7 - Chapter 25.4.12 + self.rb.pcsel.modify(|r, w| unsafe { w.pcsel().bits(r.pcsel().bits() & !(1 << chan)) }); + self.current_channel = None; + + // Retrieve result + let result = self.rb.dr.read().bits(); + nb::Result::Ok(result) + } + + fn check_conversion_conditions(&self) { + let cr = self.rb.cr.read(); + // Ensure that no conversions are ongoing + if cr.adstart().bit_is_set() { + panic!("Cannot start conversion because a regular conversion is ongoing"); + } + if cr.jadstart().bit_is_set() { + panic!("Cannot start conversion because an injected conversion is ongoing"); + } + // Ensure that the ADC is enabled + if cr.aden().bit_is_clear() { + panic!("Cannot start conversion because ADC is currently disabled"); + } + if cr.addis().bit_is_set() { + panic!("Cannot start conversion because there is a pending request to disable the ADC"); + } + } + + /// Disable ADC + pub fn disable(mut self) -> Adc<$ADC, Disabled> { + let cr = self.rb.cr.read(); + // Refer to RM0433 Rev 7 - Chapter 25.4.9 + if cr.adstart().bit_is_set() { + self.stop_regular_conversion(); + } + if cr.jadstart().bit_is_set() { + self.stop_injected_conversion(); + } + + self.rb.cr.modify(|_, w| w.addis().set_bit()); + while self.rb.cr.read().aden().bit_is_set() {} + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + lshift: self.lshift, + clock: self.clock, + current_channel: None, + _enabled: PhantomData, + } + } + } + + impl Adc<$ADC, ED> { + /// Save current ADC config + pub fn save_cfg(&mut self) -> StoredConfig { + StoredConfig(self.get_sample_time(), self.get_resolution(), self.get_lshift()) + } + + /// Restore saved ADC config + pub fn restore_cfg(&mut self, cfg: StoredConfig) { + self.set_sample_time(cfg.0); + self.set_resolution(cfg.1); + self.set_lshift(cfg.2); + } + + /// Reset the ADC config to default, return existing config + pub fn default_cfg(&mut self) -> StoredConfig { + let cfg = self.save_cfg(); + self.set_sample_time(AdcSampleTime::default()); + self.set_resolution(Resolution::SixteenBit); + self.set_lshift(AdcLshift::default()); + cfg + } + + /// The current ADC clock frequency. Defined as f_ADC in device datasheets + /// + /// The value returned by this method will always be equal or + /// lower than the `f_adc` passed to [`init`](#method.init) + pub fn clock_frequency(&self) -> Hertz { + self.clock + } + + /// The current ADC sampling frequency. This is the reciprocal of Tconv + pub fn sampling_frequency(&self) -> Hertz { + let sample_cycles_x2 = self.sample_time.clock_cycles_x2(); + + // TODO: Exception for RM0468 ADC3 + // let sar_cycles_x2 = match self.resolution { + // Resolution::SixBit => 13, // 6.5 + // Resolution::EightBit => 17, // 8.5 + // Resolution::TenBit => 21, // 10.5 + // _ => 25, // 12.5 + // }; + + let sar_cycles_x2 = match self.resolution { + Resolution::EightBit => 9, // 4.5 + Resolution::TenBit => 11, // 5.5 + Resolution::TwelveBit => 13, // 6.5 + Resolution::FourteenBit => 15, // 7.5 + _ => 17, // 8.5 + }; + + let cycles = (sample_cycles_x2 + sar_cycles_x2) / 2; + self.clock / cycles + } + + /// Get ADC samping time + pub fn get_sample_time(&self) -> AdcSampleTime { + self.sample_time + } + + /// Get ADC sampling resolution + pub fn get_resolution(&self) -> Resolution { + self.resolution + } + + /// Get ADC lshift value + pub fn get_lshift(&self) -> AdcLshift { + self.lshift + } + + /// Set ADC sampling time + /// + /// Options can be found in [AdcSampleTime]. + pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) { + self.sample_time = t_samp; + } + + /// Set ADC sampling resolution + pub fn set_resolution(&mut self, res: Resolution) { + self.resolution = res; + } + + /// Set ADC lshift + /// + /// LSHIFT\[3:0\] must be in range of 0..=15 + pub fn set_lshift(&mut self, lshift: AdcLshift) { + self.lshift = lshift; + } + + /// Returns the largest possible sample value for the current ADC configuration + /// + /// Using this value as the denominator when calculating + /// transfer functions results in a gain error, and thus should + /// be avoided. Use the [slope](#method.slope) method instead. + #[deprecated(since = "0.12.0", note = "See the slope() method instead")] + pub fn max_sample(&self) -> u32 { + ((1 << self.get_resolution().number_of_bits() as u32) - 1) << self.get_lshift().value() as u32 + } + + /// Returns the slope for the current ADC configuration. 1 LSB = Vref / slope + /// + /// This value can be used in calcuations involving the transfer function of + /// the ADC. For example, to calculate an estimate for the + /// applied voltage of an ADC channel referenced to voltage + /// `vref` + /// + /// ``` + /// let v = adc.read(&ch).unwrap() as f32 * vref / adc.slope() as f32; + /// ``` + pub fn slope(&self) -> u32 { + 1 << (self.get_resolution().number_of_bits() as u32 + self.get_lshift().value() as u32) + } + + + /// Returns the offset calibration value for single ended channel + pub fn read_offset_calibration_value(&self) -> AdcCalOffset { + AdcCalOffset(self.rb.calfact.read().calfact_s().bits()) + } + + /// Returns the linear calibration values stored in an array in the following order: + /// LINCALRDYW1 -> result\[0\] + /// ... + /// LINCALRDYW6 -> result\[5\] + pub fn read_linear_calibration_values(&mut self) -> AdcCalLinear { + // Refer to RM0433 Rev 7 - Chapter 25.4.8 + self.check_linear_read_conditions(); + + // Read 1st block of linear correction + self.rb.cr.modify(|_, w| w.lincalrdyw1().clear_bit()); + while self.rb.cr.read().lincalrdyw1().bit_is_set() {} + let res_1 = self.rb.calfact2.read().lincalfact().bits(); + + // Read 2nd block of linear correction + self.rb.cr.modify(|_, w| w.lincalrdyw2().clear_bit()); + while self.rb.cr.read().lincalrdyw2().bit_is_set() {} + let res_2 = self.rb.calfact2.read().lincalfact().bits(); + + // Read 3rd block of linear correction + self.rb.cr.modify(|_, w| w.lincalrdyw3().clear_bit()); + while self.rb.cr.read().lincalrdyw3().bit_is_set() {} + let res_3 = self.rb.calfact2.read().lincalfact().bits(); + + // Read 4th block of linear correction + self.rb.cr.modify(|_, w| w.lincalrdyw4().clear_bit()); + while self.rb.cr.read().lincalrdyw4().bit_is_set() {} + let res_4 = self.rb.calfact2.read().lincalfact().bits(); + + // Read 5th block of linear correction + self.rb.cr.modify(|_, w| w.lincalrdyw5().clear_bit()); + while self.rb.cr.read().lincalrdyw5().bit_is_set() {} + let res_5 = self.rb.calfact2.read().lincalfact().bits(); + + // Read 6th block of linear correction + self.rb.cr.modify(|_, w| w.lincalrdyw6().clear_bit()); + while self.rb.cr.read().lincalrdyw6().bit_is_set() {} + let res_6 = self.rb.calfact2.read().lincalfact().bits(); + + AdcCalLinear([res_1, res_2, res_3, res_4, res_5, res_6]) + } + + fn check_linear_read_conditions(&self) { + let cr = self.rb.cr.read(); + // Ensure the ADC is enabled and is not in deeppowerdown-mode + if cr.deeppwd().bit_is_set() { + panic!("Cannot read linear calibration value when the ADC is in deeppowerdown-mode"); + } + if cr.advregen().bit_is_clear() { + panic!("Cannot read linear calibration value when the voltage regulator is disabled"); + } + if cr.aden().bit_is_clear() { + panic!("Cannot read linear calibration value when the ADC is disabled"); + } + } + + /// Returns a reference to the inner peripheral + pub fn inner(&self) -> &$ADC { + &self.rb + } + + /// Returns a mutable reference to the inner peripheral + pub fn inner_mut(&mut self) -> &mut $ADC { + &mut self.rb + } + } + + impl embedded_hal_02::adc::OneShot<$ADC, WORD, PIN> for Adc<$ADC, Enabled> + where + WORD: From, + PIN: embedded_hal_02::adc::Channel<$ADC, ID = u8>, + { + type Error = (); + + fn read(&mut self, pin: &mut PIN) -> nb::Result { + self.start_conversion(pin); + let res = block!(self.read_sample()).unwrap(); + Ok(res.into()) + } + } + )+ + } +} + +adc_hal!( + ADC1, + ADC12_COMMON: (adc1, Adc12, ldordy), + ADC2, + ADC12_COMMON: (adc2, Adc12, ldordy), +); + +#[cfg(any(feature = "rm0433", feature = "rm0399"))] +adc_hal!(ADC3, ADC3_COMMON: (adc3, Adc3, ldordy)); +#[cfg(feature = "rm0468")] +adc_hal!(ADC3, ADC3_COMMON: (adc3, Adc3)); diff --git a/src/lib.rs b/src/lib.rs index cc3c317..ed4356c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ pub mod prelude; #[macro_use] mod macros; +#[cfg(feature = "device-selected")] +pub mod adc; + #[cfg(feature = "device-selected")] pub mod pwr; From 6a453118a8b455d747e687d6a38dc46361418bd1 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 27 Aug 2025 19:08:03 +0200 Subject: [PATCH 02/12] Compiles... --- Cargo.toml | 3 + src/adc.rs | 1141 ------------------------------------------------ src/adc/h5.rs | 112 +++++ src/adc/mod.rs | 838 +++++++++++++++++++++++++++++++++++ 4 files changed, 953 insertions(+), 1141 deletions(-) delete mode 100644 src/adc.rs create mode 100644 src/adc/h5.rs create mode 100644 src/adc/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 92f18eb..9f04f25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,11 +73,14 @@ stm32h5 = { package = "stm32h5", version = "0.16.0" } fugit = "0.3.7" embedded-dma = "0.2" embedded-hal = "1.0.0" +embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } + defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true} stm32-usbd = "0.8.0" +nb = "1.1.0" [dev-dependencies] log = { version = "0.4.20"} diff --git a/src/adc.rs b/src/adc.rs deleted file mode 100644 index a891014..0000000 --- a/src/adc.rs +++ /dev/null @@ -1,1141 +0,0 @@ -//! Analog to Digital Converter (ADC) -//! -//! ADC1 and ADC2 share a reset line. To initialise both of them, use the -//! [`adc12`] method. -//! -//! # Examples -//! -//! - [Reading a voltage using ADC1](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc.rs) -//! - [Reading a temperature using ADC3](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/temperature.rs) -//! - [Using ADC1 and ADC2 together](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc12.rs) -//! - [Using ADC1 and ADC2 in parallel](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc12_parallel.rs) -//! - [Using ADC1 through DMA](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/adc_dma.rs) - -use embedded_hal_02::blocking::delay::DelayUs; - -use core::convert::Infallible; -use core::marker::PhantomData; - -use nb::block; - -use crate::stm32::ADC12_COMMON; -use crate::stm32::{ADC1, ADC2}; -#[cfg(not(feature = "rm0455"))] -use crate::stm32::{ADC3, ADC3_COMMON}; - -#[cfg(feature = "rm0455")] -use crate::stm32::adc12_common::ccr::PRESC_A; -#[cfg(not(feature = "rm0455"))] -use crate::stm32::adc3_common::ccr::PRESC_A; - -use crate::gpio::{self, Analog}; -use crate::pwr::{current_vos, VoltageScale}; -use crate::rcc::rec::AdcClkSelGetter; -use crate::rcc::{rec, CoreClocks, ResetEnable}; -use crate::time::Hertz; - -#[cfg(any(feature = "rm0433", feature = "rm0399"))] -pub type Resolution = crate::stm32::adc3::cfgr::RES_A; -#[cfg(any(feature = "rm0455", feature = "rm0468"))] -pub type Resolution = crate::stm32::adc1::cfgr::RES_A; - -trait NumberOfBits { - fn number_of_bits(&self) -> u32; -} - -impl NumberOfBits for Resolution { - fn number_of_bits(&self) -> u32 { - match *self { - Resolution::EightBit => 8, - Resolution::TenBit => 10, - Resolution::TwelveBit => 12, - Resolution::FourteenBit => 14, - _ => 16, - } - } -} - -/// Enabled ADC (type state) -pub struct Enabled; -/// Disabled ADC (type state) -pub struct Disabled; - -pub trait ED {} -impl ED for Enabled {} -impl ED for Disabled {} - -pub struct Adc { - rb: ADC, - sample_time: AdcSampleTime, - resolution: Resolution, - lshift: AdcLshift, - clock: Hertz, - current_channel: Option, - _enabled: PhantomData, -} - -/// ADC DMA modes -/// -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AdcDmaMode { - OneShot, - Circular, -} - -/// ADC sampling time -/// -/// Options for the sampling time, each is T + 0.5 ADC clock cycles. -// -// Refer to RM0433 Rev 7 - Chapter 25.4.13 -#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(non_camel_case_types)] -pub enum AdcSampleTime { - /// 1.5 cycles sampling time - T_1, - /// 2.5 cycles sampling time - T_2, - /// 8.5 cycles sampling time - T_8, - /// 16.5 cycles sampling time - T_16, - /// 32.5 cycles sampling time - #[default] - T_32, - /// 64.5 cycles sampling time - T_64, - /// 387.5 cycles sampling time - T_387, - /// 810.5 cycles sampling time - T_810, -} - -impl AdcSampleTime { - /// Returns the number of half clock cycles represented by this sampling time - fn clock_cycles_x2(&self) -> u32 { - let x = match self { - AdcSampleTime::T_1 => 1, - AdcSampleTime::T_2 => 2, - AdcSampleTime::T_8 => 8, - AdcSampleTime::T_16 => 16, - AdcSampleTime::T_32 => 32, - AdcSampleTime::T_64 => 64, - AdcSampleTime::T_387 => 387, - AdcSampleTime::T_810 => 810, - }; - (2 * x) + 1 - } -} - -// Refer to RM0433 Rev 7 - Chapter 25.4.13 -impl From for u8 { - fn from(val: AdcSampleTime) -> u8 { - match val { - AdcSampleTime::T_1 => 0b000, - AdcSampleTime::T_2 => 0b001, - AdcSampleTime::T_8 => 0b010, - AdcSampleTime::T_16 => 0b011, - AdcSampleTime::T_32 => 0b100, - AdcSampleTime::T_64 => 0b101, - AdcSampleTime::T_387 => 0b110, - AdcSampleTime::T_810 => 0b111, - } - } -} - -/// ADC LSHIFT\[3:0\] of the converted value -/// -/// Only values in range of 0..=15 are allowed. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct AdcLshift(u8); - -impl AdcLshift { - pub fn new(lshift: u8) -> Self { - if lshift > 15 { - panic!("LSHIFT[3:0] must be in range of 0..=15"); - } - - AdcLshift(lshift) - } - - pub fn value(self) -> u8 { - self.0 - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct AdcCalOffset(u16); - -impl AdcCalOffset { - pub fn value(self) -> u16 { - self.0 - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct AdcCalLinear([u32; 6]); - -impl AdcCalLinear { - pub fn value(self) -> [u32; 6] { - self.0 - } -} - -macro_rules! adc_pins { - ($ADC:ident, $($input:ty => $chan:expr),+ $(,)*) => { - $( - impl embedded_hal_02::adc::Channel<$ADC> for $input { - type ID = u8; - - fn channel() -> u8 { - $chan - } - } - )+ - }; -} - -macro_rules! adc_internal { - ([$INT_ADC:ident, $INT_ADC_COMMON:ident]; $($input:ty => ($chan:expr, $en:ident)),+ $(,)*) => { - $( - impl $input { - pub fn new() -> Self { - Self {} - } - - /// Enables the internal voltage/sensor - /// ADC must be disabled. - pub fn enable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { - - let common = unsafe { &*$INT_ADC_COMMON::ptr() }; - - common.ccr.modify(|_, w| w.$en().enabled()); - } - /// Disables the internal voltage/sdissor - /// ADC must be disabled. - pub fn disable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { - - let common = unsafe { &*$INT_ADC_COMMON::ptr() }; - - common.ccr.modify(|_, w| w.$en().disabled()); - } - } - - adc_pins!($INT_ADC, $input => $chan); - )+ - }; -} - -/// Vref internal signal -#[derive(Default)] -pub struct Vrefint; -/// Vbat internal signal -#[derive(Default)] -pub struct Vbat; -/// Internal temperature sensor -#[derive(Default)] -pub struct Temperature; - -// Not implementing Pxy_C adc pins -// Just implmenting INPx pins (INNx defaulting to V_ref-) -// -// Refer to DS12110 Rev 7 - Chapter 5 (Table 9) -adc_pins!(ADC1, - // 0, 1 are Pxy_C pins - gpio::PF11 => 2, - gpio::PA6 => 3, - gpio::PC4 => 4, - gpio::PB1 => 5, - gpio::PF12 => 6, - gpio::PA7 => 7, - gpio::PC5 => 8, - gpio::PB0 => 9, - gpio::PC0 => 10, - gpio::PC1 => 11, - gpio::PC2 => 12, - gpio::PC3 => 13, - gpio::PA2 => 14, - gpio::PA3 => 15, - gpio::PA0 => 16, - gpio::PA1 => 17, - gpio::PA4 => 18, - gpio::PA5 => 19, -); - -adc_pins!(ADC2, - // 0, 1 are Pxy_C pins - gpio::PF13 => 2, - gpio::PA6 => 3, - gpio::PC4 => 4, - gpio::PB1 => 5, - gpio::PF14 => 6, - gpio::PA7 => 7, - gpio::PC5 => 8, - gpio::PB0 => 9, - gpio::PC0 => 10, - gpio::PC1 => 11, - gpio::PC2 => 12, - gpio::PC3 => 13, - gpio::PA2 => 14, - gpio::PA3 => 15, - // 16, 17 are dac_outX - gpio::PA4 => 18, - gpio::PA5 => 19, -); - -#[cfg(feature = "rm0455")] -adc_internal!( - [ADC2, ADC12_COMMON]; - - Vbat => (14, vbaten), - Temperature => (18, vsenseen), - Vrefint => (19, vrefen) -); - -// -------- ADC3 -------- - -#[cfg(any(feature = "rm0433", feature = "rm0399"))] -adc_pins!(ADC3, - // 0, 1 are Pxy_C pins - gpio::PF9 => 2, - gpio::PF7 => 3, - gpio::PF5 => 4, - gpio::PF3 => 5, - gpio::PF10 => 6, - gpio::PF8 => 7, - gpio::PF6 => 8, - gpio::PF4 => 9, - gpio::PC0 => 10, - gpio::PC1 => 11, - gpio::PC2 => 12, - gpio::PH2 => 13, - gpio::PH3 => 14, - gpio::PH4 => 15, - gpio::PH5 => 16, -); -#[cfg(any(feature = "rm0433", feature = "rm0399"))] -adc_internal!( - [ADC3, ADC3_COMMON]; - - Vbat => (17, vbaten), - Temperature => (18, vsenseen), - Vrefint => (19, vrefen) -); - -// ADC 3 not present on RM0455 parts - -#[cfg(feature = "rm0468")] -adc_pins!(ADC3, - // 0, 1 are Pxy_C pins - gpio::PF9 => 2, - gpio::PF7 => 3, - gpio::PF5 => 4, - gpio::PF3 => 5, - gpio::PF10 => 6, - gpio::PF8 => 7, - gpio::PF6 => 8, - gpio::PF4 => 9, - gpio::PC0 => 10, - gpio::PC1 => 11, - gpio::PC2 => 12, - gpio::PH2 => 13, - gpio::PH3 => 14, - gpio::PH4 => 15, - // Although ADC3_INP16 appears in device datasheets (on PH5), RM0468 Rev 7 - // Figure 231 does not show ADC3_INP16 -); -#[cfg(feature = "rm0468")] -adc_internal!( - [ADC3, ADC3_COMMON]; - - Vbat => (16, vbaten), - Temperature => (17, vsenseen), - Vrefint => (18, vrefen) -); - -pub trait AdcExt: Sized { - type Rec: ResetEnable; - - fn adc( - self, - f_adc: impl Into, - delay: &mut impl DelayUs, - prec: Self::Rec, - clocks: &CoreClocks, - ) -> Adc; -} - -/// Stored ADC config can be restored using the `Adc::restore_cfg` method -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct StoredConfig(AdcSampleTime, Resolution, AdcLshift); - -#[cfg(feature = "defmt")] -impl defmt::Format for StoredConfig { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!( - fmt, - "StoredConfig({:?}, {:?}, {:?})", - self.0, - defmt::Debug2Format(&self.1), - self.2 - ) - } -} - -/// Returns the frequency of the current adc_ker_ck -/// -/// # Panics -/// -/// Panics if the kernel clock is not running -fn kernel_clk_unwrap( - prec: &impl AdcClkSelGetter, - clocks: &CoreClocks, -) -> Hertz { - match prec.get_kernel_clk_mux() { - Some(rec::AdcClkSel::Pll2P) => { - clocks.pll2_p_ck().expect("ADC: PLL2_P must be enabled") - } - Some(rec::AdcClkSel::Pll3R) => { - clocks.pll3_r_ck().expect("ADC: PLL3_R must be enabled") - } - Some(rec::AdcClkSel::Per) => { - clocks.per_ck().expect("ADC: PER clock must be enabled") - } - _ => unreachable!(), - } -} - -// ADC12 is a unique case where a single reset line is used to control two -// peripherals that have separate peripheral definitions in the SVD. - -/// Initialise ADC12 together -/// -/// Sets all configurable parameters to one-shot defaults, -/// performs a boot-time calibration. -pub fn adc12( - adc1: ADC1, - adc2: ADC2, - f_adc: impl Into, - delay: &mut impl DelayUs, - prec: rec::Adc12, - clocks: &CoreClocks, -) -> (Adc, Adc) { - // Consume ADC register block, produce ADC1/2 with default settings - let mut adc1 = Adc::::default_from_rb(adc1); - let mut adc2 = Adc::::default_from_rb(adc2); - - // Check adc_ker_ck_input - kernel_clk_unwrap(&prec, clocks); - - // Enable AHB clock - let prec = prec.enable(); - - // Power Down - adc1.power_down(); - adc2.power_down(); - - // Reset peripheral - let prec = prec.reset(); - - // Power Up, Preconfigure and Calibrate - adc1.power_up(delay); - adc2.power_up(delay); - let f_adc = adc1.configure_clock(f_adc.into(), prec, clocks); // ADC12_COMMON - adc2.clock = f_adc; - adc1.preconfigure(); - adc2.preconfigure(); - adc1.calibrate(); - adc2.calibrate(); - - (adc1, adc2) -} - -/// Free both ADC1 and ADC2 along with PREC. -/// -/// Since ADC1 and ADC2 are controlled together, they are freed together. -pub fn free_adc12( - adc1: Adc, - adc2: Adc, -) -> (ADC1, ADC2, rec::Adc12) { - ( - adc1.rb, - adc2.rb, - rec::Adc12 { - _marker: PhantomData, - }, - ) -} - -#[cfg(not(feature = "rm0455"))] -/// Freeing both the peripheral and PREC is possible for ADC3 -impl Adc { - /// Releases the ADC peripheral - pub fn free(self) -> (ADC3, rec::Adc3) { - ( - self.rb, - rec::Adc3 { - _marker: PhantomData, - }, - ) - } -} - -#[allow(unused_macros)] -macro_rules! adc_hal { - ($( - $ADC:ident, $ADC_COMMON:ident: ( - $adcX: ident, - $Rec:ident - $(, $ldordy:ident )* - ) - ),+ $(,)*) => { - $( - impl AdcExt<$ADC> for $ADC { - type Rec = rec::$Rec; - - fn adc(self, - f_adc: impl Into, - delay: &mut impl DelayUs, - prec: rec::$Rec, - clocks: &CoreClocks) -> Adc<$ADC, Disabled> - { - Adc::$adcX(self, f_adc, delay, prec, clocks) - } - } - - impl Adc<$ADC, Disabled> { - /// Initialise ADC - /// - /// Sets all configurable parameters to one-shot defaults, - /// performs a boot-time calibration. - pub fn $adcX(adc: $ADC, f_adc: impl Into, delay: &mut impl DelayUs, - prec: rec::$Rec, clocks: &CoreClocks - ) -> Self { - // Consume ADC register block, produce Self with default - // settings - let mut adc = Self::default_from_rb(adc); - - // Enable AHB clock - let prec = prec.enable(); - - // Power Down - adc.power_down(); - - // Reset peripheral - let prec = prec.reset(); - - // Power Up, Preconfigure and Calibrate - adc.power_up(delay); - adc.configure_clock(f_adc.into(), prec, clocks); - adc.preconfigure(); - adc.calibrate(); - - adc - } - /// Creates ADC with default settings - fn default_from_rb(rb: $ADC) -> Self { - Self { - rb, - sample_time: AdcSampleTime::default(), - resolution: Resolution::SixteenBit, - lshift: AdcLshift::default(), - clock: Hertz::from_raw(0), - current_channel: None, - _enabled: PhantomData, - } - } - /// Sets the clock configuration for this ADC. This is common - /// between ADC1 and ADC2, so the prec block is used to ensure - /// this method can only be called on one of the ADCs (or both, - /// using the [adc12](#method.adc12) method). - /// - /// Only `CKMODE[1:0]` = 0 is supported - fn configure_clock(&mut self, f_adc: Hertz, prec: rec::$Rec, clocks: &CoreClocks) -> Hertz { - let ker_ck = kernel_clk_unwrap(&prec, clocks); - - let max_ker_ck = match current_vos() { - // See RM0468 Rev 3 Table 56. - #[cfg(feature = "rm0468")] - VoltageScale::Scale0 | VoltageScale::Scale1 => 160_000_000, - #[cfg(feature = "rm0468")] - VoltageScale::Scale2 => 60_000_000, - #[cfg(feature = "rm0468")] - VoltageScale::Scale3 => 40_000_000, - - // See RM0433 Rev 7 Table 59. - #[cfg(not(feature = "rm0468"))] - VoltageScale::Scale0 | VoltageScale::Scale1 => 80_000_000, - #[cfg(not(feature = "rm0468"))] - VoltageScale::Scale2 | VoltageScale::Scale3 => 40_000_000 - }; - assert!(ker_ck.raw() <= max_ker_ck, - "Kernel clock violates maximum frequency defined in Reference Manual. \ - Can result in erroneous ADC readings"); - - let f_adc = self.configure_clock_unchecked(f_adc, prec, clocks); - - // Maximum ADC clock speed. With BOOST = 0 there is a no - // minimum frequency given in part datasheets - assert!(f_adc.raw() <= 50_000_000); - - f_adc - } - - /// No clock checks - fn configure_clock_unchecked(&mut self, f_adc: Hertz, prec: rec::$Rec, clocks: &CoreClocks) -> Hertz { - let ker_ck = kernel_clk_unwrap(&prec, clocks); - - // Target mux output. See RM0433 Rev 7 - Figure 136. - #[cfg(feature = "revision_v")] - let f_target = f_adc.raw() * 2; - - #[cfg(not(feature = "revision_v"))] - let f_target = f_adc.raw(); - - let (divider, presc) = match ker_ck.raw().div_ceil(f_target) { - 1 => (1, PRESC_A::Div1), - 2 => (2, PRESC_A::Div2), - 3..=4 => (4, PRESC_A::Div4), - 5..=6 => (6, PRESC_A::Div6), - 7..=8 => (8, PRESC_A::Div8), - 9..=10 => (10, PRESC_A::Div10), - 11..=12 => (12, PRESC_A::Div12), - 13..=16 => (16, PRESC_A::Div16), - 17..=32 => (32, PRESC_A::Div32), - 33..=64 => (64, PRESC_A::Div64), - 65..=128 => (128, PRESC_A::Div128), - 129..=256 => (256, PRESC_A::Div256), - _ => panic!("Selecting the ADC clock required a prescaler > 256, \ - which is not possible in hardware. Either increase the ADC \ - clock frequency or decrease the kernel clock frequency"), - }; - unsafe { &*$ADC_COMMON::ptr() }.ccr.modify(|_, w| w.presc().variant(presc)); - - // Calculate actual value. See RM0433 Rev 7 - Figure 136. - #[cfg(feature = "revision_v")] - let f_adc = Hertz::from_raw(ker_ck.raw() / (divider * 2)); - - // Calculate actual value Revison Y. See RM0433 Rev 7 - Figure 137. - #[cfg(not(feature = "revision_v"))] - let f_adc = Hertz::from_raw(ker_ck.raw() / divider); - - self.clock = f_adc; - f_adc - } - - /// Disables Deeppowerdown-mode and enables voltage regulator - /// - /// Note: After power-up, a [`calibration`](#method.calibrate) shall be run - pub fn power_up(&mut self, delay: &mut impl DelayUs) { - // Refer to RM0433 Rev 7 - Chapter 25.4.6 - self.rb.cr.modify(|_, w| - w.deeppwd().clear_bit() - .advregen().set_bit() - ); - delay.delay_us(10_u8); - - // check LDORDY bit if present - $( - #[cfg(feature = "revision_v")] - while { - let $ldordy = self.rb.isr.read().bits() & 0x1000; - $ldordy == 0 - }{} - )* - } - - /// Enables Deeppowerdown-mode and disables voltage regulator - /// - /// Note: This resets the [`calibration`](#method.calibrate) of the ADC - pub fn power_down(&mut self) { - // Refer to RM0433 Rev 7 - Chapter 25.4.6 - self.rb.cr.modify(|_, w| - w.deeppwd().set_bit() - .advregen().clear_bit() - ); - } - - /// Calibrates the ADC in single channel mode - /// - /// Note: The ADC must be disabled - pub fn calibrate(&mut self) { - // Refer to RM0433 Rev 7 - Chapter 25.4.8 - self.check_calibration_conditions(); - - // single channel (INNx equals to V_ref-) - self.rb.cr.modify(|_, w| - w.adcaldif().clear_bit() - .adcallin().set_bit() - ); - // calibrate - self.rb.cr.modify(|_, w| w.adcal().set_bit()); - while self.rb.cr.read().adcal().bit_is_set() {} - } - - fn check_calibration_conditions(&self) { - let cr = self.rb.cr.read(); - if cr.aden().bit_is_set() { - panic!("Cannot start calibration when the ADC is enabled"); - } - if cr.deeppwd().bit_is_set() { - panic!("Cannot start calibration when the ADC is in deeppowerdown-mode"); - } - if cr.advregen().bit_is_clear() { - panic!("Cannot start calibration when the ADC voltage regulator is disabled"); - } - } - - /// Configuration process prior to enabling the ADC - /// - /// Note: the ADC must be disabled - fn preconfigure(&mut self) { - self.configure_channels_dif_mode(); - } - - /// Sets channels to single ended mode - fn configure_channels_dif_mode(&mut self) { - self.rb.difsel.reset(); - } - - /// Configuration process immediately after enabling the ADC - fn configure(&mut self) { - // Single conversion mode, Software trigger - // Refer to RM0433 Rev 7 - Chapters 25.4.15, 25.4.19 - self.rb.cfgr.modify(|_, w| - w.cont().clear_bit() - .exten().disabled() - .discen().set_bit() - ); - - // Enables boost mode for highest possible clock frequency - // - // Refer to RM0433 Rev 7 - Chapter 25.4.3 - #[cfg(not(feature = "revision_v"))] - self.rb.cr.modify(|_, w| w.boost().set_bit()); - #[cfg(feature = "revision_v")] - self.rb.cr.modify(|_, w| { - if self.clock.raw() <= 6_250_000 { - w.boost().lt6_25() - } else if self.clock.raw() <= 12_500_000 { - w.boost().lt12_5() - } else if self.clock.raw() <= 25_000_000 { - w.boost().lt25() - } else { - w.boost().lt50() - } - }); - } - - /// Enable ADC - pub fn enable(mut self) -> Adc<$ADC, Enabled> { - // Refer to RM0433 Rev 7 - Chapter 25.4.9 - self.rb.isr.modify(|_, w| w.adrdy().set_bit()); - self.rb.cr.modify(|_, w| w.aden().set_bit()); - while self.rb.isr.read().adrdy().bit_is_clear() {} - self.rb.isr.modify(|_, w| w.adrdy().set_bit()); - - self.configure(); - - Adc { - rb: self.rb, - sample_time: self.sample_time, - resolution: self.resolution, - lshift: self.lshift, - clock: self.clock, - current_channel: None, - _enabled: PhantomData, - } - } - } - - impl Adc<$ADC, Enabled> { - fn stop_regular_conversion(&mut self) { - self.rb.cr.modify(|_, w| w.adstp().set_bit()); - while self.rb.cr.read().adstp().bit_is_set() {} - } - - fn stop_injected_conversion(&mut self) { - self.rb.cr.modify(|_, w| w.jadstp().set_bit()); - while self.rb.cr.read().jadstp().bit_is_set() {} - } - - fn set_chan_smp(&mut self, chan: u8) { - let t = self.get_sample_time().into(); - if chan <= 9 { - self.rb.smpr1.modify(|_, w| match chan { - 0 => w.smp0().bits(t), - 1 => w.smp1().bits(t), - 2 => w.smp2().bits(t), - 3 => w.smp3().bits(t), - 4 => w.smp4().bits(t), - 5 => w.smp5().bits(t), - 6 => w.smp6().bits(t), - 7 => w.smp7().bits(t), - 8 => w.smp8().bits(t), - 9 => w.smp9().bits(t), - _ => unreachable!(), - }) - } else { - self.rb.smpr2.modify(|_, w| match chan { - 10 => w.smp10().bits(t), - 11 => w.smp11().bits(t), - 12 => w.smp12().bits(t), - 13 => w.smp13().bits(t), - 14 => w.smp14().bits(t), - 15 => w.smp15().bits(t), - 16 => w.smp16().bits(t), - 17 => w.smp17().bits(t), - 18 => w.smp18().bits(t), - 19 => w.smp19().bits(t), - _ => unreachable!(), - }) - } - } - - // This method starts a conversion sequence on the given channel - fn start_conversion_common(&mut self, chan: u8) { - self.check_conversion_conditions(); - - // Set LSHIFT[3:0] - self.rb.cfgr2.modify(|_, w| w.lshift().bits(self.get_lshift().value())); - - // Select channel (with preselection, refer to RM0433 Rev 7 - Chapter 25.4.12) - self.rb.pcsel.modify(|r, w| unsafe { w.pcsel().bits(r.pcsel().bits() | (1 << chan)) }); - self.set_chan_smp(chan); - self.rb.sqr1.modify(|_, w| unsafe { - w.sq1().bits(chan) - .l().bits(0) - }); - self.current_channel = Some(chan); - - // Perform conversion - self.rb.cr.modify(|_, w| w.adstart().set_bit()); - } - - /// Start conversion - /// - /// This method starts a conversion sequence on the given pin. - /// The value can be then read through the `read_sample` method. - // Refer to RM0433 Rev 7 - Chapter 25.4.16 - pub fn start_conversion(&mut self, _pin: &mut PIN) - where PIN: embedded_hal_02::adc::Channel<$ADC, ID = u8>, - { - let chan = PIN::channel(); - assert!(chan <= 19); - - // Set resolution - self.rb.cfgr.modify(|_, w| unsafe { w.res().bits(self.get_resolution().into()) }); - // Set discontinuous mode - self.rb.cfgr.modify(|_, w| w.cont().clear_bit().discen().set_bit()); - - self.start_conversion_common(chan); - } - - /// Start conversion in DMA mode - /// - /// This method starts a conversion sequence with DMA - /// enabled. The DMA mode selected depends on the [`AdcDmaMode`] specified. - pub fn start_conversion_dma(&mut self, _pin: &mut PIN, mode: AdcDmaMode) - where PIN: embedded_hal_02::adc::Channel<$ADC, ID = u8>, - { - let chan = PIN::channel(); - assert!(chan <= 19); - - // Set resolution - self.rb.cfgr.modify(|_, w| unsafe { w.res().bits(self.get_resolution().into()) }); - - - self.rb.cfgr.modify(|_, w| w.dmngt().bits(match mode { - AdcDmaMode::OneShot => 0b01, - AdcDmaMode::Circular => 0b11, - })); - - // Set continuous mode - self.rb.cfgr.modify(|_, w| w.cont().set_bit().discen().clear_bit() ); - - self.start_conversion_common(chan); - } - - - /// Read sample - /// - /// `nb::Error::WouldBlock` in case the conversion is still - /// progressing. - // Refer to RM0433 Rev 7 - Chapter 25.4.16 - pub fn read_sample(&mut self) -> nb::Result { - let chan = self.current_channel.expect("No channel was selected, use start_conversion first"); - - // Check if the conversion is finished - if self.rb.isr.read().eoc().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - - // Disable preselection of this channel, refer to RM0433 Rev 7 - Chapter 25.4.12 - self.rb.pcsel.modify(|r, w| unsafe { w.pcsel().bits(r.pcsel().bits() & !(1 << chan)) }); - self.current_channel = None; - - // Retrieve result - let result = self.rb.dr.read().bits(); - nb::Result::Ok(result) - } - - fn check_conversion_conditions(&self) { - let cr = self.rb.cr.read(); - // Ensure that no conversions are ongoing - if cr.adstart().bit_is_set() { - panic!("Cannot start conversion because a regular conversion is ongoing"); - } - if cr.jadstart().bit_is_set() { - panic!("Cannot start conversion because an injected conversion is ongoing"); - } - // Ensure that the ADC is enabled - if cr.aden().bit_is_clear() { - panic!("Cannot start conversion because ADC is currently disabled"); - } - if cr.addis().bit_is_set() { - panic!("Cannot start conversion because there is a pending request to disable the ADC"); - } - } - - /// Disable ADC - pub fn disable(mut self) -> Adc<$ADC, Disabled> { - let cr = self.rb.cr.read(); - // Refer to RM0433 Rev 7 - Chapter 25.4.9 - if cr.adstart().bit_is_set() { - self.stop_regular_conversion(); - } - if cr.jadstart().bit_is_set() { - self.stop_injected_conversion(); - } - - self.rb.cr.modify(|_, w| w.addis().set_bit()); - while self.rb.cr.read().aden().bit_is_set() {} - - Adc { - rb: self.rb, - sample_time: self.sample_time, - resolution: self.resolution, - lshift: self.lshift, - clock: self.clock, - current_channel: None, - _enabled: PhantomData, - } - } - } - - impl Adc<$ADC, ED> { - /// Save current ADC config - pub fn save_cfg(&mut self) -> StoredConfig { - StoredConfig(self.get_sample_time(), self.get_resolution(), self.get_lshift()) - } - - /// Restore saved ADC config - pub fn restore_cfg(&mut self, cfg: StoredConfig) { - self.set_sample_time(cfg.0); - self.set_resolution(cfg.1); - self.set_lshift(cfg.2); - } - - /// Reset the ADC config to default, return existing config - pub fn default_cfg(&mut self) -> StoredConfig { - let cfg = self.save_cfg(); - self.set_sample_time(AdcSampleTime::default()); - self.set_resolution(Resolution::SixteenBit); - self.set_lshift(AdcLshift::default()); - cfg - } - - /// The current ADC clock frequency. Defined as f_ADC in device datasheets - /// - /// The value returned by this method will always be equal or - /// lower than the `f_adc` passed to [`init`](#method.init) - pub fn clock_frequency(&self) -> Hertz { - self.clock - } - - /// The current ADC sampling frequency. This is the reciprocal of Tconv - pub fn sampling_frequency(&self) -> Hertz { - let sample_cycles_x2 = self.sample_time.clock_cycles_x2(); - - // TODO: Exception for RM0468 ADC3 - // let sar_cycles_x2 = match self.resolution { - // Resolution::SixBit => 13, // 6.5 - // Resolution::EightBit => 17, // 8.5 - // Resolution::TenBit => 21, // 10.5 - // _ => 25, // 12.5 - // }; - - let sar_cycles_x2 = match self.resolution { - Resolution::EightBit => 9, // 4.5 - Resolution::TenBit => 11, // 5.5 - Resolution::TwelveBit => 13, // 6.5 - Resolution::FourteenBit => 15, // 7.5 - _ => 17, // 8.5 - }; - - let cycles = (sample_cycles_x2 + sar_cycles_x2) / 2; - self.clock / cycles - } - - /// Get ADC samping time - pub fn get_sample_time(&self) -> AdcSampleTime { - self.sample_time - } - - /// Get ADC sampling resolution - pub fn get_resolution(&self) -> Resolution { - self.resolution - } - - /// Get ADC lshift value - pub fn get_lshift(&self) -> AdcLshift { - self.lshift - } - - /// Set ADC sampling time - /// - /// Options can be found in [AdcSampleTime]. - pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) { - self.sample_time = t_samp; - } - - /// Set ADC sampling resolution - pub fn set_resolution(&mut self, res: Resolution) { - self.resolution = res; - } - - /// Set ADC lshift - /// - /// LSHIFT\[3:0\] must be in range of 0..=15 - pub fn set_lshift(&mut self, lshift: AdcLshift) { - self.lshift = lshift; - } - - /// Returns the largest possible sample value for the current ADC configuration - /// - /// Using this value as the denominator when calculating - /// transfer functions results in a gain error, and thus should - /// be avoided. Use the [slope](#method.slope) method instead. - #[deprecated(since = "0.12.0", note = "See the slope() method instead")] - pub fn max_sample(&self) -> u32 { - ((1 << self.get_resolution().number_of_bits() as u32) - 1) << self.get_lshift().value() as u32 - } - - /// Returns the slope for the current ADC configuration. 1 LSB = Vref / slope - /// - /// This value can be used in calcuations involving the transfer function of - /// the ADC. For example, to calculate an estimate for the - /// applied voltage of an ADC channel referenced to voltage - /// `vref` - /// - /// ``` - /// let v = adc.read(&ch).unwrap() as f32 * vref / adc.slope() as f32; - /// ``` - pub fn slope(&self) -> u32 { - 1 << (self.get_resolution().number_of_bits() as u32 + self.get_lshift().value() as u32) - } - - - /// Returns the offset calibration value for single ended channel - pub fn read_offset_calibration_value(&self) -> AdcCalOffset { - AdcCalOffset(self.rb.calfact.read().calfact_s().bits()) - } - - /// Returns the linear calibration values stored in an array in the following order: - /// LINCALRDYW1 -> result\[0\] - /// ... - /// LINCALRDYW6 -> result\[5\] - pub fn read_linear_calibration_values(&mut self) -> AdcCalLinear { - // Refer to RM0433 Rev 7 - Chapter 25.4.8 - self.check_linear_read_conditions(); - - // Read 1st block of linear correction - self.rb.cr.modify(|_, w| w.lincalrdyw1().clear_bit()); - while self.rb.cr.read().lincalrdyw1().bit_is_set() {} - let res_1 = self.rb.calfact2.read().lincalfact().bits(); - - // Read 2nd block of linear correction - self.rb.cr.modify(|_, w| w.lincalrdyw2().clear_bit()); - while self.rb.cr.read().lincalrdyw2().bit_is_set() {} - let res_2 = self.rb.calfact2.read().lincalfact().bits(); - - // Read 3rd block of linear correction - self.rb.cr.modify(|_, w| w.lincalrdyw3().clear_bit()); - while self.rb.cr.read().lincalrdyw3().bit_is_set() {} - let res_3 = self.rb.calfact2.read().lincalfact().bits(); - - // Read 4th block of linear correction - self.rb.cr.modify(|_, w| w.lincalrdyw4().clear_bit()); - while self.rb.cr.read().lincalrdyw4().bit_is_set() {} - let res_4 = self.rb.calfact2.read().lincalfact().bits(); - - // Read 5th block of linear correction - self.rb.cr.modify(|_, w| w.lincalrdyw5().clear_bit()); - while self.rb.cr.read().lincalrdyw5().bit_is_set() {} - let res_5 = self.rb.calfact2.read().lincalfact().bits(); - - // Read 6th block of linear correction - self.rb.cr.modify(|_, w| w.lincalrdyw6().clear_bit()); - while self.rb.cr.read().lincalrdyw6().bit_is_set() {} - let res_6 = self.rb.calfact2.read().lincalfact().bits(); - - AdcCalLinear([res_1, res_2, res_3, res_4, res_5, res_6]) - } - - fn check_linear_read_conditions(&self) { - let cr = self.rb.cr.read(); - // Ensure the ADC is enabled and is not in deeppowerdown-mode - if cr.deeppwd().bit_is_set() { - panic!("Cannot read linear calibration value when the ADC is in deeppowerdown-mode"); - } - if cr.advregen().bit_is_clear() { - panic!("Cannot read linear calibration value when the voltage regulator is disabled"); - } - if cr.aden().bit_is_clear() { - panic!("Cannot read linear calibration value when the ADC is disabled"); - } - } - - /// Returns a reference to the inner peripheral - pub fn inner(&self) -> &$ADC { - &self.rb - } - - /// Returns a mutable reference to the inner peripheral - pub fn inner_mut(&mut self) -> &mut $ADC { - &mut self.rb - } - } - - impl embedded_hal_02::adc::OneShot<$ADC, WORD, PIN> for Adc<$ADC, Enabled> - where - WORD: From, - PIN: embedded_hal_02::adc::Channel<$ADC, ID = u8>, - { - type Error = (); - - fn read(&mut self, pin: &mut PIN) -> nb::Result { - self.start_conversion(pin); - let res = block!(self.read_sample()).unwrap(); - Ok(res.into()) - } - } - )+ - } -} - -adc_hal!( - ADC1, - ADC12_COMMON: (adc1, Adc12, ldordy), - ADC2, - ADC12_COMMON: (adc2, Adc12, ldordy), -); - -#[cfg(any(feature = "rm0433", feature = "rm0399"))] -adc_hal!(ADC3, ADC3_COMMON: (adc3, Adc3, ldordy)); -#[cfg(feature = "rm0468")] -adc_hal!(ADC3, ADC3_COMMON: (adc3, Adc3)); diff --git a/src/adc/h5.rs b/src/adc/h5.rs new file mode 100644 index 0000000..dee9629 --- /dev/null +++ b/src/adc/h5.rs @@ -0,0 +1,112 @@ +use super::{Adc, Disabled, Temperature, Vbat, Vddcore, Vrefint}; +use crate::gpio::{self, Analog}; +use crate::stm32::{ADC1, ADC2, ADCC}; + +macro_rules! adc_pins { + ($ADC:ident, $($input:ty => $chan:expr),+ $(,)*) => { + $( + impl embedded_hal_02::adc::Channel<$ADC> for $input { + type ID = u8; + + fn channel() -> u8 { + $chan + } + } + )+ + }; +} + +macro_rules! adc_internal { + ([$INT_ADC:ident, $INT_ADC_COMMON:ident]; $($input:ty => ($chan:expr, $en:ident)),+ $(,)*) => { + $( + impl $input { + pub fn new() -> Self { + Self {} + } + + /// Enables the internal voltage/sensor + /// ADC must be disabled. + pub fn enable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { + + let common = unsafe { ADCC::steal() }; + + common.ccr().modify(|_, w| w.$en().bit(true)); + } + /// Disables the internal voltage/sdissor + /// ADC must be disabled. + pub fn disable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { + + let common = unsafe { ADCC::steal() }; + + common.ccr().modify(|_, w| w.$en().bit(false)); + } + } + + adc_pins!($INT_ADC, $input => $chan); + )+ + }; +} + +impl Vddcore { + pub fn disable(_adc: &Adc) { + let adc2 = unsafe { ADC1::steal() }; + + adc2.or().modify(|_, w| w.op0().bit(false)); + } +} + +adc_internal!( + [ADC1, ADCC]; + + Temperature => (16, tsen), + Vrefint => (17, vbaten), + +); + +adc_internal!( + [ADC2, ADCC]; + + Vbat => (16, vrefen), +); + +macro_rules! adc_pins_common { + ($($input:ty => $chan:expr),+ $(,)*) => {$( + adc_pins!(ADC1, $input => $chan); + adc_pins!(ADC2, $input => $chan); + )*}; +} + +// stm32h523 +adc_pins_common!( + gpio::PC0 => 10, + gpio::PC1 => 11, + gpio::PC2 => 12, + gpio::PC3 => 13, + + gpio::PA0 => 0, + gpio::PA1 => 1, + gpio::PA2 => 14, + gpio::PA3 => 15, + gpio::PA4 => 18, + + gpio::PA5 => 19, + gpio::PA6 => 3, + gpio::PA7 => 7, + gpio::PC4 => 4, + gpio::PC5 => 8, + gpio::PB0 => 9, + gpio::PB1 => 5, +); + +adc_pins!( + ADC1, + gpio::PF11 => 2, + gpio::PF12 => 6, + +); + +adc_pins!( + ADC2, + gpio::PF13 => 2, + gpio::PF14 => 6, +); diff --git a/src/adc/mod.rs b/src/adc/mod.rs new file mode 100644 index 0000000..4c1388b --- /dev/null +++ b/src/adc/mod.rs @@ -0,0 +1,838 @@ +//! Analog to Digital Converter (ADC) +//! +//! ADC1 and ADC2 share a reset line. To initialise both of them, use the +//! [`adc12`] method. +//! +//! # Examples +//! +//! - [Reading a voltage using ADC1](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/adc.rs) +//! - [Reading a temperature using ADC2](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/temperature.rs) + +mod h5; + +use core::convert::Infallible; +use core::marker::PhantomData; +use core::ops::Deref; + +use embedded_hal::delay::DelayNs; +use stm32h5::stm32h523::ADCC; + +use crate::rcc::rec::AdcDacClkSelGetter; +use crate::stm32::{ADC1, ADC2}; + +use crate::pwr::{self, VoltageScale}; +//use crate::rcc::rec::AdcClkSelGetter; +use crate::rcc::{rec, CoreClocks, ResetEnable}; +use crate::time::Hertz; + +/// Marker trait for all ADC peripherals +pub trait Instance: + crate::Sealed + Deref +{ +} + +impl crate::Sealed for ADC1 {} +impl crate::Sealed for ADC2 {} + +impl Instance for ADC1 {} +impl Instance for ADC2 {} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Resolution { + TwelveBit = 0b00, + TenBit = 0b01, + EightBit = 0b10, + SixBit = 0b11, +} + +trait NumberOfBits { + fn number_of_bits(&self) -> u32; +} + +impl NumberOfBits for Resolution { + fn number_of_bits(&self) -> u32 { + match *self { + Resolution::SixBit => 6, + Resolution::EightBit => 8, + Resolution::TenBit => 10, + Resolution::TwelveBit => 12, + } + } +} + +/// Enabled ADC (type state) +pub struct Enabled; +/// Disabled ADC (type state) +pub struct Disabled; + +pub trait ED {} +impl ED for Enabled {} +impl ED for Disabled {} + +pub struct Adc { + rb: ADC, + sample_time: AdcSampleTime, + resolution: Resolution, + lshift: AdcLshift, + clock: Hertz, + current_channel: Option, + _enabled: PhantomData, +} + +/// ADC DMA modes +/// +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AdcDmaMode { + OneShot, + Circular, +} + +/// ADC sampling time +/// +/// Options for the sampling time, each is T + 0.5 ADC clock cycles. +// +// Refer to RM0433 Rev 7 - Chapter 25.4.13 +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(non_camel_case_types)] +pub enum AdcSampleTime { + /// 2.5 cycles sampling time + T_2_5, + /// 6.5 cycles sampling time + T_6_5, + /// 12.5 cycles sampling time + T_12_5, + /// 24.5 cycles sampling time + T_24_5, + /// 47.5 cycles sampling time + T_47_5, + /// 92.5 cycles sampling time + T_92_5, + /// 247.5 cycles sampling time + T_247_5, + /// 640.5 cycles sampling time + #[default] + T_640_5, +} + +impl AdcSampleTime { + /// Returns the number of half clock cycles represented by this sampling time + const fn clock_cycles_x2(&self) -> u32 { + let x = match self { + AdcSampleTime::T_2_5 => 2, + AdcSampleTime::T_6_5 => 6, + AdcSampleTime::T_12_5 => 12, + AdcSampleTime::T_24_5 => 24, + AdcSampleTime::T_47_5 => 47, + AdcSampleTime::T_92_5 => 92, + AdcSampleTime::T_247_5 => 247, + AdcSampleTime::T_640_5 => 640, + }; + (2 * x) + 1 + } +} + +// Refer to RM0433 Rev 7 - Chapter 25.4.13 +impl From for u8 { + fn from(val: AdcSampleTime) -> u8 { + match val { + AdcSampleTime::T_2_5 => 0b000, + AdcSampleTime::T_6_5 => 0b001, + AdcSampleTime::T_12_5 => 0b010, + AdcSampleTime::T_24_5 => 0b011, + AdcSampleTime::T_47_5 => 0b100, + AdcSampleTime::T_92_5 => 0b101, + AdcSampleTime::T_247_5 => 0b110, + AdcSampleTime::T_640_5 => 0b111, + } + } +} + +/// ADC LSHIFT\[3:0\] of the converted value +/// +/// Only values in range of 0..=15 are allowed. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcLshift(u8); + +impl AdcLshift { + pub fn new(lshift: u8) -> Self { + if lshift > 15 { + panic!("LSHIFT[3:0] must be in range of 0..=15"); + } + + AdcLshift(lshift) + } + + pub fn value(self) -> u8 { + self.0 + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcCalOffset(u8); + +impl AdcCalOffset { + pub fn value(self) -> u8 { + self.0 + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AdcCalLinear([u32; 6]); + +impl AdcCalLinear { + pub fn value(self) -> [u32; 6] { + self.0 + } +} + +/// Vref internal signal +#[derive(Default)] +pub struct Vrefint; +/// Vbat internal signal +#[derive(Default)] +pub struct Vbat; +/// Internal temperature sensor +#[derive(Default)] +pub struct Temperature; +/// Internal digital core voltage +#[derive(Default)] +pub struct Vddcore; + +pub trait AdcExt: Sized { + type Rec: ResetEnable; + + fn adc( + self, + f_adc: impl Into, + delay: &mut impl DelayNs, + prec: Self::Rec, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> Adc; +} + +/// Stored ADC config can be restored using the `Adc::restore_cfg` method +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct StoredConfig(AdcSampleTime, Resolution, AdcLshift); + +#[cfg(feature = "defmt")] +impl defmt::Format for StoredConfig { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "StoredConfig({:?}, {:?}, {:?})", + self.0, + defmt::Debug2Format(&self.1), + self.2 + ) + } +} + +/// Returns the frequency of the current adc_ker_ck +/// +/// # Panics +/// +/// Panics if the kernel clock is not running +fn kernel_clk_unwrap( + prec: &impl AdcDacClkSelGetter, + clocks: &CoreClocks, +) -> Hertz { + match prec.get_kernel_clk_mux() { + Some(rec::AdcDacClkSel::Hclk) => clocks.hclk(), + Some(rec::AdcDacClkSel::Sys) => clocks.sys_ck(), + Some(rec::AdcDacClkSel::Pll2R) => clocks + .pll2() + .r_ck() + .expect("ADC: PLL2R clock must be enabled"), + Some(rec::AdcDacClkSel::Hse) => { + clocks.hse_ck().expect("ADC: HSE clock must be enabled") + } + Some(rec::AdcDacClkSel::HsiKer) => { + clocks.hsi_ck().expect("ADC: HSI clock must be enabled") + } + Some(rec::AdcDacClkSel::CsiKer) => { + clocks.csi_ck().expect("ADC: CSI clock must be enabled") + } + None => unreachable!(), + } +} + +// ADC12 is a unique case where a single reset line is used to control two +// peripherals that have separate peripheral definitions in the SVD. + +/// Initialise ADC12 together +/// +/// Sets all configurable parameters to one-shot defaults, +/// performs a boot-time calibration. +pub fn adc12( + adc1: ADC1, + adc2: ADC2, + f_adc: impl Into, + delay: &mut impl DelayNs, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, +) -> (Adc, Adc) { + // Consume ADC register block, produce ADC1/2 with default settings + let mut adc1 = Adc::::default_from_rb(adc1); + let mut adc2 = Adc::::default_from_rb(adc2); + + // Check adc_ker_ck_input + kernel_clk_unwrap(&prec, clocks); + + // Enable AHB clock + let prec = prec.enable(); + + // Power Down + adc1.power_down(); + adc2.power_down(); + + // Reset peripheral + let prec = prec.reset(); + + // Power Up, Preconfigure and Calibrate + adc1.power_up(delay); + adc2.power_up(delay); + let f_adc = adc1.configure_clock(f_adc.into(), prec, clocks, pwrcfg); // ADC12_COMMON + adc2.clock = f_adc; + adc1.preconfigure(); + adc2.preconfigure(); + adc1.calibrate(); + adc2.calibrate(); + + // From RM0481: + // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected + adc2.rb.or().modify(|_, w| w.op0().set_bit()); + adc2.rb.or().modify(|_, w| w.op0().set_bit()); + + (adc1, adc2) +} + +/// Free both ADC1 and ADC2 along with PREC. +/// +/// Since ADC1 and ADC2 are controlled together, they are freed together. +pub fn free_adc12( + adc1: Adc, + adc2: Adc, +) -> (ADC1, ADC2, rec::Adc) { + ( + adc1.rb, + adc2.rb, + rec::Adc { + _marker: PhantomData, + }, + ) +} + +impl AdcExt for ADC { + type Rec = rec::Adc; + + fn adc( + self, + f_adc: impl Into, + delay: &mut impl DelayNs, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> Adc { + Adc::::adc(self, f_adc, delay, prec, clocks, pwrcfg) + } +} + +impl Adc { + /// Initialise ADC + /// + /// Sets all configurable parameters to one-shot defaults, + /// performs a boot-time calibration. + pub fn adc( + adc: ADC, + f_adc: impl Into, + delay: &mut impl DelayNs, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> Self { + // Consume ADC register block, produce Self with default + // settings + let mut adc = Self::default_from_rb(adc); + + // Enable AHB clock + let prec = prec.enable(); + + // Power Down + adc.power_down(); + + // Reset peripheral + let prec = prec.reset(); + + // Power Up, Preconfigure and Calibrate + adc.power_up(delay); + adc.configure_clock(f_adc.into(), prec, clocks, pwrcfg); + adc.preconfigure(); + adc.calibrate(); + + adc + } + /// Creates ADC with default settings + fn default_from_rb(rb: ADC) -> Self { + Self { + rb, + sample_time: AdcSampleTime::default(), + resolution: Resolution::TwelveBit, + lshift: AdcLshift::default(), + clock: Hertz::from_raw(0), + current_channel: None, + _enabled: PhantomData, + } + } + /// Sets the clock configuration for this ADC. This is common + /// between ADC1 and ADC2, so the prec block is used to ensure + /// this method can only be called on one of the ADCs (or both, + /// using the [adc12](#method.adc12) method). + /// + /// Only `CKMODE[1:0]` = 0 is supported + fn configure_clock( + &mut self, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> Hertz { + let ker_ck_input = kernel_clk_unwrap(&prec, clocks); + + let max_adc_ker_ck_analog = 75_000_000; + let (max_ker_ck, max_ker_ck_input) = match pwrcfg.vos { + VoltageScale::Scale0 => (125_000_000, 250_000_000), + VoltageScale::Scale1 => (100_000_000, 200_000_000), + VoltageScale::Scale2 => (75_000_000, 150_000_000), + VoltageScale::Scale3 => (50_000_000, 100_000_000), + }; + assert!(ker_ck_input.raw() <= max_ker_ck_input, + "Kernel clock violates maximum frequency defined in Reference Manual. \ + Can result in erroneous ADC readings"); + + let f_adc = self.configure_clock_unchecked(f_adc, prec, clocks); + + // Maximum ADC clock speed. With BOOST = 0 there is a no + // minimum frequency given in part datasheets + assert!(f_adc.raw() <= max_ker_ck); + assert!(f_adc.raw() <= max_adc_ker_ck_analog); + + f_adc + } + + /// No clock checks + fn configure_clock_unchecked( + &mut self, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + ) -> Hertz { + let ker_ck = kernel_clk_unwrap(&prec, clocks); + + let f_target = f_adc.raw(); + + let (divider, presc) = match ker_ck.raw().div_ceil(f_target) { + 1 => (1, 0b0000), + 2 => (2, 0b0001), + 3..=4 => (4, 0b0010), + 5..=6 => (6, 0b0011), + 7..=8 => (8, 0b0100), + 9..=10 => (10, 0b0101), + 11..=12 => (12, 0b0110), + 13..=16 => (16, 0b0111), + 17..=32 => (32, 0b1000), + 33..=64 => (64, 0b1001), + 65..=128 => (128, 0b1010), + 129..=256 => (256, 0b1011), + _ => panic!("Selecting the ADC clock required a prescaler > 256, \ + which is not possible in hardware. Either increase the ADC \ + clock frequency or decrease the kernel clock frequency"), + }; + unsafe { ADCC::steal() } + .ccr() + .modify(|_, w| unsafe { w.presc().bits(presc) }); + + let f_adc = Hertz::from_raw(ker_ck.raw() / divider); + + self.clock = f_adc; + f_adc + } + + /// Disables Deeppowerdown-mode and enables voltage regulator + /// + /// Note: After power-up, a [`calibration`](#method.calibrate) shall be run + pub fn power_up(&mut self, delay: &mut impl DelayNs) { + // Refer to RM0433 Rev 7 - Chapter 25.4.6 + self.rb + .cr() + .modify(|_, w| w.deeppwd().clear_bit().advregen().set_bit()); + delay.delay_us(10); + } + + /// Enables Deeppowerdown-mode and disables voltage regulator + /// + /// Note: This resets the [`calibration`](#method.calibrate) of the ADC + pub fn power_down(&mut self) { + // Refer to RM0433 Rev 7 - Chapter 25.4.6 + self.rb + .cr() + .modify(|_, w| w.deeppwd().set_bit().advregen().clear_bit()); + } + + /// Calibrates the ADC in single channel mode + /// + /// Note: The ADC must be disabled + pub fn calibrate(&mut self) { + // Refer to RM0433 Rev 7 - Chapter 25.4.8 + self.check_calibration_conditions(); + + let cal = |is_diff_mode| { + // single channel (INNx equals to V_ref-) + self.rb.cr().modify(|_, w| w.adcaldif().bit(is_diff_mode)); + // calibrate + self.rb.cr().modify(|_, w| w.adcal().set_bit()); + while self.rb.cr().read().adcal().bit_is_set() {} + }; + + // Calibrate for single ended + cal(false); + + // Calibrate for diff mode + cal(true); + } + + fn check_calibration_conditions(&self) { + let cr = self.rb.cr().read(); + if cr.aden().bit_is_set() { + panic!("Cannot start calibration when the ADC is enabled"); + } + if cr.deeppwd().bit_is_set() { + panic!("Cannot start calibration when the ADC is in deeppowerdown-mode"); + } + if cr.advregen().bit_is_clear() { + panic!("Cannot start calibration when the ADC voltage regulator is disabled"); + } + } + + /// Configuration process prior to enabling the ADC + /// + /// Note: the ADC must be disabled + fn preconfigure(&mut self) { + self.configure_channels_dif_mode(); + } + + /// Sets channels to single ended mode + fn configure_channels_dif_mode(&mut self) { + self.rb.difsel().reset(); + } + + /// Configuration process immediately after enabling the ADC + fn configure(&mut self) { + // Single conversion mode, Software trigger + // Refer to RM0433 Rev 7 - Chapters 25.4.15, 25.4.19 + self.rb.cfgr().modify(|_, w| { + w.cont().clear_bit().exten().disabled().discen().set_bit() + }); + + // TODO: Enable boost mode in PWR_PMCR for highest possible clock frequency when low vdd + } + + /// Enable ADC + pub fn enable(mut self) -> Adc { + // Refer to RM0433 Rev 7 - Chapter 25.4.9 + self.rb.isr().modify(|_, w| w.adrdy().clear()); + self.rb.cr().modify(|_, w| w.aden().set_bit()); + while self.rb.isr().read().adrdy().bit_is_clear() {} + self.rb.isr().modify(|_, w| w.adrdy().clear()); + + self.configure(); + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + lshift: self.lshift, + clock: self.clock, + current_channel: None, + _enabled: PhantomData, + } + } +} + +impl Adc { + fn stop_regular_conversion(&mut self) { + self.rb.cr().modify(|_, w| w.adstp().set_bit()); + while self.rb.cr().read().adstp().bit_is_set() {} + } + + fn stop_injected_conversion(&mut self) { + self.rb.cr().modify(|_, w| w.jadstp().set_bit()); + while self.rb.cr().read().jadstp().bit_is_set() {} + } + + fn set_chan_smp(&mut self, chan: u8) { + let t = self.get_sample_time().into(); + if chan <= 9 { + self.rb.smpr1().modify(|_, w| w.smp(chan).set(t)); + } else { + self.rb.smpr2().modify(|_, w| w.smp(chan - 10).set(t)); + } + } + + // This method starts a conversion sequence on the given channel + fn start_conversion_common(&mut self, chan: u8) { + self.check_conversion_conditions(); + + // Select channel (with preselection, refer to RM0433 Rev 7 - Chapter 25.4.12) + self.rb.sqr1().modify(|_, w| unsafe { w.sq1().bits(chan) }); + self.set_chan_smp(chan); + self.rb + .sqr1() + .modify(|_, w| unsafe { w.sq1().bits(chan).l().bits(0) }); + self.current_channel = Some(chan); + + // Perform conversion + self.rb.cr().modify(|_, w| w.adstart().set_bit()); + } + + /// Start conversion + /// + /// This method starts a conversion sequence on the given pin. + /// The value can be then read through the `read_sample` method. + // Refer to RM0433 Rev 7 - Chapter 25.4.16 + pub fn start_conversion(&mut self, _pin: &mut PIN) + where + PIN: embedded_hal_02::adc::Channel, + { + let chan = PIN::channel(); + assert!(chan <= 19); + + // Set resolution + self.rb + .cfgr() + .modify(|_, w| unsafe { w.res().bits(self.get_resolution() as _) }); + // Set discontinuous mode + self.rb + .cfgr() + .modify(|_, w| w.cont().clear_bit().discen().set_bit()); + + self.start_conversion_common(chan); + } + + /// Read sample + /// + /// `nb::Error::WouldBlock` in case the conversion is still + /// progressing. + // Refer to RM0433 Rev 7 - Chapter 25.4.16 + pub fn read_sample(&mut self) -> nb::Result { + self.current_channel + .expect("No channel was selected, use start_conversion first"); + + // Wait for the conversion to finished + if self.rb.isr().read().eoc().is_not_complete() { + return Err(nb::Error::WouldBlock); + } + + self.current_channel = None; + + // Retrieve result + let result = self.rb.dr().read().rdata().bits(); + Ok(result) + } + + fn check_conversion_conditions(&self) { + let cr = self.rb.cr().read(); + // Ensure that no conversions are ongoing + if cr.adstart().bit_is_set() { + panic!("Cannot start conversion because a regular conversion is ongoing"); + } + if cr.jadstart().bit_is_set() { + panic!("Cannot start conversion because an injected conversion is ongoing"); + } + // Ensure that the ADC is enabled + if cr.aden().bit_is_clear() { + panic!("Cannot start conversion because ADC is currently disabled"); + } + if cr.addis().bit_is_set() { + panic!("Cannot start conversion because there is a pending request to disable the ADC"); + } + } + + /// Disable ADC + pub fn disable(mut self) -> Adc { + let cr = self.rb.cr().read(); + // Refer to RM0433 Rev 7 - Chapter 25.4.9 + if cr.adstart().bit_is_set() { + self.stop_regular_conversion(); + } + if cr.jadstart().bit_is_set() { + self.stop_injected_conversion(); + } + + self.rb.cr().modify(|_, w| w.addis().set_bit()); + while self.rb.cr().read().aden().bit_is_set() {} + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + lshift: self.lshift, + clock: self.clock, + current_channel: None, + _enabled: PhantomData, + } + } +} + +impl Adc { + /// Save current ADC config + pub fn save_cfg(&mut self) -> StoredConfig { + StoredConfig( + self.get_sample_time(), + self.get_resolution(), + self.get_lshift(), + ) + } + + /// Restore saved ADC config + pub fn restore_cfg(&mut self, cfg: StoredConfig) { + self.set_sample_time(cfg.0); + self.set_resolution(cfg.1); + self.set_lshift(cfg.2); + } + + /// Reset the ADC config to default, return existing config + pub fn default_cfg(&mut self) -> StoredConfig { + let cfg = self.save_cfg(); + self.set_sample_time(AdcSampleTime::default()); + self.set_resolution(Resolution::TwelveBit); + self.set_lshift(AdcLshift::default()); + cfg + } + + /// The current ADC clock frequency. Defined as f_ADC in device datasheets + /// + /// The value returned by this method will always be equal or + /// lower than the `f_adc` passed to [`init`](#method.init) + pub fn clock_frequency(&self) -> Hertz { + self.clock + } + + /// The current ADC sampling frequency. This is the reciprocal of Tconv + pub const fn sampling_frequency(&self) -> Hertz { + let sample_cycles_x2 = self.sample_time.clock_cycles_x2(); + + // TODO: Exception for RM0468 ADC3 + // let sar_cycles_x2 = match self.resolution { + // Resolution::SixBit => 13, // 6.5 + // Resolution::EightBit => 17, // 8.5 + // Resolution::TenBit => 21, // 10.5 + // _ => 25, // 12.5 + // }; + + let sar_cycles = match self.resolution { + Resolution::SixBit => 6, + Resolution::EightBit => 8, + Resolution::TenBit => 10, + Resolution::TwelveBit => 12, + }; + + let cycles = (sample_cycles_x2 + 1) / 2 + sar_cycles; + Hertz::Hz(self.clock.to_Hz() / cycles) + } + + /// Get ADC samping time + pub fn get_sample_time(&self) -> AdcSampleTime { + self.sample_time + } + + /// Get ADC sampling resolution + pub fn get_resolution(&self) -> Resolution { + self.resolution + } + + /// Get ADC lshift value + pub fn get_lshift(&self) -> AdcLshift { + self.lshift + } + + /// Set ADC sampling time + /// + /// Options can be found in [AdcSampleTime]. + pub fn set_sample_time(&mut self, t_samp: AdcSampleTime) { + self.sample_time = t_samp; + } + + /// Set ADC sampling resolution + pub fn set_resolution(&mut self, res: Resolution) { + self.resolution = res; + } + + /// Set ADC lshift + /// + /// LSHIFT\[3:0\] must be in range of 0..=15 + pub fn set_lshift(&mut self, lshift: AdcLshift) { + self.lshift = lshift; + } + + /// Returns the largest possible sample value for the current ADC configuration + /// + /// Using this value as the denominator when calculating + /// transfer functions results in a gain error, and thus should + /// be avoided. Use the [slope](#method.slope) method instead. + #[deprecated(since = "0.12.0", note = "See the slope() method instead")] + pub fn max_sample(&self) -> u32 { + ((1 << self.get_resolution().number_of_bits() as u32) - 1) + << self.get_lshift().value() as u32 + } + + /// Returns the slope for the current ADC configuration. 1 LSB = Vref / slope + /// + /// This value can be used in calcuations involving the transfer function of + /// the ADC. For example, to calculate an estimate for the + /// applied voltage of an ADC channel referenced to voltage + /// `vref` + /// + /// ``` + /// let v = adc.read(&ch).unwrap() as f32 * vref / adc.slope() as f32; + /// ``` + pub fn slope(&self) -> u32 { + 1 << (self.get_resolution().number_of_bits() as u32 + + self.get_lshift().value() as u32) + } + + /// Returns the offset calibration value for single ended channel + pub fn read_offset_calibration_value(&self) -> AdcCalOffset { + AdcCalOffset(self.rb.calfact().read().calfact_s().bits()) + } + + /// Returns a reference to the inner peripheral + pub fn inner(&self) -> &ADC { + &self.rb + } + + /// Returns a mutable reference to the inner peripheral + pub fn inner_mut(&mut self) -> &mut ADC { + &mut self.rb + } +} + +impl embedded_hal_02::adc::OneShot + for Adc +where + PIN: embedded_hal_02::adc::Channel, +{ + type Error = Infallible; + + fn read(&mut self, pin: &mut PIN) -> nb::Result { + self.start_conversion(pin); + self.read_sample() + } +} From 066e18f6c6a28fceafee1fc2babc92d3021dd1fd Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 28 Aug 2025 12:45:45 +0200 Subject: [PATCH 03/12] Clippy and some cleanup --- src/adc/h5.rs | 8 ++-- src/adc/mod.rs | 99 +++++++++++++++++--------------------------------- 2 files changed, 37 insertions(+), 70 deletions(-) diff --git a/src/adc/h5.rs b/src/adc/h5.rs index dee9629..16ebf78 100644 --- a/src/adc/h5.rs +++ b/src/adc/h5.rs @@ -26,16 +26,16 @@ macro_rules! adc_internal { /// Enables the internal voltage/sensor /// ADC must be disabled. - pub fn enable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { - + pub fn enable(&mut self, _adc: &mut Adc<$INT_ADC, Disabled>) { + // TODO: This is not safe since we do not hold both adcs let common = unsafe { ADCC::steal() }; common.ccr().modify(|_, w| w.$en().bit(true)); } /// Disables the internal voltage/sdissor /// ADC must be disabled. - pub fn disable(&mut self, _adc: &Adc<$INT_ADC, Disabled>) { - + pub fn disable(&mut self, _adc: &mut Adc<$INT_ADC, Disabled>) { + // TODO: This is not safe since we do not hold both adcs let common = unsafe { ADCC::steal() }; common.ccr().modify(|_, w| w.$en().bit(false)); diff --git a/src/adc/mod.rs b/src/adc/mod.rs index 4c1388b..1169104 100644 --- a/src/adc/mod.rs +++ b/src/adc/mod.rs @@ -7,7 +7,8 @@ //! //! - [Reading a voltage using ADC1](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/adc.rs) //! - [Reading a temperature using ADC2](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/temperature.rs) - +//! +//! Originally from https://github.com/stm32-rs/stm32h7xx-hal mod h5; use core::convert::Infallible; @@ -15,10 +16,9 @@ use core::marker::PhantomData; use core::ops::Deref; use embedded_hal::delay::DelayNs; -use stm32h5::stm32h523::ADCC; use crate::rcc::rec::AdcDacClkSelGetter; -use crate::stm32::{ADC1, ADC2}; +use crate::stm32::{ADC1, ADC2, ADCC}; use crate::pwr::{self, VoltageScale}; //use crate::rcc::rec::AdcClkSelGetter; @@ -37,6 +37,9 @@ impl crate::Sealed for ADC2 {} impl Instance for ADC1 {} impl Instance for ADC2 {} +#[cfg(feature = "defmt")] +use defmt::{assert, panic}; + #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Copy, Clone, PartialEq, Debug)] pub enum Resolution { @@ -74,7 +77,6 @@ pub struct Adc { rb: ADC, sample_time: AdcSampleTime, resolution: Resolution, - lshift: AdcLshift, clock: Hertz, current_channel: Option, _enabled: PhantomData, @@ -150,27 +152,6 @@ impl From for u8 { } } -/// ADC LSHIFT\[3:0\] of the converted value -/// -/// Only values in range of 0..=15 are allowed. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct AdcLshift(u8); - -impl AdcLshift { - pub fn new(lshift: u8) -> Self { - if lshift > 15 { - panic!("LSHIFT[3:0] must be in range of 0..=15"); - } - - AdcLshift(lshift) - } - - pub fn value(self) -> u8 { - self.0 - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AdcCalOffset(u8); @@ -219,18 +200,13 @@ pub trait AdcExt: Sized { /// Stored ADC config can be restored using the `Adc::restore_cfg` method #[derive(Copy, Clone, Debug, PartialEq)] -pub struct StoredConfig(AdcSampleTime, Resolution, AdcLshift); +pub struct StoredConfig(AdcSampleTime, Resolution); #[cfg(feature = "defmt")] impl defmt::Format for StoredConfig { fn format(&self, fmt: defmt::Formatter) { - defmt::write!( - fmt, - "StoredConfig({:?}, {:?}, {:?})", - self.0, - defmt::Debug2Format(&self.1), - self.2 - ) + let StoredConfig(sample_time, res) = &self; + defmt::write!(fmt, "StoredConfig({:?}, {:?})", sample_time, res) } } @@ -341,7 +317,7 @@ impl AdcExt for ADC { clocks: &CoreClocks, pwrcfg: &pwr::PowerConfiguration, ) -> Adc { - Adc::::adc(self, f_adc, delay, prec, clocks, pwrcfg) + Adc::::new(self, f_adc, delay, prec, clocks, pwrcfg) } } @@ -350,7 +326,7 @@ impl Adc { /// /// Sets all configurable parameters to one-shot defaults, /// performs a boot-time calibration. - pub fn adc( + pub fn new( adc: ADC, f_adc: impl Into, delay: &mut impl DelayNs, @@ -385,7 +361,6 @@ impl Adc { rb, sample_time: AdcSampleTime::default(), resolution: Resolution::TwelveBit, - lshift: AdcLshift::default(), clock: Hertz::from_raw(0), current_channel: None, _enabled: PhantomData, @@ -558,7 +533,6 @@ impl Adc { rb: self.rb, sample_time: self.sample_time, resolution: self.resolution, - lshift: self.lshift, clock: self.clock, current_channel: None, _enabled: PhantomData, @@ -614,14 +588,16 @@ impl Adc { let chan = PIN::channel(); assert!(chan <= 19); + // TODO: Move to ADC init? // Set resolution self.rb .cfgr() .modify(|_, w| unsafe { w.res().bits(self.get_resolution() as _) }); - // Set discontinuous mode + + // TODO: Move to ADC init? self.rb .cfgr() - .modify(|_, w| w.cont().clear_bit().discen().set_bit()); + .modify(|_, w| w.cont().single().discen().disabled()); self.start_conversion_common(chan); } @@ -643,10 +619,20 @@ impl Adc { self.current_channel = None; // Retrieve result - let result = self.rb.dr().read().rdata().bits(); + let result = self.current_sample(); Ok(result) } + /// Read the current value in the data register + /// + /// This simply returns whatever value is in the data register without blocking. + /// Use [Self::read_sample] if you want to wait for any ongoing conversion to finish. + /// + /// NOTE: Depending on OVRMOD the data register acts like a FIFO queue with three stages + pub fn current_sample(&mut self) -> u16 { + self.rb.dr().read().rdata().bits() + } + fn check_conversion_conditions(&self) { let cr = self.rb.cr().read(); // Ensure that no conversions are ongoing @@ -683,7 +669,6 @@ impl Adc { rb: self.rb, sample_time: self.sample_time, resolution: self.resolution, - lshift: self.lshift, clock: self.clock, current_channel: None, _enabled: PhantomData, @@ -694,18 +679,13 @@ impl Adc { impl Adc { /// Save current ADC config pub fn save_cfg(&mut self) -> StoredConfig { - StoredConfig( - self.get_sample_time(), - self.get_resolution(), - self.get_lshift(), - ) + StoredConfig(self.get_sample_time(), self.get_resolution()) } /// Restore saved ADC config pub fn restore_cfg(&mut self, cfg: StoredConfig) { self.set_sample_time(cfg.0); self.set_resolution(cfg.1); - self.set_lshift(cfg.2); } /// Reset the ADC config to default, return existing config @@ -713,7 +693,6 @@ impl Adc { let cfg = self.save_cfg(); self.set_sample_time(AdcSampleTime::default()); self.set_resolution(Resolution::TwelveBit); - self.set_lshift(AdcLshift::default()); cfg } @@ -744,7 +723,7 @@ impl Adc { Resolution::TwelveBit => 12, }; - let cycles = (sample_cycles_x2 + 1) / 2 + sar_cycles; + let cycles = sample_cycles_x2.div_ceil(2) + sar_cycles; Hertz::Hz(self.clock.to_Hz() / cycles) } @@ -758,11 +737,6 @@ impl Adc { self.resolution } - /// Get ADC lshift value - pub fn get_lshift(&self) -> AdcLshift { - self.lshift - } - /// Set ADC sampling time /// /// Options can be found in [AdcSampleTime]. @@ -775,13 +749,6 @@ impl Adc { self.resolution = res; } - /// Set ADC lshift - /// - /// LSHIFT\[3:0\] must be in range of 0..=15 - pub fn set_lshift(&mut self, lshift: AdcLshift) { - self.lshift = lshift; - } - /// Returns the largest possible sample value for the current ADC configuration /// /// Using this value as the denominator when calculating @@ -789,8 +756,7 @@ impl Adc { /// be avoided. Use the [slope](#method.slope) method instead. #[deprecated(since = "0.12.0", note = "See the slope() method instead")] pub fn max_sample(&self) -> u32 { - ((1 << self.get_resolution().number_of_bits() as u32) - 1) - << self.get_lshift().value() as u32 + (1 << self.get_resolution().number_of_bits()) - 1 } /// Returns the slope for the current ADC configuration. 1 LSB = Vref / slope @@ -804,8 +770,7 @@ impl Adc { /// let v = adc.read(&ch).unwrap() as f32 * vref / adc.slope() as f32; /// ``` pub fn slope(&self) -> u32 { - 1 << (self.get_resolution().number_of_bits() as u32 - + self.get_lshift().value() as u32) + 1 << self.get_resolution().number_of_bits() } /// Returns the offset calibration value for single ended channel @@ -831,8 +796,10 @@ where { type Error = Infallible; + // TODO: We are not really non-blocking fn read(&mut self, pin: &mut PIN) -> nb::Result { self.start_conversion(pin); - self.read_sample() + let res = nb::block!(self.read_sample()).unwrap(); + Ok(res) } } From c2f8714eeb34bd2b7e1d529bedd0f768c609dc29 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 28 Aug 2025 12:47:46 +0200 Subject: [PATCH 04/12] Ccdr does not need to consume PowerConfiguration --- examples/blinky.rs | 2 +- examples/dma.rs | 2 +- examples/dwt-blinky.rs | 2 +- examples/fractional-pll.rs | 2 +- examples/i2c.rs | 2 +- examples/i2c_target.rs | 2 +- examples/i2c_target_manual_ack.rs | 2 +- examples/rcc.rs | 2 +- examples/spi.rs | 2 +- examples/spi_send_frames.rs | 2 +- examples/spi_slave.rs | 2 +- examples/usb_serial.rs | 2 +- src/rcc.rs | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/blinky.rs b/examples/blinky.rs index c1cab03..38bc6ce 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -22,7 +22,7 @@ fn main() -> ! { // Constrain and Freeze clock let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); let mut led = gpioa.pa5.into_push_pull_output(); diff --git a/examples/dma.rs b/examples/dma.rs index 5562e52..7843725 100644 --- a/examples/dma.rs +++ b/examples/dma.rs @@ -24,7 +24,7 @@ fn main() -> ! { // Constrain and Freeze clock let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let channels = dp.GPDMA1.channels(ccdr.peripheral.GPDMA1); diff --git a/examples/dwt-blinky.rs b/examples/dwt-blinky.rs index 4dbadde..c72fc0c 100644 --- a/examples/dwt-blinky.rs +++ b/examples/dwt-blinky.rs @@ -28,7 +28,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); let mut led = gpioa.pa5.into_push_pull_output(); diff --git a/examples/fractional-pll.rs b/examples/fractional-pll.rs index 1b767ec..e7371e5 100644 --- a/examples/fractional-pll.rs +++ b/examples/fractional-pll.rs @@ -31,7 +31,7 @@ fn main() -> ! { .pll2_r_ck(3_024_000.Hz()) // pll2_p / 2 --> mco2 .mco2_from_pll2_p_ck(7.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // // Enable MCO2 output pin // let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); diff --git a/examples/i2c.rs b/examples/i2c.rs index 3f201eb..dce0b51 100644 --- a/examples/i2c.rs +++ b/examples/i2c.rs @@ -27,7 +27,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS); let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); diff --git a/examples/i2c_target.rs b/examples/i2c_target.rs index 60d4498..7390e98 100644 --- a/examples/i2c_target.rs +++ b/examples/i2c_target.rs @@ -28,7 +28,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS); let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); diff --git a/examples/i2c_target_manual_ack.rs b/examples/i2c_target_manual_ack.rs index 9085df5..a7136cd 100644 --- a/examples/i2c_target_manual_ack.rs +++ b/examples/i2c_target_manual_ack.rs @@ -28,7 +28,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(100.MHz()).freeze(&pwrcfg, &dp.SBS); let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); diff --git a/examples/rcc.rs b/examples/rcc.rs index 1195de9..f110f9b 100644 --- a/examples/rcc.rs +++ b/examples/rcc.rs @@ -24,7 +24,7 @@ fn main() -> ! { // Constrain and Freeze clock info!("Setup RCC... "); let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); info!(""); info!("stm32h5xx-hal example - RCC"); diff --git a/examples/spi.rs b/examples/spi.rs index e13b98f..9a486ce 100644 --- a/examples/spi.rs +++ b/examples/spi.rs @@ -28,7 +28,7 @@ fn main() -> ! { let ccdr = rcc .sys_ck(192.MHz()) .pll1_q_ck(64.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // Acquire the GPIOB peripheral. This also enables the clock for // GPIOB in the RCC register. diff --git a/examples/spi_send_frames.rs b/examples/spi_send_frames.rs index 016c4ed..c8047c0 100644 --- a/examples/spi_send_frames.rs +++ b/examples/spi_send_frames.rs @@ -37,7 +37,7 @@ fn main() -> ! { let ccdr = rcc .sys_ck(192.MHz()) .pll1_q_ck(64.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // Acquire the GPIOB peripheral. This also enables the clock for // GPIOB in the RCC register. diff --git a/examples/spi_slave.rs b/examples/spi_slave.rs index def4c47..1eceaf9 100644 --- a/examples/spi_slave.rs +++ b/examples/spi_slave.rs @@ -24,7 +24,7 @@ fn main() -> ! { let ccdr = rcc .sys_ck(100.MHz()) .pll1_q_ck(50.MHz()) - .freeze(pwrcfg, &dp.SBS); + .freeze(&pwrcfg, &dp.SBS); // Acquire the GPIOB peripheral. This also enables the clock for // GPIOB in the RCC register. diff --git a/examples/usb_serial.rs b/examples/usb_serial.rs index dc6e04e..d38af24 100644 --- a/examples/usb_serial.rs +++ b/examples/usb_serial.rs @@ -31,7 +31,7 @@ fn main() -> ! { let pwrcfg = pwr.vos0().freeze(); // Constrain and Freeze clock let rcc = dp.RCC.constrain(); - let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + let ccdr = rcc.sys_ck(250.MHz()).freeze(&pwrcfg, &dp.SBS); let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); diff --git a/src/rcc.rs b/src/rcc.rs index 6c0a432..dff42e3 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -558,7 +558,7 @@ impl Rcc { /// function may also panic if a clock specification can be /// achieved, but the mechanism for doing so is not yet /// implemented here. - pub fn freeze(mut self, pwrcfg: PowerConfiguration, sbs: &SBS) -> Ccdr { + pub fn freeze(mut self, pwrcfg: &PowerConfiguration, sbs: &SBS) -> Ccdr { // We do not reset RCC here. This routine must assert when // the previous state of the RCC peripheral is unacceptable. From 604174b1ada9f6d54b347e28d41c7f4ee21974fa Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 28 Aug 2025 12:48:15 +0200 Subject: [PATCH 05/12] Add adc examples --- examples/adc.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++ examples/adc12.rs | 83 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 examples/adc.rs create mode 100644 examples/adc12.rs diff --git a/examples/adc.rs b/examples/adc.rs new file mode 100644 index 0000000..51bbb18 --- /dev/null +++ b/examples/adc.rs @@ -0,0 +1,90 @@ +//! Example of reading a voltage with ADC1 +//! +//! For an example of using ADC1 and ADC2 together, see examples/adc12.rs + +#![no_main] +#![no_std] + +use cortex_m_rt::entry; + +use embedded_hal_02::adc::OneShot; +use stm32h5xx_hal::{ + adc, delay::Delay, pac, prelude::*, rcc::rec::AdcDacClkSel, +}; +use utilities::logger::info; + +#[macro_use] +mod utilities; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + let cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); + + // Constrain and Freeze power + info!("Setup PWR... "); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.freeze(); + + // Constrain and Freeze clock + info!("Setup RCC... "); + let rcc = dp.RCC.constrain(); + + // We need to configure a clock for adc_ker_ck_input. The default + // adc_ker_ck_input is pll2_p_ck, but we will use per_ck. per_ck is sourced + // from the 64MHz HSI + // + // adc_ker_ck_input is then divided by the ADC prescaler to give f_adc. The + // maximum f_adc is 50MHz + let mut ccdr = rcc + .sys_ck(192.MHz()) + .pll1_q_ck(64.MHz()) + .freeze(&pwrcfg, &dp.SBS); + + // Switch adc_ker_ck_input multiplexer to per_ck + ccdr.peripheral.kernel_adcdac_clk_mux(AdcDacClkSel::HsiKer); + + info!(""); + info!("stm32h5xx-hal example - ADC"); + info!(""); + + let mut delay = Delay::new(cp.SYST, &ccdr.clocks); + + // Setup ADC + let mut adc1 = adc::Adc::new( + dp.ADC1, + 4.MHz(), + &mut delay, + ccdr.peripheral.ADC, + &ccdr.clocks, + &pwrcfg, + ); + + let mut temp = adc::Temperature::new(); + temp.enable(&mut adc1); + let mut adc1: adc::Adc< + stm32h5::Periph, + adc::Enabled, + > = adc1.enable(); + + // We can't use ADC2 here because ccdr.peripheral.ADC12 has been + // consumed. See examples/adc12.rs + + // Setup GPIOC + let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); + + // Configure pc0 as an analog input + let mut channel = gpioc.pc0.into_analog(); // ANALOG IN 10 + + loop { + let data = adc1.read(&mut channel).unwrap(); + // voltage = reading * (vref/resolution) + info!( + "ADC reading: {}, voltage for nucleo: {}V. Temp reading: {}", + data, + data as f32 * (3.3 / adc1.slope() as f32), + adc1.read(&mut temp).unwrap() + ); + } +} diff --git a/examples/adc12.rs b/examples/adc12.rs new file mode 100644 index 0000000..53e1e33 --- /dev/null +++ b/examples/adc12.rs @@ -0,0 +1,83 @@ +//! Example of using ADC1 and ADC2 together +//! +//! This is not available for H503 since it only has ADC1 +//! +//! For an example of using ADC1 alone, see examples/adc.rs + +#![no_main] +#![no_std] + +use cortex_m_rt::entry; + +use embedded_hal_02::adc::OneShot; +use stm32h5xx_hal::{ + adc, delay::Delay, pac, prelude::*, rcc::rec::AdcDacClkSel, +}; +use utilities::logger::info; + +#[macro_use] +mod utilities; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + let cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); + + // Constrain and Freeze power + info!("Setup PWR... "); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.freeze(); + + // Constrain and Freeze clock + info!("Setup RCC... "); + let rcc = dp.RCC.constrain(); + + // We need to configure a clock for adc_ker_ck_input. The default + // adc_ker_ck_input is pll2_p_ck, but we will use per_ck. per_ck is sourced + // from the 64MHz HSI + // + // adc_ker_ck_input is then divided by the ADC prescaler to give f_adc. The + // maximum f_adc is 50MHz + let mut ccdr = rcc + .sys_ck(192.MHz()) + .pll1_q_ck(64.MHz()) + .freeze(&pwrcfg, &dp.SBS); + + // Switch adc_ker_ck_input multiplexer to per_ck + ccdr.peripheral.kernel_adcdac_clk_mux(AdcDacClkSel::HsiKer); + + info!(""); + info!("stm32h5xx-hal example - ADC1 and ADC2"); + info!(""); + + let mut delay = Delay::new(cp.SYST, &ccdr.clocks); + + // Setup ADC + // Setup ADC1 and ADC2 + let (adc1, adc2) = adc::adc12( + dp.ADC1, + dp.ADC2, + 4.MHz(), + &mut delay, + ccdr.peripheral.ADC, + &ccdr.clocks, + &pwrcfg, + ); + + let mut adc1 = adc1.enable(); + let mut adc2 = adc2.enable(); + + // Setup GPIOC + // NOTE: PC2 and PC3 are only pinned out on TFBGA packages!! + let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); + let mut channel_pc2 = gpioc.pc2.into_analog(); // AIN 12 + let mut channel_pc3 = gpioc.pc3.into_analog(); // AIN 13 + + loop { + let data_pc2 = adc1.read(&mut channel_pc2).unwrap(); + let data_pc3 = adc2.read(&mut channel_pc3).unwrap(); + // voltage = reading * (vref/resolution) + info!("ADC readings: {} {}", data_pc2, data_pc3); + } +} From be9bf31174de2794d642d90b7125370d3ae88874 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 28 Aug 2025 12:55:18 +0200 Subject: [PATCH 06/12] Make adc optional --- .github/workflows/ci.yml | 7 +++++-- Cargo.toml | 15 +++++++++++++-- src/lib.rs | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cdfcc4..b36a53a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,9 @@ jobs: logger: - log - defmt + adc: + - adc + - env: # Peripheral Feature flags FLAGS: rt @@ -45,6 +48,6 @@ jobs: - name: Install thumbv8m rust target run: rustup target add thumbv8m.main-none-eabihf - name: Build - run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }} + run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.adc }},${{ env.FLAGS }} - name: Test - run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }} + run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.adc }},${{ env.FLAGS }} diff --git a/Cargo.toml b/Cargo.toml index 9f04f25..086b27a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,20 +67,22 @@ defmt = [ "stm32h5/defmt", ] +adc = ["dep:embedded-hal-02", "dep:nb"] + [dependencies] cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] } stm32h5 = { package = "stm32h5", version = "0.16.0" } fugit = "0.3.7" embedded-dma = "0.2" embedded-hal = "1.0.0" -embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } +embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"], optional = true } defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true} stm32-usbd = "0.8.0" -nb = "1.1.0" +nb = { version = "1.1.0", optional = true } [dev-dependencies] log = { version = "0.4.20"} @@ -119,3 +121,12 @@ required-features = ["stm32h503"] [[example]] name = "i2c_target_manual_ack" required-features = ["stm32h503"] + +[[example]] +name = "adc" +required-features = ["adc"] + +[[example]] +name = "adc12" +required-features = ["adc", "rm0481"] + diff --git a/src/lib.rs b/src/lib.rs index ed4356c..85b90db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ pub mod prelude; #[macro_use] mod macros; -#[cfg(feature = "device-selected")] +#[cfg(all(feature = "device-selected", feature = "adc"))] pub mod adc; #[cfg(feature = "device-selected")] From 670a94d3413a96feaa3d81c73cc07b5cae62e7fb Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 27 Aug 2025 21:13:44 +0200 Subject: [PATCH 07/12] H503 --- src/adc/h5.rs | 57 +++++++++++++++++++++++++++++++++++++++--- src/adc/mod.rs | 68 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 101 insertions(+), 24 deletions(-) diff --git a/src/adc/h5.rs b/src/adc/h5.rs index 16ebf78..47c752e 100644 --- a/src/adc/h5.rs +++ b/src/adc/h5.rs @@ -1,5 +1,10 @@ use super::{Adc, Disabled, Temperature, Vbat, Vddcore, Vrefint}; use crate::gpio::{self, Analog}; + +#[cfg(feature = "rm0492")] +use crate::stm32::{ADC1, ADC1 as ADCC}; + +#[cfg(feature = "rm0481")] use crate::stm32::{ADC1, ADC2, ADCC}; macro_rules! adc_pins { @@ -47,36 +52,78 @@ macro_rules! adc_internal { }; } +#[cfg(feature = "rm0492")] impl Vddcore { + pub fn enable(_adc: &Adc) { + let adc = unsafe { ADC1::steal() }; + + adc.or().modify(|_, w| w.op1().set_bit()); + } + + pub fn disable(_adc: &Adc) { + let adc = unsafe { ADC1::steal() }; + + adc.or().modify(|_, w| w.op1().clear_bit()); + } +} +#[cfg(feature = "rm0492")] +adc_pins!(ADC1, Vddcore => 16); + +#[cfg(feature = "rm0481")] +impl Vddcore { + pub fn enable(_adc: &Adc) { + let adc2 = unsafe { ADC1::steal() }; + + adc2.or().modify(|_, w| w.op0().bit(true)); + } + pub fn disable(_adc: &Adc) { let adc2 = unsafe { ADC1::steal() }; adc2.or().modify(|_, w| w.op0().bit(false)); } } +#[cfg(feature = "rm0481")] +adc_pins!(ADC2, Vddcore => 17); +#[cfg(feature = "rm0492")] adc_internal!( [ADC1, ADCC]; Temperature => (16, tsen), - Vrefint => (17, vbaten), + Vrefint => (17, vrefen), + Vbat => (2, vbaten), +); +#[cfg(feature = "rm0481")] +adc_internal!( + [ADC1, ADCC]; + + Temperature => (16, tsen), + Vrefint => (17, vrefen), ); +#[cfg(feature = "rm0481")] adc_internal!( [ADC2, ADCC]; - Vbat => (16, vrefen), + Vbat => (16, vbaten), ); macro_rules! adc_pins_common { ($($input:ty => $chan:expr),+ $(,)*) => {$( adc_pins!(ADC1, $input => $chan); + + #[cfg(feature = "rm0481")] adc_pins!(ADC2, $input => $chan); )*}; } -// stm32h523 +#[cfg(any( + feature = "stm32h503", + feature = "stm32h523", + feature = "stm32h533" +))] adc_pins_common!( gpio::PC0 => 10, gpio::PC1 => 11, @@ -88,16 +135,17 @@ adc_pins_common!( gpio::PA2 => 14, gpio::PA3 => 15, gpio::PA4 => 18, - gpio::PA5 => 19, gpio::PA6 => 3, gpio::PA7 => 7, + gpio::PC4 => 4, gpio::PC5 => 8, gpio::PB0 => 9, gpio::PB1 => 5, ); +#[cfg(feature = "stm32h523")] adc_pins!( ADC1, gpio::PF11 => 2, @@ -105,6 +153,7 @@ adc_pins!( ); +#[cfg(feature = "stm32h523")] adc_pins!( ADC2, gpio::PF13 => 2, diff --git a/src/adc/mod.rs b/src/adc/mod.rs index 1169104..ee68017 100644 --- a/src/adc/mod.rs +++ b/src/adc/mod.rs @@ -18,6 +18,11 @@ use core::ops::Deref; use embedded_hal::delay::DelayNs; use crate::rcc::rec::AdcDacClkSelGetter; + +#[cfg(feature = "rm0492")] +use crate::stm32::{ADC1, ADC1 as ADCC}; + +#[cfg(feature = "rm0481")] use crate::stm32::{ADC1, ADC2, ADCC}; use crate::pwr::{self, VoltageScale}; @@ -32,9 +37,13 @@ pub trait Instance: } impl crate::Sealed for ADC1 {} + +#[cfg(feature = "rm0481")] impl crate::Sealed for ADC2 {} impl Instance for ADC1 {} + +#[cfg(feature = "rm0481")] impl Instance for ADC2 {} #[cfg(feature = "defmt")] @@ -175,7 +184,7 @@ impl AdcCalLinear { /// Vref internal signal #[derive(Default)] pub struct Vrefint; -/// Vbat internal signal +/// Vbat/4 (Vbat pin input voltage divided by 4) internal signal #[derive(Default)] pub struct Vbat; /// Internal temperature sensor @@ -239,13 +248,48 @@ fn kernel_clk_unwrap( } } -// ADC12 is a unique case where a single reset line is used to control two -// peripherals that have separate peripheral definitions in the SVD. +#[cfg(feature = "rm0492")] +pub fn adc1( + adc1: ADC1, + f_adc: impl Into, + delay: &mut impl DelayNs, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, +) -> Adc { + // Consume ADC register block, produce ADC1/2 with default settings + let mut adc1 = Adc::::default_from_rb(adc1); + + // Check adc_ker_ck_input + kernel_clk_unwrap(&prec, clocks); + + // Enable AHB clock + let prec = prec.enable(); + + // Power Down + adc1.power_down(); + + // Reset peripheral + let prec = prec.reset(); + + // Power Up, Preconfigure and Calibrate + adc1.power_up(delay); + adc1.configure_clock(f_adc.into(), prec, clocks, pwrcfg); // ADC12_COMMON + adc1.preconfigure(); + adc1.calibrate(); + + // From RM0481: + // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected + adc1.rb.or().modify(|_, w| w.op0().set_bit()); + + adc1 +} /// Initialise ADC12 together /// /// Sets all configurable parameters to one-shot defaults, /// performs a boot-time calibration. +#[cfg(feature = "rm0481")] pub fn adc12( adc1: ADC1, adc2: ADC2, @@ -284,28 +328,12 @@ pub fn adc12( // From RM0481: // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected - adc2.rb.or().modify(|_, w| w.op0().set_bit()); + adc1.rb.or().modify(|_, w| w.op0().set_bit()); adc2.rb.or().modify(|_, w| w.op0().set_bit()); (adc1, adc2) } -/// Free both ADC1 and ADC2 along with PREC. -/// -/// Since ADC1 and ADC2 are controlled together, they are freed together. -pub fn free_adc12( - adc1: Adc, - adc2: Adc, -) -> (ADC1, ADC2, rec::Adc) { - ( - adc1.rb, - adc2.rb, - rec::Adc { - _marker: PhantomData, - }, - ) -} - impl AdcExt for ADC { type Rec = rec::Adc; From 958b7610b3c68cc031969e2b120a22494262ada3 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 29 Aug 2025 09:03:55 +0200 Subject: [PATCH 08/12] Require mut ref to adc --- src/adc/h5.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/adc/h5.rs b/src/adc/h5.rs index 47c752e..16284e3 100644 --- a/src/adc/h5.rs +++ b/src/adc/h5.rs @@ -54,13 +54,13 @@ macro_rules! adc_internal { #[cfg(feature = "rm0492")] impl Vddcore { - pub fn enable(_adc: &Adc) { + pub fn enable(_adc: &mut Adc) { let adc = unsafe { ADC1::steal() }; adc.or().modify(|_, w| w.op1().set_bit()); } - pub fn disable(_adc: &Adc) { + pub fn disable(_adc: &mut Adc) { let adc = unsafe { ADC1::steal() }; adc.or().modify(|_, w| w.op1().clear_bit()); @@ -71,13 +71,13 @@ adc_pins!(ADC1, Vddcore => 16); #[cfg(feature = "rm0481")] impl Vddcore { - pub fn enable(_adc: &Adc) { + pub fn enable(_adc: &mut Adc) { let adc2 = unsafe { ADC1::steal() }; adc2.or().modify(|_, w| w.op0().bit(true)); } - pub fn disable(_adc: &Adc) { + pub fn disable(_adc: &mut Adc) { let adc2 = unsafe { ADC1::steal() }; adc2.or().modify(|_, w| w.op0().bit(false)); From d4f1397510440c2d3ace2643691ac16458ec3519 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 6 Sep 2025 23:29:55 +0200 Subject: [PATCH 09/12] Update ADC API --- examples/adc.rs | 18 +- src/adc/mod.rs | 475 ++++++++++++++++++++---------------------------- 2 files changed, 217 insertions(+), 276 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index 51bbb18..8687978 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -9,7 +9,11 @@ use cortex_m_rt::entry; use embedded_hal_02::adc::OneShot; use stm32h5xx_hal::{ - adc, delay::Delay, pac, prelude::*, rcc::rec::AdcDacClkSel, + adc::{self, AdcCommonExt}, + delay::Delay, + pac, + prelude::*, + rcc::rec::AdcDacClkSel, }; use utilities::logger::info; @@ -51,6 +55,18 @@ fn main() -> ! { let mut delay = Delay::new(cp.SYST, &ccdr.clocks); + #[cfg(feature = "rm0481")] + let adc = dp + .ADCC + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg) + .claim_and_configure(dp.ADC1, &mut delay); + + #[cfg(feature = "rm0492")] + let adc = dp + .ADC1 + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg) + .claim_and_configure(&mut delay); + // Setup ADC let mut adc1 = adc::Adc::new( dp.ADC1, diff --git a/src/adc/mod.rs b/src/adc/mod.rs index ee68017..d0d0555 100644 --- a/src/adc/mod.rs +++ b/src/adc/mod.rs @@ -46,6 +46,170 @@ impl Instance for ADC1 {} #[cfg(feature = "rm0481")] impl Instance for ADC2 {} +pub trait AdcCommonExt { + fn claim( + self, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> AdcCommon; +} + +impl AdcCommonExt for ADCC { + fn claim( + self, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> AdcCommon { + // Check adc_ker_ck_input + kernel_clk_unwrap(&prec, clocks); + + // Enable AHB clock + let prec = prec.enable(); + + // Reset peripheral + let prec = prec.reset(); + + let _f_adc = AdcCommon::configure_clock( + &self, + f_adc.into(), + prec, + clocks, + pwrcfg, + ); // ADC12_COMMON + #[cfg(feature = "defmt")] + defmt::trace!("Set f_adc to: {}", _f_adc); + #[cfg(feature = "log")] + log::trace!("Set f_adc to: {}", _f_adc); + + AdcCommon {} + } +} + +/// Type for initialized `ADC12_COMMON` or `ADC345_COMMON` +/// +/// See [`AdcCommon::claim`] +#[non_exhaustive] +pub struct AdcCommon {} + +impl AdcCommon { + /// Sets the clock configuration for this ADC. This is common + /// between ADC1 and ADC2, so the prec block is used to ensure + /// this method can only be called on one of the ADCs (or both, + /// using the [adc12](#method.adc12) method). + /// + /// Only `CKMODE[1:0]` = 0 is supported + fn configure_clock( + adcc: &ADCC, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + pwrcfg: &pwr::PowerConfiguration, + ) -> Hertz { + let ker_ck_input = kernel_clk_unwrap(&prec, clocks); + + let max_adc_ker_ck_analog = 75_000_000; + let (max_ker_ck, max_ker_ck_input) = match pwrcfg.vos { + VoltageScale::Scale0 => (125_000_000, 250_000_000), + VoltageScale::Scale1 => (100_000_000, 200_000_000), + VoltageScale::Scale2 => (75_000_000, 150_000_000), + VoltageScale::Scale3 => (50_000_000, 100_000_000), + }; + assert!(ker_ck_input.raw() <= max_ker_ck_input, + "Kernel clock violates maximum frequency defined in Reference Manual. \ + Can result in erroneous ADC readings"); + + let f_adc = Self::configure_clock_unchecked(adcc, f_adc, prec, clocks); + + // Maximum ADC clock speed. With BOOST = 0 there is a no + // minimum frequency given in part datasheets + assert!(f_adc.raw() <= max_ker_ck); + assert!(f_adc.raw() <= max_adc_ker_ck_analog); + + f_adc + } + + /// No clock checks + fn configure_clock_unchecked( + adcc: &ADCC, + f_adc: Hertz, + prec: rec::Adc, + clocks: &CoreClocks, + ) -> Hertz { + let ker_ck = kernel_clk_unwrap(&prec, clocks); + + let f_target = f_adc.raw(); + + let (divider, presc) = match ker_ck.raw().div_ceil(f_target) { + 1 => (1, 0b0000), + 2 => (2, 0b0001), + 3..=4 => (4, 0b0010), + 5..=6 => (6, 0b0011), + 7..=8 => (8, 0b0100), + 9..=10 => (10, 0b0101), + 11..=12 => (12, 0b0110), + 13..=16 => (16, 0b0111), + 17..=32 => (32, 0b1000), + 33..=64 => (64, 0b1001), + 65..=128 => (128, 0b1010), + 129..=256 => (256, 0b1011), + _ => panic!("Selecting the ADC clock required a prescaler > 256, \ + which is not possible in hardware. Either increase the ADC \ + clock frequency or decrease the kernel clock frequency"), + }; + adcc.ccr().modify(|_, w| unsafe { w.presc().bits(presc) }); + + let f_adc = Hertz::from_raw(ker_ck.raw() / divider); + f_adc + } + + fn setup_adc( + adc: ADC, + delay: &mut impl DelayNs, + ) -> Adc { + // Consume ADC register block, produce ADC1/2 with default settings + let adc = Adc::::default_from_rb(adc); + + // Power Up, Preconfigure and Calibrate + let adc = adc.power_up_and_calibrate(delay); + + // From RM0481: + // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected + adc.rb.or().modify(|_, w| w.op0().set_bit()); + + adc + } + + /// Initialise ADC + /// + /// Sets all configurable parameters to one-shot defaults, + /// performs a boot-time calibration. + #[cfg(feature = "rm0481")] + pub fn claim_and_configure( + &self, + adc: ADC, + delay: &mut impl DelayNs, + ) -> Adc { + Self::setup_adc(adc, delay) + } + + /// Initialise ADC + /// + /// Sets all configurable parameters to one-shot defaults, + /// performs a boot-time calibration. + #[cfg(feature = "rm0492")] + pub fn claim_and_configure( + self, + delay: &mut impl DelayNs, + ) -> Adc { + let adcc = unsafe { ADC1::steal() }; + Self::setup_adc::(adcc, delay) + } +} + #[cfg(feature = "defmt")] use defmt::{assert, panic}; @@ -76,8 +240,13 @@ impl NumberOfBits for Resolution { /// Enabled ADC (type state) pub struct Enabled; /// Disabled ADC (type state) +/// +/// Disabled but powered on pub struct Disabled; +/// Powered down ADC (type state) +pub struct PoweredDown; + pub trait ED {} impl ED for Enabled {} impl ED for Disabled {} @@ -86,7 +255,6 @@ pub struct Adc { rb: ADC, sample_time: AdcSampleTime, resolution: Resolution, - clock: Hertz, current_channel: Option, _enabled: PhantomData, } @@ -128,23 +296,6 @@ pub enum AdcSampleTime { T_640_5, } -impl AdcSampleTime { - /// Returns the number of half clock cycles represented by this sampling time - const fn clock_cycles_x2(&self) -> u32 { - let x = match self { - AdcSampleTime::T_2_5 => 2, - AdcSampleTime::T_6_5 => 6, - AdcSampleTime::T_12_5 => 12, - AdcSampleTime::T_24_5 => 24, - AdcSampleTime::T_47_5 => 47, - AdcSampleTime::T_92_5 => 92, - AdcSampleTime::T_247_5 => 247, - AdcSampleTime::T_640_5 => 640, - }; - (2 * x) + 1 - } -} - // Refer to RM0433 Rev 7 - Chapter 25.4.13 impl From for u8 { fn from(val: AdcSampleTime) -> u8 { @@ -248,230 +399,22 @@ fn kernel_clk_unwrap( } } -#[cfg(feature = "rm0492")] -pub fn adc1( - adc1: ADC1, - f_adc: impl Into, - delay: &mut impl DelayNs, - prec: rec::Adc, - clocks: &CoreClocks, - pwrcfg: &pwr::PowerConfiguration, -) -> Adc { - // Consume ADC register block, produce ADC1/2 with default settings - let mut adc1 = Adc::::default_from_rb(adc1); - - // Check adc_ker_ck_input - kernel_clk_unwrap(&prec, clocks); - - // Enable AHB clock - let prec = prec.enable(); - - // Power Down - adc1.power_down(); - - // Reset peripheral - let prec = prec.reset(); - - // Power Up, Preconfigure and Calibrate - adc1.power_up(delay); - adc1.configure_clock(f_adc.into(), prec, clocks, pwrcfg); // ADC12_COMMON - adc1.preconfigure(); - adc1.calibrate(); - - // From RM0481: - // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected - adc1.rb.or().modify(|_, w| w.op0().set_bit()); - - adc1 -} - -/// Initialise ADC12 together -/// -/// Sets all configurable parameters to one-shot defaults, -/// performs a boot-time calibration. -#[cfg(feature = "rm0481")] -pub fn adc12( - adc1: ADC1, - adc2: ADC2, - f_adc: impl Into, - delay: &mut impl DelayNs, - prec: rec::Adc, - clocks: &CoreClocks, - pwrcfg: &pwr::PowerConfiguration, -) -> (Adc, Adc) { - // Consume ADC register block, produce ADC1/2 with default settings - let mut adc1 = Adc::::default_from_rb(adc1); - let mut adc2 = Adc::::default_from_rb(adc2); - - // Check adc_ker_ck_input - kernel_clk_unwrap(&prec, clocks); - - // Enable AHB clock - let prec = prec.enable(); - - // Power Down - adc1.power_down(); - adc2.power_down(); - - // Reset peripheral - let prec = prec.reset(); - - // Power Up, Preconfigure and Calibrate - adc1.power_up(delay); - adc2.power_up(delay); - let f_adc = adc1.configure_clock(f_adc.into(), prec, clocks, pwrcfg); // ADC12_COMMON - adc2.clock = f_adc; - adc1.preconfigure(); - adc2.preconfigure(); - adc1.calibrate(); - adc2.calibrate(); - - // From RM0481: - // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected - adc1.rb.or().modify(|_, w| w.op0().set_bit()); - adc2.rb.or().modify(|_, w| w.op0().set_bit()); - - (adc1, adc2) -} - -impl AdcExt for ADC { - type Rec = rec::Adc; - - fn adc( - self, - f_adc: impl Into, - delay: &mut impl DelayNs, - prec: rec::Adc, - clocks: &CoreClocks, - pwrcfg: &pwr::PowerConfiguration, - ) -> Adc { - Adc::::new(self, f_adc, delay, prec, clocks, pwrcfg) - } -} - -impl Adc { - /// Initialise ADC - /// - /// Sets all configurable parameters to one-shot defaults, - /// performs a boot-time calibration. - pub fn new( - adc: ADC, - f_adc: impl Into, - delay: &mut impl DelayNs, - prec: rec::Adc, - clocks: &CoreClocks, - pwrcfg: &pwr::PowerConfiguration, - ) -> Self { - // Consume ADC register block, produce Self with default - // settings - let mut adc = Self::default_from_rb(adc); - - // Enable AHB clock - let prec = prec.enable(); - - // Power Down - adc.power_down(); - - // Reset peripheral - let prec = prec.reset(); - - // Power Up, Preconfigure and Calibrate - adc.power_up(delay); - adc.configure_clock(f_adc.into(), prec, clocks, pwrcfg); - adc.preconfigure(); - adc.calibrate(); - - adc - } +impl Adc { /// Creates ADC with default settings fn default_from_rb(rb: ADC) -> Self { Self { rb, sample_time: AdcSampleTime::default(), resolution: Resolution::TwelveBit, - clock: Hertz::from_raw(0), current_channel: None, _enabled: PhantomData, } } - /// Sets the clock configuration for this ADC. This is common - /// between ADC1 and ADC2, so the prec block is used to ensure - /// this method can only be called on one of the ADCs (or both, - /// using the [adc12](#method.adc12) method). - /// - /// Only `CKMODE[1:0]` = 0 is supported - fn configure_clock( - &mut self, - f_adc: Hertz, - prec: rec::Adc, - clocks: &CoreClocks, - pwrcfg: &pwr::PowerConfiguration, - ) -> Hertz { - let ker_ck_input = kernel_clk_unwrap(&prec, clocks); - - let max_adc_ker_ck_analog = 75_000_000; - let (max_ker_ck, max_ker_ck_input) = match pwrcfg.vos { - VoltageScale::Scale0 => (125_000_000, 250_000_000), - VoltageScale::Scale1 => (100_000_000, 200_000_000), - VoltageScale::Scale2 => (75_000_000, 150_000_000), - VoltageScale::Scale3 => (50_000_000, 100_000_000), - }; - assert!(ker_ck_input.raw() <= max_ker_ck_input, - "Kernel clock violates maximum frequency defined in Reference Manual. \ - Can result in erroneous ADC readings"); - - let f_adc = self.configure_clock_unchecked(f_adc, prec, clocks); - - // Maximum ADC clock speed. With BOOST = 0 there is a no - // minimum frequency given in part datasheets - assert!(f_adc.raw() <= max_ker_ck); - assert!(f_adc.raw() <= max_adc_ker_ck_analog); - - f_adc - } - - /// No clock checks - fn configure_clock_unchecked( - &mut self, - f_adc: Hertz, - prec: rec::Adc, - clocks: &CoreClocks, - ) -> Hertz { - let ker_ck = kernel_clk_unwrap(&prec, clocks); - - let f_target = f_adc.raw(); - - let (divider, presc) = match ker_ck.raw().div_ceil(f_target) { - 1 => (1, 0b0000), - 2 => (2, 0b0001), - 3..=4 => (4, 0b0010), - 5..=6 => (6, 0b0011), - 7..=8 => (8, 0b0100), - 9..=10 => (10, 0b0101), - 11..=12 => (12, 0b0110), - 13..=16 => (16, 0b0111), - 17..=32 => (32, 0b1000), - 33..=64 => (64, 0b1001), - 65..=128 => (128, 0b1010), - 129..=256 => (256, 0b1011), - _ => panic!("Selecting the ADC clock required a prescaler > 256, \ - which is not possible in hardware. Either increase the ADC \ - clock frequency or decrease the kernel clock frequency"), - }; - unsafe { ADCC::steal() } - .ccr() - .modify(|_, w| unsafe { w.presc().bits(presc) }); - - let f_adc = Hertz::from_raw(ker_ck.raw() / divider); - - self.clock = f_adc; - f_adc - } /// Disables Deeppowerdown-mode and enables voltage regulator /// /// Note: After power-up, a [`calibration`](#method.calibrate) shall be run - pub fn power_up(&mut self, delay: &mut impl DelayNs) { + fn power_up(&mut self, delay: &mut impl DelayNs) { // Refer to RM0433 Rev 7 - Chapter 25.4.6 self.rb .cr() @@ -479,20 +422,47 @@ impl Adc { delay.delay_us(10); } + pub fn power_up_and_calibrate( + mut self, + delay: &mut impl DelayNs, + ) -> Adc { + self.power_up(delay); + + let mut adc = Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + current_channel: self.current_channel, + _enabled: PhantomData, + }; + adc.calibrate(); + adc + } +} + +impl Adc { /// Enables Deeppowerdown-mode and disables voltage regulator /// /// Note: This resets the [`calibration`](#method.calibrate) of the ADC - pub fn power_down(&mut self) { + pub fn power_down(self) -> Adc { // Refer to RM0433 Rev 7 - Chapter 25.4.6 self.rb .cr() .modify(|_, w| w.deeppwd().set_bit().advregen().clear_bit()); + + Adc { + rb: self.rb, + sample_time: self.sample_time, + resolution: self.resolution, + current_channel: self.current_channel, + _enabled: PhantomData, + } } /// Calibrates the ADC in single channel mode /// /// Note: The ADC must be disabled - pub fn calibrate(&mut self) { + fn calibrate(&mut self) { // Refer to RM0433 Rev 7 - Chapter 25.4.8 self.check_calibration_conditions(); @@ -524,18 +494,6 @@ impl Adc { } } - /// Configuration process prior to enabling the ADC - /// - /// Note: the ADC must be disabled - fn preconfigure(&mut self) { - self.configure_channels_dif_mode(); - } - - /// Sets channels to single ended mode - fn configure_channels_dif_mode(&mut self) { - self.rb.difsel().reset(); - } - /// Configuration process immediately after enabling the ADC fn configure(&mut self) { // Single conversion mode, Software trigger @@ -561,7 +519,6 @@ impl Adc { rb: self.rb, sample_time: self.sample_time, resolution: self.resolution, - clock: self.clock, current_channel: None, _enabled: PhantomData, } @@ -697,7 +654,6 @@ impl Adc { rb: self.rb, sample_time: self.sample_time, resolution: self.resolution, - clock: self.clock, current_channel: None, _enabled: PhantomData, } @@ -724,37 +680,6 @@ impl Adc { cfg } - /// The current ADC clock frequency. Defined as f_ADC in device datasheets - /// - /// The value returned by this method will always be equal or - /// lower than the `f_adc` passed to [`init`](#method.init) - pub fn clock_frequency(&self) -> Hertz { - self.clock - } - - /// The current ADC sampling frequency. This is the reciprocal of Tconv - pub const fn sampling_frequency(&self) -> Hertz { - let sample_cycles_x2 = self.sample_time.clock_cycles_x2(); - - // TODO: Exception for RM0468 ADC3 - // let sar_cycles_x2 = match self.resolution { - // Resolution::SixBit => 13, // 6.5 - // Resolution::EightBit => 17, // 8.5 - // Resolution::TenBit => 21, // 10.5 - // _ => 25, // 12.5 - // }; - - let sar_cycles = match self.resolution { - Resolution::SixBit => 6, - Resolution::EightBit => 8, - Resolution::TenBit => 10, - Resolution::TwelveBit => 12, - }; - - let cycles = sample_cycles_x2.div_ceil(2) + sar_cycles; - Hertz::Hz(self.clock.to_Hz() / cycles) - } - /// Get ADC samping time pub fn get_sample_time(&self) -> AdcSampleTime { self.sample_time From 92e78e4cc5fe956efc8e65450cf3e59dcb99b2fa Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 7 Sep 2025 17:51:52 +0200 Subject: [PATCH 10/12] Cleanup and put eh-02 behind feature --- Cargo.toml | 8 +- examples/adc.rs | 51 ++++----- examples/adc12.rs | 34 +++--- src/adc/h5.rs | 23 ++-- src/adc/mod.rs | 269 +++++++++++++++++++++++++++++++--------------- src/lib.rs | 2 +- 6 files changed, 242 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 086b27a..4252e51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ defmt = [ "stm32h5/defmt", ] -adc = ["dep:embedded-hal-02", "dep:nb"] +eh-02 = ["dep:embedded-hal-02", "dep:nb"] [dependencies] cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] } @@ -122,11 +122,7 @@ required-features = ["stm32h503"] name = "i2c_target_manual_ack" required-features = ["stm32h503"] -[[example]] -name = "adc" -required-features = ["adc"] - [[example]] name = "adc12" -required-features = ["adc", "rm0481"] +required-features = ["rm0481"] diff --git a/examples/adc.rs b/examples/adc.rs index 8687978..3ff03de 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -7,9 +7,8 @@ use cortex_m_rt::entry; -use embedded_hal_02::adc::OneShot; use stm32h5xx_hal::{ - adc::{self, AdcCommonExt}, + adc::{self, AdcCommonExt, AdcSampleTime}, delay::Delay, pac, prelude::*, @@ -56,33 +55,27 @@ fn main() -> ! { let mut delay = Delay::new(cp.SYST, &ccdr.clocks); #[cfg(feature = "rm0481")] - let adc = dp - .ADCC - .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg) - .claim_and_configure(dp.ADC1, &mut delay); + let mut adcc = + dp.ADCC + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg); #[cfg(feature = "rm0492")] - let adc = dp - .ADC1 - .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg) - .claim_and_configure(&mut delay); - - // Setup ADC - let mut adc1 = adc::Adc::new( - dp.ADC1, - 4.MHz(), - &mut delay, - ccdr.peripheral.ADC, - &ccdr.clocks, - &pwrcfg, - ); + let mut adcc = + dp.ADC1 + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg); let mut temp = adc::Temperature::new(); - temp.enable(&mut adc1); - let mut adc1: adc::Adc< - stm32h5::Periph, - adc::Enabled, - > = adc1.enable(); + temp.enable(&mut adcc); + + #[cfg(feature = "rm0481")] + let mut adc = adcc + .claim_and_configure(dp.ADC1, &mut delay, adc::Resolution::TwelveBit) + .enable(); + + #[cfg(feature = "rm0492")] + let mut adc = adcc + .claim_and_configure(&mut delay, adc::Resolution::TwelveBit) + .enable(); // We can't use ADC2 here because ccdr.peripheral.ADC12 has been // consumed. See examples/adc12.rs @@ -91,16 +84,16 @@ fn main() -> ! { let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); // Configure pc0 as an analog input - let mut channel = gpioc.pc0.into_analog(); // ANALOG IN 10 + let pin = gpioc.pc0.into_analog(); loop { - let data = adc1.read(&mut channel).unwrap(); + let data = adc.convert(&pin, AdcSampleTime::default()); // voltage = reading * (vref/resolution) info!( "ADC reading: {}, voltage for nucleo: {}V. Temp reading: {}", data, - data as f32 * (3.3 / adc1.slope() as f32), - adc1.read(&mut temp).unwrap() + data as f32 * (3.3 / adc.slope() as f32), + adc.convert(&temp, AdcSampleTime::default()) ); } } diff --git a/examples/adc12.rs b/examples/adc12.rs index 53e1e33..460a740 100644 --- a/examples/adc12.rs +++ b/examples/adc12.rs @@ -9,9 +9,12 @@ use cortex_m_rt::entry; -use embedded_hal_02::adc::OneShot; use stm32h5xx_hal::{ - adc, delay::Delay, pac, prelude::*, rcc::rec::AdcDacClkSel, + adc::{self, AdcCommonExt, AdcSampleTime}, + delay::Delay, + pac, + prelude::*, + rcc::rec::AdcDacClkSel, }; use utilities::logger::info; @@ -53,16 +56,21 @@ fn main() -> ! { let mut delay = Delay::new(cp.SYST, &ccdr.clocks); - // Setup ADC - // Setup ADC1 and ADC2 - let (adc1, adc2) = adc::adc12( + // Setup adc common + let adcc = + dp.ADCC + .claim(4.MHz(), ccdr.peripheral.ADC, &ccdr.clocks, &pwrcfg); + + // Set up individual adc's + let adc1 = adcc.claim_and_configure( dp.ADC1, + &mut delay, + adc::Resolution::TwelveBit, + ); + let adc2 = adcc.claim_and_configure( dp.ADC2, - 4.MHz(), &mut delay, - ccdr.peripheral.ADC, - &ccdr.clocks, - &pwrcfg, + adc::Resolution::TwelveBit, ); let mut adc1 = adc1.enable(); @@ -71,12 +79,12 @@ fn main() -> ! { // Setup GPIOC // NOTE: PC2 and PC3 are only pinned out on TFBGA packages!! let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); - let mut channel_pc2 = gpioc.pc2.into_analog(); // AIN 12 - let mut channel_pc3 = gpioc.pc3.into_analog(); // AIN 13 + let pc2 = gpioc.pc2.into_analog(); // AIN 12 + let pc3 = gpioc.pc3.into_analog(); // AIN 13 loop { - let data_pc2 = adc1.read(&mut channel_pc2).unwrap(); - let data_pc3 = adc2.read(&mut channel_pc3).unwrap(); + let data_pc2 = adc1.convert(&pc2, AdcSampleTime::default()); + let data_pc3 = adc2.convert(&pc3, AdcSampleTime::default()); // voltage = reading * (vref/resolution) info!("ADC readings: {} {}", data_pc2, data_pc3); } diff --git a/src/adc/h5.rs b/src/adc/h5.rs index 16284e3..a41750d 100644 --- a/src/adc/h5.rs +++ b/src/adc/h5.rs @@ -1,4 +1,4 @@ -use super::{Adc, Disabled, Temperature, Vbat, Vddcore, Vrefint}; +use super::{AdcCommon, Temperature, Vbat, Vddcore, Vrefint}; use crate::gpio::{self, Analog}; #[cfg(feature = "rm0492")] @@ -10,6 +10,7 @@ use crate::stm32::{ADC1, ADC2, ADCC}; macro_rules! adc_pins { ($ADC:ident, $($input:ty => $chan:expr),+ $(,)*) => { $( + #[cfg(feature = "eh-02")] impl embedded_hal_02::adc::Channel<$ADC> for $input { type ID = u8; @@ -17,6 +18,10 @@ macro_rules! adc_pins { $chan } } + + impl super::AdcChannel<$ADC> for $input { + const CH: u8 = $chan; + } )+ }; } @@ -31,7 +36,7 @@ macro_rules! adc_internal { /// Enables the internal voltage/sensor /// ADC must be disabled. - pub fn enable(&mut self, _adc: &mut Adc<$INT_ADC, Disabled>) { + pub fn enable(&mut self, _adc: &mut AdcCommon) { // TODO: This is not safe since we do not hold both adcs let common = unsafe { ADCC::steal() }; @@ -39,7 +44,7 @@ macro_rules! adc_internal { } /// Disables the internal voltage/sdissor /// ADC must be disabled. - pub fn disable(&mut self, _adc: &mut Adc<$INT_ADC, Disabled>) { + pub fn disable(&mut self, _adc: &mut AdcCommon) { // TODO: This is not safe since we do not hold both adcs let common = unsafe { ADCC::steal() }; @@ -54,13 +59,13 @@ macro_rules! adc_internal { #[cfg(feature = "rm0492")] impl Vddcore { - pub fn enable(_adc: &mut Adc) { + pub fn enable(_adc: &mut AdcCommon) { let adc = unsafe { ADC1::steal() }; adc.or().modify(|_, w| w.op1().set_bit()); } - pub fn disable(_adc: &mut Adc) { + pub fn disable(_adc: &mut AdcCommon) { let adc = unsafe { ADC1::steal() }; adc.or().modify(|_, w| w.op1().clear_bit()); @@ -71,14 +76,14 @@ adc_pins!(ADC1, Vddcore => 16); #[cfg(feature = "rm0481")] impl Vddcore { - pub fn enable(_adc: &mut Adc) { - let adc2 = unsafe { ADC1::steal() }; + pub fn enable(_adc: &mut super::Adc) { + let adc2 = unsafe { ADC2::steal() }; adc2.or().modify(|_, w| w.op0().bit(true)); } - pub fn disable(_adc: &mut Adc) { - let adc2 = unsafe { ADC1::steal() }; + pub fn disable(_adc: &mut super::Adc) { + let adc2 = unsafe { ADC2::steal() }; adc2.or().modify(|_, w| w.op0().bit(false)); } diff --git a/src/adc/mod.rs b/src/adc/mod.rs index d0d0555..874ba34 100644 --- a/src/adc/mod.rs +++ b/src/adc/mod.rs @@ -11,6 +11,7 @@ //! Originally from https://github.com/stm32-rs/stm32h7xx-hal mod h5; +#[cfg(feature = "eh-02")] use core::convert::Infallible; use core::marker::PhantomData; use core::ops::Deref; @@ -73,13 +74,8 @@ impl AdcCommonExt for ADCC { // Reset peripheral let prec = prec.reset(); - let _f_adc = AdcCommon::configure_clock( - &self, - f_adc.into(), - prec, - clocks, - pwrcfg, - ); // ADC12_COMMON + let _f_adc = + AdcCommon::configure_clock(&self, f_adc, prec, clocks, pwrcfg); // ADC12_COMMON #[cfg(feature = "defmt")] defmt::trace!("Set f_adc to: {}", _f_adc); #[cfg(feature = "log")] @@ -162,24 +158,30 @@ impl AdcCommon { }; adcc.ccr().modify(|_, w| unsafe { w.presc().bits(presc) }); - let f_adc = Hertz::from_raw(ker_ck.raw() / divider); - f_adc + Hertz::from_raw(ker_ck.raw() / divider) } fn setup_adc( adc: ADC, delay: &mut impl DelayNs, + resolution: Resolution, ) -> Adc { // Consume ADC register block, produce ADC1/2 with default settings let adc = Adc::::default_from_rb(adc); // Power Up, Preconfigure and Calibrate - let adc = adc.power_up_and_calibrate(delay); + let mut adc = adc.power_up_and_calibrate(delay); // From RM0481: // This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected adc.rb.or().modify(|_, w| w.op0().set_bit()); + // Set resolution + adc.rb + .cfgr() + .modify(|_, w| unsafe { w.res().bits(resolution as _) }); + + adc.set_resolution(resolution); adc } @@ -192,8 +194,9 @@ impl AdcCommon { &self, adc: ADC, delay: &mut impl DelayNs, + resolution: Resolution, ) -> Adc { - Self::setup_adc(adc, delay) + Self::setup_adc(adc, delay, resolution) } /// Initialise ADC @@ -204,9 +207,10 @@ impl AdcCommon { pub fn claim_and_configure( self, delay: &mut impl DelayNs, + resolution: Resolution, ) -> Adc { let adcc = unsafe { ADC1::steal() }; - Self::setup_adc::(adcc, delay) + Self::setup_adc::(adcc, delay, resolution) } } @@ -296,6 +300,90 @@ pub enum AdcSampleTime { T_640_5, } +/// 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!(), + } + } +} + // Refer to RM0433 Rev 7 - Chapter 25.4.13 impl From for u8 { fn from(val: AdcSampleTime) -> u8 { @@ -345,19 +433,6 @@ pub struct Temperature; #[derive(Default)] pub struct Vddcore; -pub trait AdcExt: Sized { - type Rec: ResetEnable; - - fn adc( - self, - f_adc: impl Into, - delay: &mut impl DelayNs, - prec: Self::Rec, - clocks: &CoreClocks, - pwrcfg: &pwr::PowerConfiguration, - ) -> Adc; -} - /// Stored ADC config can be restored using the `Adc::restore_cfg` method #[derive(Copy, Clone, Debug, PartialEq)] pub struct StoredConfig(AdcSampleTime, Resolution); @@ -370,6 +445,10 @@ impl defmt::Format for StoredConfig { } } +pub trait AdcChannel { + const CH: u8; +} + /// Returns the frequency of the current adc_ker_ck /// /// # Panics @@ -536,55 +615,85 @@ impl Adc { while self.rb.cr().read().jadstp().bit_is_set() {} } - fn set_chan_smp(&mut self, chan: u8) { - let t = self.get_sample_time().into(); - if chan <= 9 { - self.rb.smpr1().modify(|_, w| w.smp(chan).set(t)); - } else { - self.rb.smpr2().modify(|_, w| w.smp(chan - 10).set(t)); + pub fn configure_ch( + &mut self, + ch: u8, + sequence: Sequence, + sample_time: AdcSampleTime, + ) { + //Check the sequence is long enough + self.rb.sqr1().modify(|r, w| unsafe { + let prev: Sequence = r.l().bits().into(); + if prev < sequence { + w.l().bits(sequence.into()) + } else { + w + } + }); + 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.rb.sqr1().modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 1 => self.rb.sqr2().modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 2 => self.rb.sqr3().modify(|_, w| unsafe { w.sq(i).bits(ch) }), + 3 => self.rb.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.rb.smpr1().modify(|_, w| w.smp(ch).bits(st)), + 10.. => self.rb.smpr2().modify(|_, w| w.smp(ch - 10).bits(st)), + }; } } - // This method starts a conversion sequence on the given channel - fn start_conversion_common(&mut self, chan: u8) { - self.check_conversion_conditions(); - - // Select channel (with preselection, refer to RM0433 Rev 7 - Chapter 25.4.12) - self.rb.sqr1().modify(|_, w| unsafe { w.sq1().bits(chan) }); - self.set_chan_smp(chan); - self.rb - .sqr1() - .modify(|_, w| unsafe { w.sq1().bits(chan).l().bits(0) }); - self.current_channel = Some(chan); - - // Perform conversion - self.rb.cr().modify(|_, w| w.adstart().set_bit()); + /// 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: Sequence, + sample_time: AdcSampleTime, + ) where + CHANNEL: AdcChannel, + { + self.configure_ch(CHANNEL::CH, sequence, sample_time); } /// Start conversion /// - /// This method starts a conversion sequence on the given pin. - /// The value can be then read through the `read_sample` method. - // Refer to RM0433 Rev 7 - Chapter 25.4.16 - pub fn start_conversion(&mut self, _pin: &mut PIN) - where - PIN: embedded_hal_02::adc::Channel, - { - let chan = PIN::channel(); - assert!(chan <= 19); - - // TODO: Move to ADC init? - // Set resolution - self.rb - .cfgr() - .modify(|_, w| unsafe { w.res().bits(self.get_resolution() as _) }); + /// This method starts a conversion sequence + #[inline(always)] + fn start_conversion(&mut self) { + //Start conversion + self.rb.cr().modify(|_, w| w.adstart().set_bit()); + } - // TODO: Move to ADC init? - self.rb - .cfgr() - .modify(|_, w| w.cont().single().discen().disabled()); + /// Block until the conversion is completed and return to configured + pub fn wait_for_conversion_sequence(&mut self) { + while !self.rb.isr().read().eoc().bit_is_set() {} + } - self.start_conversion_common(chan); + pub fn convert>( + &mut self, + pin: &PIN, + sample_time: AdcSampleTime, + ) -> u16 { + self.configure_channel(pin, Sequence::One, sample_time); + self.start_conversion(); + //Wait for the sequence to complete + self.wait_for_conversion_sequence(); + self.current_sample() } /// Read sample @@ -592,6 +701,7 @@ impl Adc { /// `nb::Error::WouldBlock` in case the conversion is still /// progressing. // Refer to RM0433 Rev 7 - Chapter 25.4.16 + #[cfg(feature = "eh-02")] pub fn read_sample(&mut self) -> nb::Result { self.current_channel .expect("No channel was selected, use start_conversion first"); @@ -618,24 +728,6 @@ impl Adc { self.rb.dr().read().rdata().bits() } - fn check_conversion_conditions(&self) { - let cr = self.rb.cr().read(); - // Ensure that no conversions are ongoing - if cr.adstart().bit_is_set() { - panic!("Cannot start conversion because a regular conversion is ongoing"); - } - if cr.jadstart().bit_is_set() { - panic!("Cannot start conversion because an injected conversion is ongoing"); - } - // Ensure that the ADC is enabled - if cr.aden().bit_is_clear() { - panic!("Cannot start conversion because ADC is currently disabled"); - } - if cr.addis().bit_is_set() { - panic!("Cannot start conversion because there is a pending request to disable the ADC"); - } - } - /// Disable ADC pub fn disable(mut self) -> Adc { let cr = self.rb.cr().read(); @@ -742,6 +834,7 @@ impl Adc { } } +#[cfg(feature = "eh-02")] impl embedded_hal_02::adc::OneShot for Adc where @@ -750,9 +843,11 @@ where type Error = Infallible; // TODO: We are not really non-blocking - fn read(&mut self, pin: &mut PIN) -> nb::Result { - self.start_conversion(pin); - let res = nb::block!(self.read_sample()).unwrap(); - Ok(res) + fn read(&mut self, _pin: &mut PIN) -> nb::Result { + self.configure_ch(PIN::channel(), Sequence::One, self.sample_time); + self.start_conversion(); + //Wait for the sequence to complete + self.wait_for_conversion_sequence(); + Ok(self.current_sample()) } } diff --git a/src/lib.rs b/src/lib.rs index 85b90db..ed4356c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ pub mod prelude; #[macro_use] mod macros; -#[cfg(all(feature = "device-selected", feature = "adc"))] +#[cfg(feature = "device-selected")] pub mod adc; #[cfg(feature = "device-selected")] From 806715fb786509b9373283cd9d360250dd19c0c2 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 7 Sep 2025 17:53:26 +0200 Subject: [PATCH 11/12] Update CI --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b36a53a..aad6f26 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,8 +23,8 @@ jobs: logger: - log - defmt - adc: - - adc + eh-02: + - eh-02 - env: # Peripheral Feature flags FLAGS: rt @@ -48,6 +48,6 @@ jobs: - name: Install thumbv8m rust target run: rustup target add thumbv8m.main-none-eabihf - name: Build - run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.adc }},${{ env.FLAGS }} + run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.eh-02 }},${{ env.FLAGS }} - name: Test - run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.adc }},${{ env.FLAGS }} + run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ matrix.eh-02 }},${{ env.FLAGS }} From e202591369e0adff85a519249ed28d619c1bbaff Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 7 Sep 2025 18:08:40 +0200 Subject: [PATCH 12/12] ADC - Add pins for 56x and 573 --- src/adc/h5.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/adc/h5.rs b/src/adc/h5.rs index a41750d..a481982 100644 --- a/src/adc/h5.rs +++ b/src/adc/h5.rs @@ -124,11 +124,6 @@ macro_rules! adc_pins_common { )*}; } -#[cfg(any( - feature = "stm32h503", - feature = "stm32h523", - feature = "stm32h533" -))] adc_pins_common!( gpio::PC0 => 10, gpio::PC1 => 11, @@ -150,15 +145,14 @@ adc_pins_common!( gpio::PB1 => 5, ); -#[cfg(feature = "stm32h523")] +#[cfg(feature = "rm0481")] adc_pins!( ADC1, gpio::PF11 => 2, gpio::PF12 => 6, - ); -#[cfg(feature = "stm32h523")] +#[cfg(feature = "rm0481")] adc_pins!( ADC2, gpio::PF13 => 2,