From f5022719401ef15b02bfefadcf7e10719e48f06e Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 5 Sep 2023 16:46:57 -0500 Subject: [PATCH] stm32: add initial adc f3 impl --- embassy-stm32/build.rs | 11 ++- embassy-stm32/src/adc/f3.rs | 126 +++++++++++++++++++++++++++ embassy-stm32/src/adc/mod.rs | 59 ++++++++++--- embassy-stm32/src/adc/resolution.rs | 8 +- embassy-stm32/src/adc/sample_time.rs | 18 +++- embassy-stm32/src/rcc/f3.rs | 93 +++++++++++++++++++- embassy-stm32/src/rcc/mod.rs | 5 +- examples/stm32f334/src/bin/adc.rs | 34 ++++++++ 8 files changed, 331 insertions(+), 23 deletions(-) create mode 100644 embassy-stm32/src/adc/f3.rs create mode 100644 examples/stm32f334/src/bin/adc.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index aef0668a2..d3bfd24fb 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -309,14 +309,17 @@ fn main() { // Generate RccPeripheral impls // TODO: maybe get this from peripheral kind? Not sure - let refcounted_peripherals = HashSet::from(["USART"]); + let mut refcounted_peripherals = HashSet::from(["usart"]); 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 == "f3")) || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4")) { continue; @@ -348,13 +351,13 @@ fn main() { TokenStream::new() }; - let ptype = (if let Some(reg) = &p.registers { reg.kind } else { "" }).to_ascii_uppercase(); + let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" }; let pname = format_ident!("{}", p.name); let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase()); let en_reg = format_ident!("{}", en.register.to_ascii_lowercase()); let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase()); - let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype.as_str()) { + let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) { let refcount_static = format_ident!("{}_{}", en.register.to_ascii_uppercase(), en.field.to_ascii_uppercase()); diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs new file mode 100644 index 000000000..458573c05 --- /dev/null +++ b/embassy-stm32/src/adc/f3.rs @@ -0,0 +1,126 @@ +use embassy_hal_internal::into_ref; +use embedded_hal_02::blocking::delay::DelayUs; + +use crate::adc::{Adc, AdcPin, Instance, SampleTime}; +use crate::time::Hertz; +use crate::Peripheral; + +pub const VDDA_CALIB_MV: u32 = 3300; +pub const ADC_MAX: u32 = (1 << 12) - 1; +// No calibration data for F103, voltage should be 1.2v +pub const VREF_INT: u32 = 1200; + +pub struct Vref; +impl AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { + fn channel(&self) -> u8 { + 18 + } +} + +pub struct Temperature; +impl AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { + fn channel(&self) -> u8 { + 16 + } +} + +impl<'d, T: Instance> Adc<'d, T> { + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + use crate::pac::adc::vals; + + into_ref!(adc); + + T::enable(); + T::reset(); + + // Enable the adc regulator + T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE)); + T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::ENABLED)); + + // Wait for the regulator to stabilize + delay.delay_us(10); + + assert!(!T::regs().cr().read().aden()); + + // Begin calibration + T::regs().cr().modify(|w| w.set_adcaldif(false)); + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + // Enable the adc + T::regs().cr().modify(|w| w.set_aden(true)); + + // Wait until the adc is ready + while !T::regs().isr().read().adrdy() {} + + Self { + adc, + sample_time: Default::default(), + } + } + + fn freq() -> Hertz { + ::frequency() + } + + pub fn sample_time_for_us(&self, us: u32) -> SampleTime { + match us * Self::freq().0 / 1_000_000 { + 0..=1 => SampleTime::Cycles1_5, + 2..=4 => SampleTime::Cycles4_5, + 5..=7 => SampleTime::Cycles7_5, + 8..=19 => SampleTime::Cycles19_5, + 20..=61 => SampleTime::Cycles61_5, + 62..=181 => SampleTime::Cycles181_5, + _ => SampleTime::Cycles601_5, + } + } + + pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref { + T::common_regs().ccr().modify(|w| w.set_vrefen(true)); + + Vref {} + } + + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|w| w.set_tsen(true)); + + Temperature {} + } + + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + /// Perform a single conversion. + fn convert(&mut self) -> u16 { + T::regs().isr().write(|_| {}); + T::regs().cr().modify(|w| w.set_adstart(true)); + + while !T::regs().isr().read().eoc() && !T::regs().isr().read().eos() {} + T::regs().isr().write(|_| {}); + + T::regs().dr().read().0 as u16 + } + + pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + // pin.set_as_analog(); + + Self::set_channel_sample_time(pin.channel(), self.sample_time); + + // Configure the channel to sample + T::regs().sqr3().write(|w| w.set_sq(0, pin.channel())); + self.convert() + } + + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); + if ch <= 9 { + T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); + } else { + T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); + } + } +} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index e57889aa6..a127445d8 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,23 +1,24 @@ #![macro_use] -#[cfg(not(any(adc_f3, adc_f3_v2)))] +#[cfg(not(adc_f3_v2))] #[cfg_attr(adc_f1, path = "f1.rs")] +#[cfg_attr(adc_f3, path = "f3.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] +#[cfg(not(any(adc_f1, adc_f3_v2)))] mod resolution; mod sample_time; -#[cfg(not(any(adc_f3, adc_f3_v2)))] #[allow(unused)] +#[cfg(not(adc_f3_v2))] pub use _version::*; #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] pub use resolution::Resolution; -#[cfg(not(any(adc_f3, adc_f3_v2)))] +#[cfg(not(adc_f3_v2))] pub use sample_time::SampleTime; use crate::peripherals; @@ -25,15 +26,17 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, - #[cfg(not(any(adc_f3, adc_f3_v2)))] + #[cfg(not(adc_f3_v2))] sample_time: SampleTime, } pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; - #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; + #[cfg(adc_f3)] + fn frequency() -> crate::time::Hertz; } pub trait AdcPin { @@ -45,22 +48,22 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))] +#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3)))] pub trait Instance: sealed::Instance + crate::Peripheral

{} -#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))] +#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3))] pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} pub trait InternalChannel: sealed::InternalChannel {} -#[cfg(not(stm32h7))] +#[cfg(not(any(stm32h7, adc_f3)))] foreach_peripheral!( (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { @@ -74,7 +77,7 @@ foreach_peripheral!( }; ); -#[cfg(stm32h7)] +#[cfg(any(stm32h7, adc_f3))] foreach_peripheral!( (adc, ADC3) => { impl crate::adc::sealed::Instance for peripherals::ADC3 { @@ -89,16 +92,43 @@ foreach_peripheral!( }; } } + + #[cfg(adc_f3)] + fn frequency() -> crate::time::Hertz { + unsafe { crate::rcc::get_freqs() }.adc34.unwrap() + } } impl crate::adc::Instance for peripherals::ADC3 {} }; + (adc, ADC4) => { + impl crate::adc::sealed::Instance for peripherals::ADC4 { + fn regs() -> crate::pac::adc::Adc { + crate::pac::ADC4 + } + #[cfg(not(any(adc_f1, adc_v1)))] + fn common_regs() -> crate::pac::adccommon::AdcCommon { + foreach_peripheral!{ + (adccommon, ADC3_COMMON) => { + return crate::pac::ADC3_COMMON + }; + } + } + + #[cfg(adc_f3)] + fn frequency() -> crate::time::Hertz { + unsafe { crate::rcc::get_freqs() }.adc34.unwrap() + } + } + + impl crate::adc::Instance for peripherals::ADC4 {} + }; (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(all(not(adc_f1), not(adc_v1)))] + #[cfg(not(any(adc_f1, adc_v1)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, ADC_COMMON) => { @@ -106,6 +136,11 @@ foreach_peripheral!( }; } } + + #[cfg(adc_f3)] + fn frequency() -> crate::time::Hertz { + unsafe { crate::rcc::get_freqs() }.adc.unwrap() + } } impl crate::adc::Instance for peripherals::$inst {} diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 67fb9b8c0..5668137b5 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,4 +1,4 @@ -#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] +#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { TwelveBit, @@ -19,7 +19,7 @@ pub enum Resolution { impl Default for Resolution { fn default() -> Self { - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] { Self::TwelveBit } @@ -40,7 +40,7 @@ impl From for crate::pac::adc::vals::Res { Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } @@ -56,7 +56,7 @@ impl Resolution { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, Resolution::EightBit => (1 << 8) - 1, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] Resolution::SixBit => (1 << 6) - 1, } } diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 5480e7a77..6a6619299 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,4 +1,4 @@ -#[cfg(not(any(adc_f3, adc_f3_v2)))] +#[cfg(not(adc_f3_v2))] macro_rules! impl_sample_time { ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] @@ -105,3 +105,19 @@ impl_sample_time!( ("810.5", Cycles810_5, CYCLES810_5) ) ); + +#[cfg(adc_f3)] +impl_sample_time!( + "1.5", + Cycles1_5, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("2.5", Cycles2_5, CYCLES2_5), + ("4.5", Cycles4_5, CYCLES4_5), + ("7.5", Cycles7_5, CYCLES7_5), + ("19.5", Cycles19_5, CYCLES19_5), + ("61.5", Cycles61_5, CYCLES61_5), + ("181.5", Cycles181_5, CYCLES181_5), + ("601.5", Cycles601_5, CYCLES601_5) + ) +); diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 7480c0393..f8726c24a 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -1,5 +1,5 @@ use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; +use crate::pac::rcc::vals::{Adcpres, Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -10,6 +10,46 @@ pub const HSI_FREQ: Hertz = Hertz(8_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(40_000); +#[repr(u16)] +#[derive(Clone, Copy)] +pub enum ADCPrescaler { + Div1 = 1, + Div2 = 2, + Div4 = 4, + Div6 = 6, + Div8 = 8, + Div12 = 12, + Div16 = 16, + Div32 = 32, + Div64 = 64, + Div128 = 128, + Div256 = 256, +} + +impl From for Adcpres { + fn from(value: ADCPrescaler) -> Self { + match value { + ADCPrescaler::Div1 => Adcpres::DIV1, + ADCPrescaler::Div2 => Adcpres::DIV2, + ADCPrescaler::Div4 => Adcpres::DIV4, + ADCPrescaler::Div6 => Adcpres::DIV6, + ADCPrescaler::Div8 => Adcpres::DIV8, + ADCPrescaler::Div12 => Adcpres::DIV12, + ADCPrescaler::Div16 => Adcpres::DIV16, + ADCPrescaler::Div32 => Adcpres::DIV32, + ADCPrescaler::Div64 => Adcpres::DIV64, + ADCPrescaler::Div128 => Adcpres::DIV128, + ADCPrescaler::Div256 => Adcpres::DIV256, + } + } +} + +#[derive(Clone, Copy)] +pub enum ADCClock { + AHB(ADCPrescaler), + PLL(ADCPrescaler), +} + /// Clocks configutation #[non_exhaustive] #[derive(Default)] @@ -36,9 +76,18 @@ pub struct Config { /// - The System clock frequency is either 48MHz or 72MHz /// - APB1 clock has a minimum frequency of 10MHz pub pll48: bool, + #[cfg(rcc_f3)] + /// ADC clock setup + /// - For AHB, a psc of 4 or less must be used + pub adc: Option, + #[cfg(rcc_f3)] + /// ADC clock setup + /// - For AHB, a psc of 4 or less must be used + pub adc34: Option, } // Information required to setup the PLL clock +#[derive(Clone, Copy)] struct PllConfig { pll_src: Pllsrc, pll_mul: Pllmul, @@ -148,6 +197,44 @@ pub(crate) unsafe fn init(config: Config) { }); } + #[cfg(rcc_f3)] + let adc = config.adc.map(|adc| match adc { + ADCClock::PLL(psc) => RCC.cfgr2().modify(|w| { + // Make sure that we're using the PLL + pll_config.unwrap(); + w.set_adc12pres(psc.into()); + + Hertz(sysclk / psc as u32) + }), + ADCClock::AHB(psc) => { + assert!(psc as u16 <= 4); + assert!(!(psc as u16 == 1 && hpre_bits != Hpre::DIV1)); + + // To select this scheme, bits CKMODE[1:0] of the ADCx_CCR register must be + // different from “00”. + todo!(); + } + }); + + #[cfg(rcc_f3)] + let adc34 = config.adc34.map(|adc| match adc { + ADCClock::PLL(psc) => RCC.cfgr2().modify(|w| { + // Make sure that we're using the PLL + pll_config.unwrap(); + w.set_adc34pres(psc.into()); + + Hertz(sysclk / psc as u32) + }), + ADCClock::AHB(psc) => { + assert!(psc as u16 <= 4); + assert!(!(psc as u16 == 1 && hpre_bits != Hpre::DIV1)); + + // To select this scheme, bits CKMODE[1:0] of the ADCx_CCR register must be + // different from “00”. + todo!(); + } + }); + // Set prescalers // CFGR has been written before (PLL, PLL48) don't overwrite these settings RCC.cfgr().modify(|w| { @@ -177,6 +264,10 @@ pub(crate) unsafe fn init(config: Config) { apb1_tim: Hertz(pclk1 * timer_mul1), apb2_tim: Hertz(pclk2 * timer_mul2), ahb1: Hertz(hclk), + #[cfg(rcc_f3)] + adc: adc, + #[cfg(rcc_f3)] + adc34: adc34, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 0430e4a74..2e1f60358 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -74,9 +74,12 @@ pub struct Clocks { #[cfg(stm32f1)] pub adc: Hertz, - #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] + #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3))] pub adc: Option, + #[cfg(rcc_f3)] + pub adc34: 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/stm32f334/src/bin/adc.rs b/examples/stm32f334/src/bin/adc.rs new file mode 100644 index 000000000..729497e82 --- /dev/null +++ b/examples/stm32f334/src/bin/adc.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_stm32::rcc::{ADCClock, ADCPrescaler}; +use embassy_stm32::time::Hertz; +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.hse = Some(Hertz(8_000_000)); + config.rcc.sysclk = Some(Hertz(16_000_000)); + config.rcc.adc = Some(ADCClock::PLL(ADCPrescaler::Div1)); + + let mut p = embassy_stm32::init(config); + + let mut adc = Adc::new(p.ADC1, &mut Delay); + + let mut vrefint = adc.enable_vref(&mut Delay); + + let _vref = adc.read(&mut vrefint); + let _pin = adc.read(&mut p.PA0); + + loop { + info!("Hello World!"); + Timer::after(Duration::from_secs(1)).await; + } +}