diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d169107d..30694596 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7e2310f49fa123fbc3225b91be73522b212703f0" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -77,7 +77,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7e2310f49fa123fbc3225b91be73522b212703f0", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d3bfd24f..bb81736b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -308,20 +308,11 @@ fn main() { // ======== // Generate RccPeripheral impls - // TODO: maybe get this from peripheral kind? Not sure - let mut refcounted_peripherals = HashSet::from(["usart"]); + let refcounted_peripherals = HashSet::from(["usart", "adc"]); let mut refcount_statics = HashSet::new(); - if chip_name.starts_with("stm32f3") { - refcounted_peripherals.insert("adc"); - } - for p in METADATA.peripherals { - // 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")) - || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4")) - { + if !singletons.contains(&p.name.to_string()) { continue; } @@ -711,6 +702,10 @@ fn main() { // ADC is special if regs.kind == "adc" { + if p.rcc.is_none() { + continue; + } + let peri = format_ident!("{}", p.name); let pin_name = format_ident!("{}", pin.pin); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index a127445d..0eeadfa9 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -56,7 +56,7 @@ pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc: pub trait AdcPin: sealed::AdcPin {} pub trait InternalChannel: sealed::InternalChannel {} -#[cfg(not(any(stm32h7, adc_f3)))] +#[cfg(not(any(stm32h7, adc_f3, adc_v4)))] foreach_peripheral!( (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { @@ -77,9 +77,10 @@ foreach_peripheral!( }; ); -#[cfg(any(stm32h7, adc_f3))] +#[cfg(any(stm32h7, adc_f3, adc_v4))] foreach_peripheral!( (adc, ADC3) => { + #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] impl crate::adc::sealed::Instance for peripherals::ADC3 { fn regs() -> crate::pac::adc::Adc { crate::pac::ADC3 @@ -99,9 +100,11 @@ foreach_peripheral!( } } + #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] impl crate::adc::Instance for peripherals::ADC3 {} }; (adc, ADC4) => { + #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] impl crate::adc::sealed::Instance for peripherals::ADC4 { fn regs() -> crate::pac::adc::Adc { crate::pac::ADC4 @@ -121,7 +124,11 @@ foreach_peripheral!( } } + #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))] impl crate::adc::Instance for peripherals::ADC4 {} + }; + (adc, ADC5) => { + }; (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 64d0f0c7..655c0cb6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,6 +1,5 @@ -use core::sync::atomic::{AtomicU8, Ordering}; - use embedded_hal_02::blocking::delay::DelayUs; +#[allow(unused)] use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; @@ -13,12 +12,31 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -// NOTE: Vrefint/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 +/// Max single ADC operation clock frequency +#[cfg(stm32g4)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); +#[cfg(stm32h7)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); + +#[cfg(stm32g4)] +const VREF_CHANNEL: u8 = 18; +#[cfg(stm32g4)] +const TEMP_CHANNEL: u8 = 16; + +#[cfg(stm32h7)] +const VREF_CHANNEL: u8 = 19; +#[cfg(stm32h7)] +const TEMP_CHANNEL: u8 = 18; + +// TODO this should be 14 for H7a/b/35 +const VBAT_CHANNEL: u8 = 17; + +// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} impl super::sealed::InternalChannel for VrefInt { fn channel(&self) -> u8 { - 19 + VREF_CHANNEL } } @@ -26,7 +44,7 @@ pub struct Temperature; impl InternalChannel for Temperature {} impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { - 18 + TEMP_CHANNEL } } @@ -34,128 +52,10 @@ pub struct Vbat; impl InternalChannel for Vbat {} impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { - // TODO this should be 14 for H7a/b/35 - 17 + VBAT_CHANNEL } } -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(|_| { - match unsafe { 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(|_| { - 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(|_| { - 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(|_| { - 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(|_| { - match unsafe { 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(|_| { - 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(|_| { - 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(|_| { - 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(|_| { - match unsafe { 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(|_| { - crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true)) - }); - } - - fn disable() { - critical_section::with(|_| { - crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false)); - }) - } - - fn reset() { - critical_section::with(|_| { - 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 {} - }; -); - // 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)] @@ -176,7 +76,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / 50_000_000; + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, @@ -237,20 +137,23 @@ impl<'d, T: Instance> Adc<'d, T> { let frequency = Hertz(T::frequency().0 / prescaler.divisor()); info!("ADC frequency set to {} Hz", frequency.0); - if frequency > Hertz::mhz(50) { - panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information."); + if frequency > MAX_ADC_CLK_FREQ { + panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); } - let boost = if frequency < Hertz::khz(6_250) { - Boost::LT6_25 - } else if frequency < Hertz::khz(12_500) { - Boost::LT12_5 - } else if frequency < Hertz::mhz(25) { - Boost::LT25 - } else { - Boost::LT50 - }; - T::regs().cr().modify(|w| w.set_boost(boost)); + #[cfg(stm32h7)] + { + let boost = if frequency < Hertz::khz(6_250) { + Boost::LT6_25 + } else if frequency < Hertz::khz(12_500) { + Boost::LT12_5 + } else if frequency < Hertz::mhz(25) { + Boost::LT25 + } else { + Boost::LT50 + }; + T::regs().cr().modify(|w| w.set_boost(boost)); + } let mut s = Self { adc, sample_time: Default::default(), @@ -379,10 +282,14 @@ impl<'d, T: Instance> Adc<'d, T> { // 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)); + #[cfg(stm32h7)] + { + 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); diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 3b044cd1..2359f39c 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,5 +1,5 @@ use stm32_metapac::flash::vals::Latency; -use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; +use stm32_metapac::rcc::vals::{Adcsel, Hpre, Pllsrc, Ppre, Sw}; use stm32_metapac::FLASH; pub use super::bus::{AHBPrescaler, APBPrescaler}; @@ -14,6 +14,29 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); +#[derive(Clone, Copy)] +pub enum AdcClockSource { + NoClk, + SysClk, + PllP, +} + +impl AdcClockSource { + pub fn adcsel(&self) -> Adcsel { + match self { + AdcClockSource::NoClk => Adcsel::NOCLK, + AdcClockSource::SysClk => Adcsel::SYSCLK, + AdcClockSource::PllP => Adcsel::PLLP, + } + } +} + +impl Default for AdcClockSource { + fn default() -> Self { + Self::NoClk + } +} + /// System clock mux source #[derive(Clone, Copy)] pub enum ClockSrc { @@ -327,6 +350,8 @@ pub struct Config { pub pll: Option, /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. pub clock_48mhz_src: Option, + pub adc12_clock_source: AdcClockSource, + pub adc345_clock_source: AdcClockSource, } /// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator. @@ -346,6 +371,8 @@ impl Default for Config { low_power_run: false, pll: None, clock_48mhz_src: None, + adc12_clock_source: Default::default(), + adc345_clock_source: Default::default(), } } } @@ -549,6 +576,29 @@ pub(crate) unsafe fn init(config: Config) { RCC.ccipr().modify(|w| w.set_clk48sel(source)); } + RCC.ccipr() + .modify(|w| w.set_adc12sel(config.adc12_clock_source.adcsel())); + RCC.ccipr() + .modify(|w| w.set_adc345sel(config.adc345_clock_source.adcsel())); + + let adc12_ck = match config.adc12_clock_source { + AdcClockSource::NoClk => None, + AdcClockSource::PllP => match &pll_freq { + Some(pll) => pll.pll_p, + None => None, + }, + AdcClockSource::SysClk => Some(Hertz(sys_clk)), + }; + + let adc345_ck = match config.adc345_clock_source { + AdcClockSource::NoClk => None, + AdcClockSource::PllP => match &pll_freq { + Some(pll) => pll.pll_p, + None => None, + }, + AdcClockSource::SysClk => Some(Hertz(sys_clk)), + }; + if config.low_power_run { assert!(sys_clk <= 2_000_000); PWR.cr1().modify(|w| w.set_lpr(true)); @@ -562,5 +612,7 @@ pub(crate) unsafe fn init(config: Config) { apb1_tim: Hertz(apb1_tim_freq), apb2: Hertz(apb2_freq), apb2_tim: Hertz(apb2_tim_freq), + adc: adc12_ck, + adc34: adc345_ck, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 2e1f6035..535ab6ad 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -74,10 +74,10 @@ pub struct Clocks { #[cfg(stm32f1)] pub adc: Hertz, - #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3))] + #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))] pub adc: Option, - #[cfg(rcc_f3)] + #[cfg(any(rcc_f3, rcc_g4))] pub adc34: Option, #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 0c1cdd67..2e81d206 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -11,6 +11,8 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +usbd-hid = "0.6.0" defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs new file mode 100644 index 00000000..a792748b --- /dev/null +++ b/examples/stm32g4/src/bin/adc.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSrc}; +use embassy_stm32::Config; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + config.rcc.pll = Some(Pll { + source: PllSrc::HSI16, + prediv_m: PllM::Div4, + mul_n: PllN::Mul85, + div_p: None, + div_q: None, + // Main system clock at 170 MHz + div_r: Some(PllR::Div2), + }); + + config.rcc.adc12_clock_source = AdcClockSource::SysClk; + config.rcc.mux = ClockSrc::PLL; + + let mut p = embassy_stm32::init(config); + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC2, &mut Delay); + adc.set_sample_time(SampleTime::Cycles32_5); + + loop { + let measured = adc.read(&mut p.PA7); + info!("measured: {}", measured); + Timer::after(Duration::from_millis(500)).await; + } +}