diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 87f9083b..ab1cae89 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -59,7 +59,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-8ee2862086886cd8ebaf5fd5e3bd6cfbe5baa840" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-06d13dfd245cc9bf86fd88c35b401bdb84c079c4" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -78,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-8ee2862086886cd8ebaf5fd5e3bd6cfbe5baa840", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-06d13dfd245cc9bf86fd88c35b401bdb84c079c4", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ed5fa84d..f825dbee 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -810,6 +810,20 @@ fn main() { } } + if regs.kind == "opamp" { + if !pin.signal.starts_with("VP") { + continue; + } + + let peri = format_ident!("{}", p.name); + let pin_name = format_ident!("{}", pin.pin); + let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); + + g.extend(quote! { + impl_opamp_pin!( #peri, #pin_name, #ch); + }) + } + // DAC is special if regs.kind == "dac" { let peri = format_ident!("{}", p.name); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index bd77fae4..e883678b 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -49,6 +49,8 @@ pub mod i2s; pub mod ipcc; #[cfg(feature = "low-power")] pub mod low_power; +#[cfg(opamp)] +pub mod opamp; #[cfg(quadspi)] pub mod qspi; #[cfg(rng)] diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs new file mode 100644 index 00000000..7b388aef --- /dev/null +++ b/embassy-stm32/src/opamp.rs @@ -0,0 +1,133 @@ +#![macro_use] + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use crate::Peripheral; + +#[cfg(opamp_f3)] +pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin> { + _inner: &'d OpAmp<'d, T>, + _input: &'p mut P, +} + +pub struct OpAmp<'d, T: Instance> { + _inner: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> OpAmp<'d, T> { + pub fn new(opamp: impl Peripheral

+ 'd) -> Self { + Self::new_inner(opamp) + } + + fn new_inner(opamp: impl Peripheral

+ 'd) -> Self { + into_ref!(opamp); + + #[cfg(opamp_f3)] + T::regs().opampcsr().modify(|w| { + w.set_opampen(true); + }); + + #[cfg(opamp_g4)] + T::regs().opamp_csr().modify(|w| { + w.set_opaen(true); + }); + + Self { _inner: opamp } + } + + #[cfg(opamp_f3)] + pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P) -> OpAmpOutput<'a, 'b, T, P> + where + P: NonInvertingPin, + { + #[cfg(opamp_f3)] + T::regs().opampcsr().modify(|w| { + w.set_vp_sel(pin.channel()); + }); + + OpAmpOutput { + _inner: self, + _input: pin, + } + } +} + +pub trait Instance: sealed::Instance + 'static {} + +pub(crate) mod sealed { + pub trait Instance { + fn regs() -> crate::pac::opamp::Opamp; + } + + pub trait NonInvertingPin { + fn channel(&self) -> u8; + } + + pub trait InvertingPin { + fn channel(&self) -> u8; + } +} + +pub trait NonInvertingPin: sealed::NonInvertingPin {} + +pub trait InvertingPin: sealed::InvertingPin {} + +#[cfg(opamp_f3)] +macro_rules! impl_opamp_output { + ($inst:ident, $adc:ident, $ch:expr) => { + impl<'d, 'p, P: NonInvertingPin> crate::adc::sealed::AdcPin + for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> + { + fn channel(&self) -> u8 { + $ch + } + } + + impl<'d, 'p, P: NonInvertingPin> crate::adc::AdcPin + for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> + { + } + }; +} + +#[cfg(opamp_f3)] +foreach_peripheral!( + (opamp, OPAMP1) => { + impl_opamp_output!(OPAMP1, ADC1, 3); + }; + (opamp, OPAMP2) => { + impl_opamp_output!(OPAMP2, ADC2, 3); + }; + (opamp, OPAMP3) => { + impl_opamp_output!(OPAMP3, ADC3, 1); + }; + (opamp, OPAMP4) => { + impl_opamp_output!(OPAMP4, ADC4, 3); + }; +); + +foreach_peripheral! { + (opamp, $inst:ident) => { + impl sealed::Instance for crate::peripherals::$inst { + fn regs() -> crate::pac::opamp::Opamp { + crate::pac::$inst + } + } + + impl Instance for crate::peripherals::$inst { + + } + }; +} + +#[allow(unused_macros)] +macro_rules! impl_opamp_pin { + ($inst:ident, $pin:ident, $ch:expr) => { + impl crate::opamp::NonInvertingPin for crate::peripherals::$pin {} + impl crate::opamp::sealed::NonInvertingPin for crate::peripherals::$pin { + fn channel(&self) -> u8 { + $ch + } + } + }; +} diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs new file mode 100644 index 00000000..72263bab --- /dev/null +++ b/examples/stm32f334/src/bin/opamp.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::opamp::OpAmp; +use embassy_stm32::peripherals::ADC2; +use embassy_stm32::rcc::AdcClockSource; +use embassy_stm32::time::mhz; +use embassy_stm32::{adc, bind_interrupts, Config}; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1_2 => adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.sysclk = Some(mhz(64)); + config.rcc.hclk = Some(mhz(64)); + config.rcc.pclk1 = Some(mhz(32)); + config.rcc.pclk2 = Some(mhz(64)); + config.rcc.adc = Some(AdcClockSource::PllDiv1); + + let mut p = embassy_stm32::init(config); + + info!("create adc..."); + + let mut adc = Adc::new(p.ADC2, Irqs, &mut Delay); + let mut opamp = OpAmp::new(p.OPAMP2); + + adc.set_sample_time(SampleTime::Cycles601_5); + + info!("enable vrefint..."); + + let mut vrefint = adc.enable_vref(&mut Delay); + let mut temperature = adc.enable_temperature(); + let mut buffer = opamp.buffer_for(&mut p.PA7); + + loop { + let vref = adc.read(&mut vrefint).await; + info!("read vref: {} (should be {})", vref, vrefint.value()); + + let temp = adc.read(&mut temperature).await; + info!("read temperature: {}", temp); + + let buffer = adc.read(&mut buffer).await; + info!("read buffer: {}", buffer); + + let pin_mv = (buffer as u32 * vrefint.value() as u32 / vref as u32) * 3300 / 4095; + info!("computed pin mv: {}", pin_mv); + + Timer::after(Duration::from_millis(500)).await; + } +}