From 49ba9c3da2b6929c5ec1fb17d8c43c271a70eb34 Mon Sep 17 00:00:00 2001 From: Daehyeok Mun Date: Sun, 20 Aug 2023 19:31:47 -0700 Subject: [PATCH] initial support for STM32G4 ADC --- embassy-stm32/Cargo.toml | 5 +- embassy-stm32/src/adc/v4.rs | 247 +++++++++++++++----------------- embassy-stm32/src/rcc/g4.rs | 54 ++++++- embassy-stm32/src/rcc/mod.rs | 6 + examples/stm32g4/Cargo.toml | 2 + examples/stm32g4/src/bin/adc.rs | 41 ++++++ 6 files changed, 219 insertions(+), 136 deletions(-) create mode 100644 examples/stm32g4/src/bin/adc.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d169107d..712996f2 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" @@ -70,6 +70,7 @@ embedded-io-async = { version = "0.5.0", optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" document-features = "0.2.7" +paste = "1.0" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -77,7 +78,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/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 64d0f0c7..d03f2550 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,8 +1,10 @@ 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; +use paste::paste; use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::time::Hertz; @@ -13,12 +15,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 +47,7 @@ pub struct Temperature; impl InternalChannel for Temperature {} impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { - 18 + TEMP_CHANNEL } } @@ -34,126 +55,79 @@ 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(any(stm32g4x3, stm32g4x4))] +static ADC345_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0); + +macro_rules! rcc_peripheral { + ($adc_name:ident, $freqs:ident, $ahb:ident, $reg:ident $(, $counter:ident )? ) => { + impl crate::rcc::sealed::RccPeripheral for crate::peripherals::$adc_name { + fn frequency() -> crate::time::Hertz { + critical_section::with(|_| { + match unsafe { crate::rcc::get_freqs() }.$freqs { + Some(ck) => ck, + None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") + } + }) + } + + fn enable() { + critical_section::with(|_| { + paste!{crate::pac::RCC.[< $ahb enr >]().modify(|w| w.[< set_ $reg en >](true))} + }); + $ ( $counter.fetch_add(1, Ordering::SeqCst); )? + } + + fn disable() { + $ ( if $counter.load(Ordering::SeqCst) == 1 )? { + critical_section::with(|_| { + paste!{crate::pac::RCC.[< $ahb enr >]().modify(|w| w.[< set_ $reg en >](false))} + }) + } + $ ( $counter.fetch_sub(1, Ordering::SeqCst); )? + } + + fn reset() { + $ ( if $counter.load(Ordering::SeqCst) == 1 )? { + critical_section::with(|_| { + paste!{crate::pac::RCC.[< $ahb rstr >]().modify(|w| w.[< set_ $reg rst >](true))} + paste!{crate::pac::RCC.[< $ahb rstr >]().modify(|w| w.[< set_ $reg rst >](false))} + }); + } + } + } + + impl crate::rcc::RccPeripheral for crate::peripherals::$adc_name {} + }; +} + +#[cfg(stm32g4)] +foreach_peripheral!( + (adc, ADC1) => { rcc_peripheral!(ADC1, adc12, ahb2, adc12, ADC12_ENABLE_COUNTER); }; + (adc, ADC2) => { rcc_peripheral!(ADC2, adc12, ahb2, adc12, ADC12_ENABLE_COUNTER); }; +); + +#[cfg(stm32g4x1)] +foreach_peripheral!( + (adc, ADC3) => { rcc_peripheral!(ADC3, adc345, ahb2, adc345); }; +); + +#[cfg(any(stm32g4x3, stm32g4x4))] +foreach_peripheral!( + (adc, ADC3) => { rcc_peripheral!(ADC3, adc345, ahb2, adc345, ADC345_ENABLE_COUNTER); }; + (adc, ADC4) => { rcc_peripheral!(ADC4, adc345, ahb2, adc345, ADC345_ENABLE_COUNTER); }; + (adc, ADC5) => { rcc_peripheral!(ADC5, adc345, ahb2, adc345, ADC345_ENABLE_COUNTER); }; +); #[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 {} - }; + (adc, ADC1) => { rcc_peripheral!(ADC1, adc, ahb1, adc12, ADC12_ENABLE_COUNTER); }; + (adc, ADC2) => { rcc_peripheral!(ADC2, adc, ahb1, adc12, ADC12_ENABLE_COUNTER); }; + (adc, ADC3) => { rcc_peripheral!(ADC3, adc, ahb4, adc3); }; ); // NOTE (unused): The prescaler enum closely copies the hardware capabilities, @@ -176,7 +150,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 +211,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 +356,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..4c95bb15 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), + adc12: adc12_ck, + adc345: adc345_ck, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 0430e4a7..ecabe23a 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -77,6 +77,12 @@ pub struct Clocks { #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, + #[cfg(any(rcc_g4))] + pub adc12: Option, + + #[cfg(any(rcc_g4))] + pub adc345: Option, + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] /// Set only if the lsi or lse is configured, indicates stop is supported pub rtc: Option, 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; + } +}