//! Operational Amplifier (OPAMP) #![macro_use] use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::Peripheral; /// Gain #[allow(missing_docs)] #[derive(Clone, Copy)] pub enum OpAmpGain { Mul1, Mul2, Mul4, Mul8, Mul16, } /// Speed #[allow(missing_docs)] #[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. /// /// This struct can also be used as an ADC input. pub struct OpAmpOutput<'d, T: Instance> { _inner: &'d OpAmp<'d, T>, } /// 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> { /// Create a new driver instance. /// /// Does not enable the opamp, but does set the speed mode on some families. pub fn new(opamp: impl Peripheral

+ 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { into_ref!(opamp); #[cfg(opamp_g4)] T::regs().opamp_csr().modify(|w| { w.set_opahsm(speed.into()); }); Self { _inner: opamp } } /// Configure the OpAmp as a buffer for the provided input pin, /// outputting to the provided output pin, and enable the opamp. /// /// 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. The opamp will be disabled when the /// [`OpAmpOutput`] is dropped. pub fn buffer_ext( &'d mut self, in_pin: impl Peripheral

+ crate::gpio::sealed::Pin>, out_pin: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, gain: OpAmpGain, ) -> OpAmpOutput<'d, T> { into_ref!(in_pin); into_ref!(out_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), OpAmpGain::Mul4 => (0b10, 0b01), OpAmpGain::Mul8 => (0b10, 0b10), OpAmpGain::Mul16 => (0b10, 0b11), }; #[cfg(opamp_f3)] T::regs().opampcsr().modify(|w| { 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(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 } } /// Configure the OpAmp as a buffer for the provided input pin, /// with the output only used internally, and enable the opamp. /// /// 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. /// The opamp output will be disabled when it is dropped. #[cfg(opamp_g4)] pub fn buffer_int( &'d mut self, pin: impl Peripheral

+ crate::gpio::sealed::Pin>, gain: OpAmpGain, ) -> OpAmpInternalOutput<'d, T> { into_ref!(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 OpAmpOutput<'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); }); } } impl<'d, T: Instance> Drop for OpAmpInternalOutput<'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); }); } } /// Opamp instance trait. 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 OutputPin {} } /// Non-inverting pin trait. pub trait NonInvertingPin: sealed::NonInvertingPin {} /// Inverting pin trait. pub trait InvertingPin: sealed::InvertingPin {} /// Output pin trait. pub trait OutputPin: sealed::OutputPin {} macro_rules! impl_opamp_external_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( ($adc, $common_inst:ident, $adc_clock:ident) => { impl<'d> crate::adc::sealed::AdcPin for OpAmpOutput<'d, crate::peripherals::$inst> { fn channel(&self) -> u8 { $ch } } impl<'d> crate::adc::AdcPin for OpAmpOutput<'d, crate::peripherals::$inst> { } }; ); }; } foreach_peripheral!( (opamp, OPAMP1) => { impl_opamp_external_output!(OPAMP1, ADC1, 3); }; (opamp, OPAMP2) => { impl_opamp_external_output!(OPAMP2, ADC2, 3); }; (opamp, OPAMP3) => { impl_opamp_external_output!(OPAMP3, ADC3, 1); }; // OPAMP4 only in STM32G4 Cat 3 devices (opamp, OPAMP4) => { 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); }; ); 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_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 { fn channel(&self) -> u8 { $ch } } }; } #[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 {} }; }