From 28eb4cd8173063b5e61f371c9642edb5a9289a2b Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sun, 5 Nov 2023 03:37:22 +0000 Subject: [PATCH] stm32: support internal output on g4 opamps --- embassy-stm32/build.rs | 28 ++-- embassy-stm32/src/opamp.rs | 217 ++++++++++++++++++++++++---- examples/stm32f334/src/bin/opamp.rs | 2 +- 3 files changed, 211 insertions(+), 36 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 276c1d63..35c3f23b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -944,17 +944,25 @@ fn main() { } if regs.kind == "opamp" { - if !pin.signal.starts_with("VP") { - continue; + println!("{}", pin.signal); + + if pin.signal.starts_with("VP") { + // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc) + 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_vp_pin!( #peri, #pin_name, #ch); + }) + } else if pin.signal == "VOUT" { + // Impl OutputPin for the VOUT pin + let peri = format_ident!("{}", p.name); + let pin_name = format_ident!("{}", pin.pin); + g.extend(quote! { + impl_opamp_vout_pin!( #peri, #pin_name ); + }) } - - 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 diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index cb55cbe1..89db6d14 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -13,21 +13,50 @@ pub enum OpAmpGain { Mul16, } -pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin> { - _inner: &'d OpAmp<'d, T>, - _input: &'p mut P, +#[derive(Clone, Copy)] +pub enum OpAmpSpeed { + Normal, + HighSpeed, } +#[cfg(opamp_g4)] +impl From for crate::pac::opamp::vals::OpampCsrOpahsm { + fn from(v: OpAmpSpeed) -> Self { + match v { + OpAmpSpeed::Normal => crate::pac::opamp::vals::OpampCsrOpahsm::NORMAL, + OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::OpampCsrOpahsm::HIGHSPEED, + } + } +} + +/// OpAmp external outputs, wired to a GPIO pad. +/// +/// The GPIO output pad is held by this struct to ensure it cannot be used elsewhere. +/// +/// This struct can also be used as an ADC input. +pub struct OpAmpOutput<'d, 'p, T: Instance, P: OutputPin> { + _inner: &'d OpAmp<'d, T>, + _output: &'p mut P, +} + +/// OpAmp internal outputs, wired directly to ADC inputs. +/// +/// This struct can be used as an ADC input. +pub struct OpAmpInternalOutput<'d, T: Instance> { + _inner: &'d OpAmp<'d, T>, +} + +/// OpAmp driver. 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 { + /// Create a new driver instance. + /// + /// Enables the OpAmp and configures the speed, but + /// does not set any other configuration. + pub fn new(opamp: impl Peripheral

+ 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { into_ref!(opamp); #[cfg(opamp_f3)] @@ -38,15 +67,34 @@ impl<'d, T: Instance> OpAmp<'d, T> { #[cfg(opamp_g4)] T::regs().opamp_csr().modify(|w| { w.set_opaen(true); + w.set_opahsm(speed.into()); }); Self { _inner: opamp } } - pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P, gain: OpAmpGain) -> OpAmpOutput<'a, 'b, T, P> + /// Configure the OpAmp as a buffer for the provided input pin, + /// outputting to the provided output pin. + /// + /// The input pin is configured for analogue mode but not consumed, + /// so it may subsequently be used for ADC or comparator inputs. + /// + /// The output pin is held within the returned [`OpAmpOutput`] struct, + /// preventing it being used elsewhere. The `OpAmpOutput` can then be + /// directly used as an ADC input. + pub fn buffer_ext<'a, 'b, IP, OP>( + &'a mut self, + in_pin: &IP, + out_pin: &'b mut OP, + gain: OpAmpGain, + ) -> OpAmpOutput<'a, 'b, T, OP> where - P: NonInvertingPin, + IP: NonInvertingPin + crate::gpio::sealed::Pin, + OP: OutputPin + crate::gpio::sealed::Pin, { + in_pin.set_as_analog(); + out_pin.set_as_analog(); + let (vm_sel, pga_gain) = match gain { OpAmpGain::Mul1 => (0b11, 0b00), OpAmpGain::Mul2 => (0b10, 0b00), @@ -57,25 +105,76 @@ impl<'d, T: Instance> OpAmp<'d, T> { #[cfg(opamp_f3)] T::regs().opampcsr().modify(|w| { - w.set_vp_sel(pin.channel()); + w.set_vp_sel(in_pin.channel()); w.set_vm_sel(vm_sel); w.set_pga_gain(pga_gain); + w.set_opampen(true); }); #[cfg(opamp_g4)] T::regs().opamp_csr().modify(|w| { use crate::pac::opamp::vals::*; - w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel())); + w.set_vp_sel(OpampCsrVpSel::from_bits(in_pin.channel())); w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); + w.set_opaintoen(OpampCsrOpaintoen::OUTPUTPIN); + w.set_opaen(true); }); OpAmpOutput { _inner: self, - _input: pin, + _output: out_pin, } } + + /// Configure the OpAmp as a buffer for the provided input pin, + /// with the output only used internally. + /// + /// The input pin is configured for analogue mode but not consumed, + /// so it may be subsequently used for ADC or comparator inputs. + /// + /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. + #[cfg(opamp_g4)] + pub fn buffer_int<'a, P>(&'a mut self, pin: &P, gain: OpAmpGain) -> OpAmpInternalOutput<'a, T> + where + P: NonInvertingPin + crate::gpio::sealed::Pin, + { + pin.set_as_analog(); + + let (vm_sel, pga_gain) = match gain { + OpAmpGain::Mul1 => (0b11, 0b00), + OpAmpGain::Mul2 => (0b10, 0b00), + OpAmpGain::Mul4 => (0b10, 0b01), + OpAmpGain::Mul8 => (0b10, 0b10), + OpAmpGain::Mul16 => (0b10, 0b11), + }; + + T::regs().opamp_csr().modify(|w| { + use crate::pac::opamp::vals::*; + w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel())); + w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); + w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); + w.set_opaintoen(OpampCsrOpaintoen::ADCCHANNEL); + w.set_opaen(true); + }); + + OpAmpInternalOutput { _inner: self } + } +} + +impl<'d, T: Instance> Drop for OpAmp<'d, T> { + fn drop(&mut self) { + #[cfg(opamp_f3)] + T::regs().opampcsr().modify(|w| { + w.set_opampen(false); + }); + + #[cfg(opamp_g4)] + T::regs().opamp_csr().modify(|w| { + w.set_opaen(false); + }); + } } pub trait Instance: sealed::Instance + 'static {} @@ -92,18 +191,19 @@ pub(crate) mod sealed { pub trait InvertingPin { fn channel(&self) -> u8; } + + pub trait OutputPin {} } pub trait NonInvertingPin: sealed::NonInvertingPin {} - pub trait InvertingPin: sealed::InvertingPin {} +pub trait OutputPin: sealed::OutputPin {} -#[cfg(opamp_f3)] -macro_rules! impl_opamp_output { +macro_rules! impl_opamp_external_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( ($adc, $common_inst:ident, $adc_clock:ident) => { - impl<'d, 'p, P: NonInvertingPin> crate::adc::sealed::AdcPin + impl<'d, 'p, P: OutputPin> crate::adc::sealed::AdcPin for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> { fn channel(&self) -> u8 { @@ -111,7 +211,7 @@ macro_rules! impl_opamp_output { } } - impl<'d, 'p, P: NonInvertingPin> crate::adc::AdcPin + impl<'d, 'p, P: OutputPin> crate::adc::AdcPin for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> { } @@ -120,19 +220,79 @@ macro_rules! impl_opamp_output { }; } -#[cfg(opamp_f3)] foreach_peripheral!( (opamp, OPAMP1) => { - impl_opamp_output!(OPAMP1, ADC1, 3); + impl_opamp_external_output!(OPAMP1, ADC1, 3); }; (opamp, OPAMP2) => { - impl_opamp_output!(OPAMP2, ADC2, 3); + impl_opamp_external_output!(OPAMP2, ADC2, 3); }; (opamp, OPAMP3) => { - impl_opamp_output!(OPAMP3, ADC3, 1); + impl_opamp_external_output!(OPAMP3, ADC3, 1); }; + // OPAMP4 only in STM32G4 Cat 3 devices (opamp, OPAMP4) => { - impl_opamp_output!(OPAMP4, ADC4, 3); + impl_opamp_external_output!(OPAMP4, ADC4, 3); + }; + // OPAMP5 only in STM32G4 Cat 3 devices + (opamp, OPAMP5) => { + impl_opamp_external_output!(OPAMP5, ADC5, 1); + }; + // OPAMP6 only in STM32G4 Cat 3/4 devices + (opamp, OPAMP6) => { + impl_opamp_external_output!(OPAMP6, ADC1, 14); + }; +); + +#[cfg(opamp_g4)] +macro_rules! impl_opamp_internal_output { + ($inst:ident, $adc:ident, $ch:expr) => { + foreach_adc!( + ($adc, $common_inst:ident, $adc_clock:ident) => { + impl<'d> crate::adc::sealed::AdcPin + for OpAmpInternalOutput<'d, crate::peripherals::$inst> + { + fn channel(&self) -> u8 { + $ch + } + } + + impl<'d> crate::adc::AdcPin + for OpAmpInternalOutput<'d, crate::peripherals::$inst> + { + } + }; + ); + }; +} + +#[cfg(opamp_g4)] +foreach_peripheral!( + (opamp, OPAMP1) => { + impl_opamp_internal_output!(OPAMP1, ADC1, 13); + }; + (opamp, OPAMP2) => { + impl_opamp_internal_output!(OPAMP2, ADC2, 16); + }; + (opamp, OPAMP3) => { + impl_opamp_internal_output!(OPAMP3, ADC2, 18); + // Only in Cat 3/4 devices + impl_opamp_internal_output!(OPAMP3, ADC3, 13); + }; + // OPAMP4 only in Cat 3 devices + (opamp, OPAMP4) => { + impl_opamp_internal_output!(OPAMP4, ADC5, 5); + }; + // OPAMP5 only in Cat 3 devices + (opamp, OPAMP5) => { + impl_opamp_internal_output!(OPAMP5, ADC5, 3); + }; + // OPAMP6 only in Cat 3/4 devices + (opamp, OPAMP6) => { + // Only in Cat 3 devices + impl_opamp_internal_output!(OPAMP6, ADC4, 17); + // Only in Cat 4 devices + impl_opamp_internal_output!(OPAMP6, ADC3, 17); }; ); @@ -145,13 +305,12 @@ foreach_peripheral! { } impl Instance for crate::peripherals::$inst { - } }; } #[allow(unused_macros)] -macro_rules! impl_opamp_pin { +macro_rules! impl_opamp_vp_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::opamp::NonInvertingPin for crate::peripherals::$pin {} impl crate::opamp::sealed::NonInvertingPin for crate::peripherals::$pin { @@ -161,3 +320,11 @@ macro_rules! impl_opamp_pin { } }; } + +#[allow(unused_macros)] +macro_rules! impl_opamp_vout_pin { + ($inst:ident, $pin:ident) => { + impl crate::opamp::OutputPin for crate::peripherals::$pin {} + impl crate::opamp::sealed::OutputPin for crate::peripherals::$pin {} + }; +} diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index 128001bf..137fc9e6 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! { let mut vrefint = adc.enable_vref(&mut Delay); let mut temperature = adc.enable_temperature(); - let mut buffer = opamp.buffer_for(&mut p.PA7, OpAmpGain::Mul1); + let mut buffer = opamp.buffer_ext(&p.PA7, &mut p.PA6, OpAmpGain::Mul1); loop { let vref = adc.read(&mut vrefint).await;