embassy_stm32/
opamp.rs

1//! Operational Amplifier (OPAMP)
2#![macro_use]
3
4use embassy_hal_internal::{into_ref, PeripheralRef};
5
6use crate::pac::opamp::vals::*;
7use crate::Peripheral;
8
9/// Gain
10#[allow(missing_docs)]
11#[derive(Clone, Copy)]
12pub enum OpAmpGain {
13    Mul1,
14    Mul2,
15    Mul4,
16    Mul8,
17    Mul16,
18}
19
20/// Speed
21#[allow(missing_docs)]
22#[derive(Clone, Copy)]
23pub enum OpAmpSpeed {
24    Normal,
25    HighSpeed,
26}
27
28#[cfg(opamp_g4)]
29impl From<OpAmpSpeed> for crate::pac::opamp::vals::Opahsm {
30    fn from(v: OpAmpSpeed) -> Self {
31        match v {
32            OpAmpSpeed::Normal => crate::pac::opamp::vals::Opahsm::NORMAL,
33            OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::Opahsm::HIGH_SPEED,
34        }
35    }
36}
37
38/// OpAmp external outputs, wired to a GPIO pad.
39///
40/// This struct can also be used as an ADC input.
41pub struct OpAmpOutput<'d, T: Instance> {
42    _inner: &'d OpAmp<'d, T>,
43}
44
45/// OpAmp internal outputs, wired directly to ADC inputs.
46///
47/// This struct can be used as an ADC input.
48#[cfg(opamp_g4)]
49pub struct OpAmpInternalOutput<'d, T: Instance> {
50    _inner: &'d OpAmp<'d, T>,
51}
52
53/// OpAmp driver.
54pub struct OpAmp<'d, T: Instance> {
55    _inner: PeripheralRef<'d, T>,
56}
57
58impl<'d, T: Instance> OpAmp<'d, T> {
59    /// Create a new driver instance.
60    ///
61    /// Does not enable the opamp, but does set the speed mode on some families.
62    pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self {
63        into_ref!(opamp);
64
65        #[cfg(opamp_g4)]
66        T::regs().csr().modify(|w| {
67            w.set_opahsm(speed.into());
68        });
69
70        Self { _inner: opamp }
71    }
72
73    /// Configure the OpAmp as a buffer for the provided input pin,
74    /// outputting to the provided output pin, and enable the opamp.
75    ///
76    /// The input pin is configured for analogue mode but not consumed,
77    /// so it may subsequently be used for ADC or comparator inputs.
78    ///
79    /// The output pin is held within the returned [`OpAmpOutput`] struct,
80    /// preventing it being used elsewhere. The `OpAmpOutput` can then be
81    /// directly used as an ADC input. The opamp will be disabled when the
82    /// [`OpAmpOutput`] is dropped.
83    pub fn buffer_ext(
84        &mut self,
85        in_pin: impl Peripheral<P = impl NonInvertingPin<T> + crate::gpio::Pin>,
86        out_pin: impl Peripheral<P = impl OutputPin<T> + crate::gpio::Pin>,
87        gain: OpAmpGain,
88    ) -> OpAmpOutput<'_, T> {
89        into_ref!(in_pin);
90        into_ref!(out_pin);
91        in_pin.set_as_analog();
92        out_pin.set_as_analog();
93
94        // PGA_GAIN value may have different meaning in different MCU serials, use with caution.
95        let (vm_sel, pga_gain) = match gain {
96            OpAmpGain::Mul1 => (0b11, 0b00),
97            OpAmpGain::Mul2 => (0b10, 0b00),
98            OpAmpGain::Mul4 => (0b10, 0b01),
99            OpAmpGain::Mul8 => (0b10, 0b10),
100            OpAmpGain::Mul16 => (0b10, 0b11),
101        };
102
103        T::regs().csr().modify(|w| {
104            w.set_vp_sel(VpSel::from_bits(in_pin.channel()));
105            w.set_vm_sel(VmSel::from_bits(vm_sel));
106            w.set_pga_gain(PgaGain::from_bits(pga_gain));
107            #[cfg(opamp_g4)]
108            w.set_opaintoen(Opaintoen::OUTPUT_PIN);
109            w.set_opampen(true);
110        });
111
112        OpAmpOutput { _inner: self }
113    }
114    /// Configure the OpAmp as a buffer for the DAC it is connected to,
115    /// outputting to the provided output pin, and enable the opamp.
116    ///
117    /// The output pin is held within the returned [`OpAmpOutput`] struct,
118    /// preventing it being used elsewhere. The `OpAmpOutput` can then be
119    /// directly used as an ADC input. The opamp will be disabled when the
120    /// [`OpAmpOutput`] is dropped.
121    #[cfg(opamp_g4)]
122    pub fn buffer_dac(
123        &mut self,
124        out_pin: impl Peripheral<P = impl OutputPin<T> + crate::gpio::Pin>,
125    ) -> OpAmpOutput<'_, T> {
126        into_ref!(out_pin);
127        out_pin.set_as_analog();
128
129        T::regs().csr().modify(|w| {
130            use crate::pac::opamp::vals::*;
131
132            w.set_vm_sel(VmSel::OUTPUT);
133            w.set_vp_sel(VpSel::DAC3_CH1);
134            w.set_opaintoen(Opaintoen::OUTPUT_PIN);
135            w.set_opampen(true);
136        });
137
138        OpAmpOutput { _inner: self }
139    }
140
141    /// Configure the OpAmp as a buffer for the provided input pin,
142    /// with the output only used internally, and enable the opamp.
143    ///
144    /// The input pin is configured for analogue mode but not consumed,
145    /// so it may be subsequently used for ADC or comparator inputs.
146    ///
147    /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
148    /// The opamp output will be disabled when it is dropped.
149    #[cfg(opamp_g4)]
150    pub fn buffer_int(
151        &mut self,
152        pin: impl Peripheral<P = impl NonInvertingPin<T> + crate::gpio::Pin>,
153        gain: OpAmpGain,
154    ) -> OpAmpInternalOutput<'_, T> {
155        into_ref!(pin);
156        pin.set_as_analog();
157
158        // PGA_GAIN value may have different meaning in different MCU serials, use with caution.
159        let (vm_sel, pga_gain) = match gain {
160            OpAmpGain::Mul1 => (0b11, 0b00),
161            OpAmpGain::Mul2 => (0b10, 0b00),
162            OpAmpGain::Mul4 => (0b10, 0b01),
163            OpAmpGain::Mul8 => (0b10, 0b10),
164            OpAmpGain::Mul16 => (0b10, 0b11),
165        };
166
167        T::regs().csr().modify(|w| {
168            use crate::pac::opamp::vals::*;
169            w.set_vp_sel(VpSel::from_bits(pin.channel()));
170            w.set_vm_sel(VmSel::from_bits(vm_sel));
171            w.set_pga_gain(PgaGain::from_bits(pga_gain));
172            w.set_opaintoen(Opaintoen::ADCCHANNEL);
173            w.set_opampen(true);
174        });
175
176        OpAmpInternalOutput { _inner: self }
177    }
178}
179
180impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> {
181    fn drop(&mut self) {
182        T::regs().csr().modify(|w| {
183            w.set_opampen(false);
184        });
185    }
186}
187
188#[cfg(opamp_g4)]
189impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> {
190    fn drop(&mut self) {
191        T::regs().csr().modify(|w| {
192            w.set_opampen(false);
193        });
194    }
195}
196
197pub(crate) trait SealedInstance {
198    fn regs() -> crate::pac::opamp::Opamp;
199}
200
201pub(crate) trait SealedNonInvertingPin<T: Instance> {
202    fn channel(&self) -> u8;
203}
204
205pub(crate) trait SealedInvertingPin<T: Instance> {
206    #[allow(unused)]
207    fn channel(&self) -> u8;
208}
209
210pub(crate) trait SealedOutputPin<T: Instance> {}
211
212/// Opamp instance trait.
213#[allow(private_bounds)]
214pub trait Instance: SealedInstance + 'static {}
215/// Non-inverting pin trait.
216#[allow(private_bounds)]
217pub trait NonInvertingPin<T: Instance>: SealedNonInvertingPin<T> {}
218/// Inverting pin trait.
219#[allow(private_bounds)]
220pub trait InvertingPin<T: Instance>: SealedInvertingPin<T> {}
221/// Output pin trait.
222#[allow(private_bounds)]
223pub trait OutputPin<T: Instance>: SealedOutputPin<T> {}
224
225macro_rules! impl_opamp_external_output {
226    ($inst:ident, $adc:ident, $ch:expr) => {
227        foreach_adc!(
228            ($adc, $common_inst:ident, $adc_clock:ident) => {
229                impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>
230                    for OpAmpOutput<'d, crate::peripherals::$inst>
231                {
232                    fn channel(&self) -> u8 {
233                        $ch
234                    }
235                }
236
237                impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>
238                    for OpAmpOutput<'d, crate::peripherals::$inst>
239                {
240                }
241            };
242        );
243    };
244}
245
246foreach_peripheral!(
247    (opamp, OPAMP1) => {
248        impl_opamp_external_output!(OPAMP1, ADC1, 3);
249    };
250    (opamp, OPAMP2) => {
251        impl_opamp_external_output!(OPAMP2, ADC2, 3);
252    };
253    (opamp, OPAMP3) => {
254        impl_opamp_external_output!(OPAMP3, ADC1, 12);
255        impl_opamp_external_output!(OPAMP3, ADC3, 1);
256    };
257    // OPAMP4 only in STM32G4 Cat 3 devices
258    (opamp, OPAMP4) => {
259        impl_opamp_external_output!(OPAMP4, ADC1, 11);
260        impl_opamp_external_output!(OPAMP4, ADC4, 3);
261    };
262    // OPAMP5 only in STM32G4 Cat 3 devices
263    (opamp, OPAMP5) => {
264        impl_opamp_external_output!(OPAMP5, ADC5, 1);
265    };
266    // OPAMP6 only in STM32G4 Cat 3/4 devices
267    (opamp, OPAMP6) => {
268        impl_opamp_external_output!(OPAMP6, ADC1, 14);
269        impl_opamp_external_output!(OPAMP6, ADC2, 14);
270    };
271);
272
273#[cfg(opamp_g4)]
274macro_rules! impl_opamp_internal_output {
275    ($inst:ident, $adc:ident, $ch:expr) => {
276        foreach_adc!(
277            ($adc, $common_inst:ident, $adc_clock:ident) => {
278                impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>
279                    for OpAmpInternalOutput<'d, crate::peripherals::$inst>
280                {
281                    fn channel(&self) -> u8 {
282                        $ch
283                    }
284                }
285
286                impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>
287                    for OpAmpInternalOutput<'d, crate::peripherals::$inst>
288                {
289                }
290            };
291        );
292    };
293}
294
295#[cfg(opamp_g4)]
296foreach_peripheral!(
297    (opamp, OPAMP1) => {
298        impl_opamp_internal_output!(OPAMP1, ADC1, 13);
299    };
300    (opamp, OPAMP2) => {
301        impl_opamp_internal_output!(OPAMP2, ADC2, 16);
302    };
303    (opamp, OPAMP3) => {
304        impl_opamp_internal_output!(OPAMP3, ADC2, 18);
305        // Only in Cat 3/4 devices
306        impl_opamp_internal_output!(OPAMP3, ADC3, 13);
307    };
308    // OPAMP4 only in Cat 3 devices
309    (opamp, OPAMP4) => {
310        impl_opamp_internal_output!(OPAMP4, ADC5, 5);
311    };
312    // OPAMP5 only in Cat 3 devices
313    (opamp, OPAMP5) => {
314        impl_opamp_internal_output!(OPAMP5, ADC5, 3);
315    };
316    // OPAMP6 only in Cat 3/4 devices
317    (opamp, OPAMP6) => {
318        // Only in Cat 3 devices
319        impl_opamp_internal_output!(OPAMP6, ADC4, 17);
320        // Only in Cat 4 devices
321        impl_opamp_internal_output!(OPAMP6, ADC3, 17);
322    };
323);
324
325foreach_peripheral! {
326    (opamp, $inst:ident) => {
327        impl SealedInstance for crate::peripherals::$inst {
328            fn regs() -> crate::pac::opamp::Opamp {
329                crate::pac::$inst
330            }
331        }
332
333        impl Instance for crate::peripherals::$inst {
334        }
335    };
336}
337
338#[allow(unused_macros)]
339macro_rules! impl_opamp_vp_pin {
340    ($inst:ident, $pin:ident, $ch:expr) => {
341        impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
342        impl crate::opamp::SealedNonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
343            fn channel(&self) -> u8 {
344                $ch
345            }
346        }
347    };
348}
349
350#[allow(unused_macros)]
351macro_rules! impl_opamp_vout_pin {
352    ($inst:ident, $pin:ident) => {
353        impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
354        impl crate::opamp::SealedOutputPin<peripherals::$inst> for crate::peripherals::$pin {}
355    };
356}