diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 2c8409a3..9125155c 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -145,7 +145,10 @@ fn main() { // Generate RccPeripheral impls for p in METADATA.peripherals { - if !singletons.contains(&p.name.to_string()) { + // generating RccPeripheral impl for H7 ADC3 would result in bad frequency + if !singletons.contains(&p.name.to_string()) + || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) + { continue; } @@ -459,11 +462,23 @@ fn main() { if regs.kind == "adc" { let peri = format_ident!("{}", p.name); let pin_name = format_ident!("{}", pin.pin); - let ch: u8 = pin.signal.strip_prefix("IN").unwrap().parse().unwrap(); - g.extend(quote! { - impl_adc_pin!( #peri, #pin_name, #ch); - }) + // H7 has differential voltage measurements + let ch: Option = if pin.signal.starts_with("INP") { + Some(pin.signal.strip_prefix("INP").unwrap().parse().unwrap()) + } else if pin.signal.starts_with("INN") { + // TODO handle in the future when embassy supports differential measurements + None + } else if pin.signal.starts_with("IN") { + Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap()) + } else { + None + }; + if let Some(ch) = ch { + g.extend(quote! { + impl_adc_pin!( #peri, #pin_name, #ch); + }) + } } // DAC is special diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 7b3233a1..8da13073 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,9 +1,11 @@ #![macro_use] +#[cfg_attr(adc_v4, path = "v4.rs")] #[cfg_attr(adc_v3, path = "v3.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(adc_g0, path = "v3.rs")] #[cfg_attr(adc_f1, path = "f1.rs")] +#[cfg_attr(adc_v1, path = "v1.rs")] mod _version; #[allow(unused)] @@ -14,11 +16,11 @@ use crate::peripherals; pub(crate) mod sealed { pub trait Instance { fn regs() -> &'static crate::pac::adc::Adc; - #[cfg(not(adc_f1))] + #[cfg(all(not(adc_f1), not(adc_v1)))] fn common_regs() -> &'static crate::pac::adccommon::AdcCommon; } - #[cfg(not(adc_f1))] + #[cfg(all(not(adc_f1), not(adc_v1)))] pub trait Common { fn regs() -> &'static crate::pac::adccommon::AdcCommon; } @@ -32,17 +34,18 @@ pub(crate) mod sealed { pub trait Instance: sealed::Instance + 'static {} #[cfg(adc_f1)] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} -#[cfg(not(adc_f1))] +#[cfg(all(not(adc_f1), not(adc_v1)))] pub trait Common: sealed::Common + 'static {} pub trait AdcPin: sealed::AdcPin {} +#[cfg(not(stm32h7))] foreach_peripheral!( (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { fn regs() -> &'static crate::pac::adc::Adc { &crate::pac::$inst } - #[cfg(not(adc_f1))] + #[cfg(all(not(adc_f1), not(adc_v1)))] fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { @@ -56,7 +59,45 @@ foreach_peripheral!( }; ); -#[cfg(not(adc_f1))] +#[cfg(stm32h7)] +foreach_peripheral!( + (adc, ADC3) => { + impl crate::adc::sealed::Instance for peripherals::ADC3 { + fn regs() -> &'static crate::pac::adc::Adc { + &crate::pac::ADC3 + } + #[cfg(all(not(adc_f1), not(adc_v1)))] + fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + foreach_peripheral!{ + (adccommon, ADC3_COMMON) => { + return &crate::pac::ADC3_COMMON + }; + } + } + } + + impl crate::adc::Instance for peripherals::ADC3 {} + }; + (adc, $inst:ident) => { + impl crate::adc::sealed::Instance for peripherals::$inst { + fn regs() -> &'static crate::pac::adc::Adc { + &crate::pac::$inst + } + #[cfg(all(not(adc_f1), not(adc_v1)))] + fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + foreach_peripheral!{ + (adccommon, ADC_COMMON) => { + return &crate::pac::ADC_COMMON + }; + } + } + } + + impl crate::adc::Instance for peripherals::$inst {} + }; +); + +#[cfg(all(not(adc_f1), not(adc_v1)))] foreach_peripheral!( (adccommon, $inst:ident) => { impl sealed::Common for peripherals::$inst { diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/embassy-stm32/src/adc/v1.rs @@ -0,0 +1 @@ + diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs new file mode 100644 index 00000000..11df9677 --- /dev/null +++ b/embassy-stm32/src/adc/v4.rs @@ -0,0 +1,542 @@ +use core::marker::PhantomData; + +use crate::time::{Hertz, U32Ext}; +use atomic_polyfill::AtomicU8; +use atomic_polyfill::Ordering; +use embassy::util::Unborrow; +use embedded_hal_02::blocking::delay::DelayUs; +use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; +use pac::adccommon::vals::Presc; + +use crate::pac; + +use super::{AdcPin, Instance}; + +pub enum Resolution { + SixteenBit, + FourteenBit, + TwelveBit, + TenBit, + EightBit, +} + +impl Default for Resolution { + fn default() -> Self { + Self::SixteenBit + } +} + +impl Resolution { + fn res(&self) -> pac::adc::vals::Res { + match self { + Resolution::SixteenBit => pac::adc::vals::Res::SIXTEENBIT, + Resolution::FourteenBit => pac::adc::vals::Res::FOURTEENBITV, + Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBITV, + Resolution::TenBit => pac::adc::vals::Res::TENBIT, + Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, + } + } + + pub fn to_max_count(&self) -> u32 { + match self { + Resolution::SixteenBit => (1 << 16) - 1, + Resolution::FourteenBit => (1 << 14) - 1, + Resolution::TwelveBit => (1 << 12) - 1, + Resolution::TenBit => (1 << 10) - 1, + Resolution::EightBit => (1 << 8) - 1, + } + } +} + +pub trait InternalChannel: sealed::InternalChannel {} + +mod sealed { + pub trait InternalChannel { + fn channel(&self) -> u8; + } +} + +// NOTE: Vref/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs +pub struct Vref; +impl InternalChannel for Vref {} +impl sealed::InternalChannel for Vref { + fn channel(&self) -> u8 { + 19 + } +} + +pub struct Temperature; +impl InternalChannel for Temperature {} +impl sealed::InternalChannel for Temperature { + fn channel(&self) -> u8 { + 18 + } +} + +pub struct Vbat; +impl InternalChannel for Vbat {} +impl sealed::InternalChannel for Vbat { + fn channel(&self) -> u8 { + // TODO this should be 14 for H7a/b/35 + 17 + } +} + +static ADC12_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0); + +#[cfg(stm32h7)] +foreach_peripheral!( + (adc, ADC1) => { + impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 { + fn frequency() -> crate::time::Hertz { + critical_section::with(|_| unsafe { + match crate::rcc::get_freqs().adc { + Some(ck) => ck, + None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") + } + }) + } + + fn enable() { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) + }); + ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); + } + + fn disable() { + if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); + }) + } + ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); + } + + fn reset() { + if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); + crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); + }); + } + } + } + + impl crate::rcc::RccPeripheral for crate::peripherals::ADC1 {} + }; + (adc, ADC2) => { + impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 { + fn frequency() -> crate::time::Hertz { + critical_section::with(|_| unsafe { + match crate::rcc::get_freqs().adc { + Some(ck) => ck, + None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") + } + }) + } + + fn enable() { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) + }); + ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); + } + + fn disable() { + if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); + }) + } + ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst); + } + + fn reset() { + if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); + crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); + }); + } + } + } + + impl crate::rcc::RccPeripheral for crate::peripherals::ADC2 {} + }; + (adc, ADC3) => { + impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 { + fn frequency() -> crate::time::Hertz { + critical_section::with(|_| unsafe { + match crate::rcc::get_freqs().adc { + Some(ck) => ck, + None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") + } + }) + } + + fn enable() { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true)) + }); + } + + fn disable() { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false)); + }) + } + + fn reset() { + critical_section::with(|_| unsafe { + crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true)); + crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false)); + }); + } + } + + impl crate::rcc::RccPeripheral for crate::peripherals::ADC3 {} + }; +); + +/// ADC sample time +/// +/// The default setting is 2.5 ADC clock cycles. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub enum SampleTime { + /// 1.5 ADC clock cycles + Cycles1_5, + + /// 2.5 ADC clock cycles + Cycles2_5, + + /// 8.5 ADC clock cycles + Cycles8_5, + + /// 16.5 ADC clock cycles + Cycles16_5, + + /// 32.5 ADC clock cycles + Cycles32_5, + + /// 64.5 ADC clock cycles + Cycles64_5, + + /// 387.5 ADC clock cycles + Cycles387_5, + + /// 810.5 ADC clock cycles + Cycles810_5, +} + +impl SampleTime { + pub(crate) fn sample_time(&self) -> pac::adc::vals::Smp { + match self { + SampleTime::Cycles1_5 => pac::adc::vals::Smp::CYCLES1_5, + SampleTime::Cycles2_5 => pac::adc::vals::Smp::CYCLES2_5, + SampleTime::Cycles8_5 => pac::adc::vals::Smp::CYCLES8_5, + SampleTime::Cycles16_5 => pac::adc::vals::Smp::CYCLES16_5, + SampleTime::Cycles32_5 => pac::adc::vals::Smp::CYCLES32_5, + SampleTime::Cycles64_5 => pac::adc::vals::Smp::CYCLES64_5, + SampleTime::Cycles387_5 => pac::adc::vals::Smp::CYCLES387_5, + SampleTime::Cycles810_5 => pac::adc::vals::Smp::CYCLES810_5, + } + } +} + +impl Default for SampleTime { + fn default() -> Self { + Self::Cycles1_5 + } +} + +// NOTE (unused): The prescaler enum closely copies the hardware capabilities, +// but high prescaling doesn't make a lot of sense in the current implementation and is ommited. +#[allow(unused)] +enum Prescaler { + NotDivided, + DividedBy2, + DividedBy4, + DividedBy6, + DividedBy8, + DividedBy10, + DividedBy12, + DividedBy16, + DividedBy32, + DividedBy64, + DividedBy128, + DividedBy256, +} + +impl Prescaler { + fn from_ker_ck(frequency: Hertz) -> Self { + let raw_prescaler = frequency.0 / 50_000_000; + match raw_prescaler { + 0 => Self::NotDivided, + 1 => Self::DividedBy2, + 2..=3 => Self::DividedBy4, + 4..=5 => Self::DividedBy6, + 6..=7 => Self::DividedBy8, + 8..=9 => Self::DividedBy10, + 10..=11 => Self::DividedBy12, + _ => unimplemented!(), + } + } + + fn divisor(&self) -> u32 { + match self { + Prescaler::NotDivided => 1, + Prescaler::DividedBy2 => 2, + Prescaler::DividedBy4 => 4, + Prescaler::DividedBy6 => 6, + Prescaler::DividedBy8 => 8, + Prescaler::DividedBy10 => 10, + Prescaler::DividedBy12 => 12, + Prescaler::DividedBy16 => 16, + Prescaler::DividedBy32 => 32, + Prescaler::DividedBy64 => 64, + Prescaler::DividedBy128 => 128, + Prescaler::DividedBy256 => 256, + } + } + + fn presc(&self) -> Presc { + match self { + Prescaler::NotDivided => Presc::DIV1, + Prescaler::DividedBy2 => Presc::DIV2, + Prescaler::DividedBy4 => Presc::DIV4, + Prescaler::DividedBy6 => Presc::DIV6, + Prescaler::DividedBy8 => Presc::DIV8, + Prescaler::DividedBy10 => Presc::DIV10, + Prescaler::DividedBy12 => Presc::DIV12, + Prescaler::DividedBy16 => Presc::DIV16, + Prescaler::DividedBy32 => Presc::DIV32, + Prescaler::DividedBy64 => Presc::DIV64, + Prescaler::DividedBy128 => Presc::DIV128, + Prescaler::DividedBy256 => Presc::DIV256, + } + } +} + +pub struct Adc<'d, T: Instance> { + sample_time: SampleTime, + resolution: Resolution, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { + pub fn new(_peri: impl Unborrow + 'd, delay: &mut impl DelayUs) -> Self { + embassy_hal_common::unborrow!(_peri); + T::enable(); + T::reset(); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + unsafe { + T::common_regs() + .ccr() + .modify(|w| w.set_presc(prescaler.presc())); + } + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + defmt::info!("ADC frequency set to {} Hz", frequency.0); + + if frequency > 50.mhz().into() { + panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information."); + } + let boost = if frequency < 6_250.khz().into() { + Boost::LT6_25 + } else if frequency < 12_500.khz().into() { + Boost::LT12_5 + } else if frequency < 25.mhz().into() { + Boost::LT25 + } else { + Boost::LT50 + }; + unsafe { + T::regs().cr().modify(|w| w.set_boost(boost)); + } + + let mut s = Self { + sample_time: Default::default(), + resolution: Resolution::default(), + phantom: PhantomData, + }; + s.power_up(delay); + s.configure_differential_inputs(); + + s.calibrate(); + delay.delay_us(1); + + s.enable(); + s.configure(); + + s + } + + fn power_up(&mut self, delay: &mut impl DelayUs) { + unsafe { + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + } + + delay.delay_us(10); + } + + fn configure_differential_inputs(&mut self) { + unsafe { + T::regs().difsel().modify(|w| { + for n in 0..20 { + w.set_difsel(n, Difsel::SINGLEENDED); + } + }) + }; + } + + fn calibrate(&mut self) { + unsafe { + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::SINGLEENDED); + w.set_adcallin(true); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + } + } + + fn enable(&mut self) { + unsafe { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); + } + } + + fn configure(&mut self) { + // single conversion mode, software trigger + unsafe { + T::regs().cfgr().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + }) + } + } + + pub fn enable_vref(&self) -> Vref { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); + } + + Vref {} + } + + pub fn enable_temperature(&self) -> Temperature { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); + }); + } + + Temperature {} + } + + pub fn enable_vbat(&self) -> Vbat { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); + } + + Vbat {} + } + + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + pub fn set_resolution(&mut self, resolution: Resolution) { + self.resolution = resolution; + } + + /// Convert a measurement to millivolts + pub fn to_millivolts(&self, sample: u16) -> u16 { + ((u32::from(sample) * 3300) / self.resolution.to_max_count()) as u16 + } + + /// Perform a single conversion. + fn convert(&mut self) -> u16 { + unsafe { + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + while !T::regs().isr().read().eos() { + // spin + } + + T::regs().dr().read().0 as u16 + } + } + + pub fn read

(&mut self, pin: &mut P) -> u16 + where + P: AdcPin, + P: crate::gpio::sealed::Pin, + { + unsafe { + pin.set_as_analog(); + + self.read_channel(pin.channel()) + } + } + + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + unsafe { self.read_channel(channel.channel()) } + } + + unsafe fn read_channel(&mut self, channel: u8) -> u16 { + // Configure ADC + T::regs() + .cfgr() + .modify(|reg| reg.set_res(self.resolution.res())); + + // Configure channel + Self::set_channel_sample_time(channel, self.sample_time); + + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + T::regs().sqr1().write(|reg| { + reg.set_sq(0, channel); + reg.set_l(0); + }); + + self.convert() + } + + unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + if ch <= 9 { + T::regs() + .smpr(0) + .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + } else { + T::regs() + .smpr(1) + .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + } + } +} diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index d2668525..6c81d3db 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -7,7 +7,7 @@ use stm32_metapac::rcc::vals::{Mco1, Mco2}; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::pac::rcc::vals::Timpre; -use crate::pac::rcc::vals::{Ckpersel, Dppre, Hpre, Hsidiv, Pllsrc, Sw}; +use crate::pac::rcc::vals::{Adcsel, Ckpersel, Dppre, Hpre, Hsidiv, Pllsrc, Sw}; use crate::pac::{PWR, RCC, SYSCFG}; use crate::peripherals; use crate::rcc::{set_freqs, Clocks}; @@ -36,6 +36,29 @@ pub enum VoltageScale { Scale3, } +#[derive(Clone, Copy)] +pub enum AdcClockSource { + Pll2PCk, + Pll3RCk, + PerCk, +} + +impl AdcClockSource { + pub fn adcsel(&self) -> Adcsel { + match self { + AdcClockSource::Pll2PCk => Adcsel::PLL2_P, + AdcClockSource::Pll3RCk => Adcsel::PLL3_R, + AdcClockSource::PerCk => Adcsel::PER, + } + } +} + +impl Default for AdcClockSource { + fn default() -> Self { + Self::Pll2PCk + } +} + /// Core clock frequencies #[derive(Clone, Copy)] pub struct CoreClocks { @@ -65,6 +88,7 @@ pub struct CoreClocks { pub pll3_r_ck: Option, pub timx_ker_ck: Option, pub timy_ker_ck: Option, + pub adc_ker_ck: Option, pub sys_ck: Hertz, pub c_ck: Hertz, } @@ -85,6 +109,7 @@ pub struct Config { pub pll1: PllConfig, pub pll2: PllConfig, pub pll3: PllConfig, + pub adc_clock_source: AdcClockSource, } /// Setup traceclk @@ -614,6 +639,16 @@ pub(crate) unsafe fn init(mut config: Config) { // Peripheral Clock (per_ck) RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel)); + // ADC clock MUX + RCC.d3ccipr() + .modify(|w| w.set_adcsel(config.adc_clock_source.adcsel())); + + let adc_ker_ck = match config.adc_clock_source { + AdcClockSource::Pll2PCk => pll2_p_ck.map(Hertz), + AdcClockSource::Pll3RCk => pll3_r_ck.map(Hertz), + AdcClockSource::PerCk => Some(per_ck), + }; + // Set timer clocks prescaler setting RCC.cfgr().modify(|w| w.set_timpre(timpre)); @@ -668,6 +703,7 @@ pub(crate) unsafe fn init(mut config: Config) { pll3_r_ck: pll3_r_ck.map(Hertz), timx_ker_ck: rcc_timerx_ker_ck.map(Hertz), timy_ker_ck: rcc_timery_ker_ck.map(Hertz), + adc_ker_ck, sys_ck, c_ck: Hertz(sys_d1cpre_ck), }; @@ -683,6 +719,7 @@ pub(crate) unsafe fn init(mut config: Config) { apb4: core_clocks.pclk4, apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1), apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2), + adc: core_clocks.adc_ker_ck, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d0ae2fc7..9a95836a 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -58,6 +58,9 @@ pub struct Clocks { #[cfg(rcc_f1)] pub adc: Hertz, + + #[cfg(any(rcc_h7, rcc_h7ab))] + pub adc: Option, } /// Frozen clock frequencies diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 6146b6dc..64baad99 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [features] [dependencies] -embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } +embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", default-features = false, features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs new file mode 100644 index 00000000..b12bca30 --- /dev/null +++ b/examples/stm32h7/src/bin/adc.rs @@ -0,0 +1,42 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy::executor::Spawner; +use embassy::time::{Delay, Duration, Timer}; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::rcc::AdcClockSource; +use embassy_stm32::time::U32Ext; +use embassy_stm32::{Config, Peripherals}; + +use defmt::*; +use defmt_rtt as _; // global logger +use panic_probe as _; + +pub fn config() -> Config { + let mut config = Config::default(); + config.rcc.sys_ck = Some(400.mhz().into()); + config.rcc.hclk = Some(200.mhz().into()); + config.rcc.per_ck = Some(64.mhz().into()); + config.rcc.adc_clock_source = AdcClockSource::PerCk; + config +} + +#[embassy::main(config = "config()")] +async fn main(_spawner: Spawner, mut p: Peripherals) { + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC3, &mut Delay); + + adc.set_sample_time(SampleTime::Cycles32_5); + + let mut vref_channel = adc.enable_vref(); + + loop { + let vref = adc.read_internal(&mut vref_channel); + info!("vref: {}", vref); + let measured = adc.read(&mut p.PC0); + info!("measured: {}", measured); + Timer::after(Duration::from_millis(500)).await; + } +} diff --git a/stm32-data b/stm32-data index 5295cf1a..8fb98d74 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 5295cf1aa474aa4b70ba2bc19ab0ced0173cd792 +Subproject commit 8fb98d74620ab71fb9d0be2d800c427e0b77c46f