embassy_stm32/
opamp.rs

1//! Operational Amplifier (OPAMP)
2#![macro_use]
3
4use embassy_hal_internal::PeripheralType;
5
6use crate::pac::opamp::vals::*;
7#[cfg(not(any(stm32g4, stm32f3)))]
8use crate::rcc::RccInfo;
9use crate::Peri;
10
11/// Performs a busy-wait delay for a specified number of microseconds.
12#[cfg(opamp_v5)]
13fn blocking_delay_ms(ms: u32) {
14    #[cfg(feature = "time")]
15    embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64));
16    #[cfg(not(feature = "time"))]
17    cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms);
18}
19
20/// Gain
21#[allow(missing_docs)]
22#[derive(Clone, Copy)]
23pub enum OpAmpGain {
24    Mul2,
25    Mul4,
26    Mul8,
27    Mul16,
28    #[cfg(opamp_v5)]
29    Mul32,
30    #[cfg(opamp_v5)]
31    Mul64,
32}
33
34#[cfg(opamp_v5)]
35enum OpAmpDifferentialPair {
36    P,
37    N,
38}
39
40/// Speed
41#[allow(missing_docs)]
42#[derive(Clone, Copy, PartialEq)]
43pub enum OpAmpSpeed {
44    Normal,
45    HighSpeed,
46}
47
48/// OpAmp external outputs, wired to a GPIO pad.
49///
50/// This struct can also be used as an ADC input.
51pub struct OpAmpOutput<'d, T: Instance> {
52    _inner: &'d OpAmp<'d, T>,
53}
54
55/// OpAmp internal outputs, wired directly to ADC inputs.
56///
57/// This struct can be used as an ADC input.
58#[cfg(opamp_v5)]
59pub struct OpAmpInternalOutput<'d, T: Instance> {
60    _inner: &'d OpAmp<'d, T>,
61}
62
63/// OpAmp driver.
64pub struct OpAmp<'d, T: Instance> {
65    _inner: Peri<'d, T>,
66}
67
68impl<'d, T: Instance> OpAmp<'d, T> {
69    /// Create a new driver instance.
70    ///
71    /// Does not enable the opamp, but does set the speed mode on some families.
72    pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_v5)] speed: OpAmpSpeed) -> Self {
73        #[cfg(not(any(stm32g4, stm32f3)))]
74        T::info().rcc.enable_and_reset();
75        #[cfg(opamp_v5)]
76        T::regs().csr().modify(|w| {
77            w.set_opahsm(speed == OpAmpSpeed::HighSpeed);
78        });
79
80        Self { _inner: opamp }
81    }
82
83    /// Configure the OpAmp as a buffer for the provided input pin,
84    /// outputting to the provided output pin, and enable the opamp.
85    ///
86    /// The input pin is configured for analogue mode but not consumed,
87    /// so it may subsequently be used for ADC or comparator inputs.
88    ///
89    /// The output pin is held within the returned [`OpAmpOutput`] struct,
90    /// preventing it being used elsewhere. The `OpAmpOutput` can then be
91    /// directly used as an ADC input. The opamp will be disabled when the
92    /// [`OpAmpOutput`] is dropped.
93    pub fn buffer_ext(
94        &mut self,
95        in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,
96        out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>,
97    ) -> OpAmpOutput<'_, T> {
98        in_pin.set_as_analog();
99        out_pin.set_as_analog();
100
101        #[cfg(opamp_v5)]
102        let vm_sel = VmSel::OUTPUT;
103        #[cfg(not(opamp_v5))]
104        let vm_sel = VmSel::from_bits(0b11);
105
106        T::regs().csr().modify(|w| {
107            w.set_vp_sel(VpSel::from_bits(in_pin.channel()));
108            w.set_vm_sel(vm_sel);
109            #[cfg(opamp_v5)]
110            w.set_opaintoen(false);
111            w.set_opampen(true);
112        });
113
114        OpAmpOutput { _inner: self }
115    }
116
117    /// Configure the OpAmp as a PGA for the provided input pin,
118    /// outputting to the provided output pin, and enable the opamp.
119    ///
120    /// The input pin is configured for analogue mode but not consumed,
121    /// so it may subsequently be used for ADC or comparator inputs.
122    ///
123    /// The output pin is held within the returned [`OpAmpOutput`] struct,
124    /// preventing it being used elsewhere. The `OpAmpOutput` can then be
125    /// directly used as an ADC input. The opamp will be disabled when the
126    /// [`OpAmpOutput`] is dropped.
127    pub fn pga_ext(
128        &mut self,
129        in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,
130        out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>,
131        gain: OpAmpGain,
132    ) -> OpAmpOutput<'_, T> {
133        in_pin.set_as_analog();
134        out_pin.set_as_analog();
135
136        #[cfg(opamp_v5)]
137        let vm_sel = VmSel::PGA;
138        #[cfg(not(opamp_v5))]
139        let vm_sel = VmSel::from_bits(0b10);
140
141        #[cfg(opamp_v5)]
142        let pga_gain = match gain {
143            OpAmpGain::Mul2 => PgaGain::GAIN2,
144            OpAmpGain::Mul4 => PgaGain::GAIN4,
145            OpAmpGain::Mul8 => PgaGain::GAIN8,
146            OpAmpGain::Mul16 => PgaGain::GAIN16,
147            OpAmpGain::Mul32 => PgaGain::GAIN32,
148            OpAmpGain::Mul64 => PgaGain::GAIN64,
149        };
150        #[cfg(not(opamp_v5))]
151        let pga_gain = PgaGain::from_bits(match gain {
152            OpAmpGain::Mul2 => 0b00,
153            OpAmpGain::Mul4 => 0b01,
154            OpAmpGain::Mul8 => 0b10,
155            OpAmpGain::Mul16 => 0b11,
156        });
157
158        T::regs().csr().modify(|w| {
159            w.set_vp_sel(VpSel::from_bits(in_pin.channel()));
160            w.set_vm_sel(vm_sel);
161            w.set_pga_gain(pga_gain);
162            #[cfg(opamp_v5)]
163            w.set_opaintoen(false);
164            w.set_opampen(true);
165        });
166
167        OpAmpOutput { _inner: self }
168    }
169
170    /// Configure the OpAmp as a buffer for the DAC it is connected to,
171    /// outputting to the provided output pin, and enable the opamp.
172    ///
173    /// The output pin is held within the returned [`OpAmpOutput`] struct,
174    /// preventing it being used elsewhere. The `OpAmpOutput` can then be
175    /// directly used as an ADC input. The opamp will be disabled when the
176    /// [`OpAmpOutput`] is dropped.
177    #[cfg(opamp_v5)]
178    pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>) -> OpAmpOutput<'_, T> {
179        out_pin.set_as_analog();
180
181        T::regs().csr().modify(|w| {
182            use crate::pac::opamp::vals::*;
183
184            w.set_vm_sel(VmSel::OUTPUT);
185            w.set_vp_sel(VpSel::DAC3_CH1);
186            w.set_opaintoen(false);
187            w.set_opampen(true);
188        });
189
190        OpAmpOutput { _inner: self }
191    }
192
193    /// Configure the OpAmp as a buffer for the provided input pin,
194    /// with the output only used internally, and enable the opamp.
195    ///
196    /// The input pin is configured for analogue mode but not consumed,
197    /// so it may be subsequently used for ADC or comparator inputs.
198    ///
199    /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
200    /// The opamp output will be disabled when it is dropped.
201    #[cfg(opamp_v5)]
202    pub fn buffer_int(
203        &mut self,
204        pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,
205    ) -> OpAmpInternalOutput<'_, T> {
206        pin.set_as_analog();
207
208        T::regs().csr().modify(|w| {
209            w.set_vp_sel(VpSel::from_bits(pin.channel()));
210            w.set_vm_sel(VmSel::OUTPUT);
211            #[cfg(opamp_v5)]
212            w.set_opaintoen(true);
213            w.set_opampen(true);
214        });
215
216        OpAmpInternalOutput { _inner: self }
217    }
218
219    /// Configure the OpAmp as a PGA for the provided input pin,
220    /// with the output only used internally, and enable the opamp.
221    ///
222    /// The input pin is configured for analogue mode but not consumed,
223    /// so it may be subsequently used for ADC or comparator inputs.
224    ///
225    /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
226    /// The opamp output will be disabled when it is dropped.
227    #[cfg(opamp_v5)]
228    pub fn pga_int(
229        &mut self,
230        pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,
231        gain: OpAmpGain,
232    ) -> OpAmpInternalOutput<'_, T> {
233        pin.set_as_analog();
234
235        let pga_gain = match gain {
236            OpAmpGain::Mul2 => PgaGain::GAIN2,
237            OpAmpGain::Mul4 => PgaGain::GAIN4,
238            OpAmpGain::Mul8 => PgaGain::GAIN8,
239            OpAmpGain::Mul16 => PgaGain::GAIN16,
240            OpAmpGain::Mul32 => PgaGain::GAIN32,
241            OpAmpGain::Mul64 => PgaGain::GAIN64,
242        };
243
244        T::regs().csr().modify(|w| {
245            w.set_vp_sel(VpSel::from_bits(pin.channel()));
246            w.set_vm_sel(VmSel::PGA);
247            w.set_pga_gain(pga_gain);
248            w.set_opaintoen(true);
249            w.set_opampen(true);
250        });
251
252        OpAmpInternalOutput { _inner: self }
253    }
254
255    /// Configure the OpAmp as a standalone DAC with the inverting input
256    /// connected to the provided pin, and the output is connected
257    /// internally to an ADC channel.
258    ///
259    /// The input pin is configured for analogue mode but not consumed,
260    /// so it may be subsequently used for ADC or comparator inputs.
261    ///
262    /// The returned `OpAmpInternalOutput` struct may be used as an ADC
263    /// input. The opamp output will be disabled when it is dropped.
264    #[cfg(opamp_v5)]
265    pub fn standalone_dac_int(
266        &mut self,
267        m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>,
268    ) -> OpAmpInternalOutput<'_, T> {
269        m_pin.set_as_analog();
270
271        T::regs().csr().modify(|w| {
272            use crate::pac::opamp::vals::*;
273            w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx
274            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));
275            w.set_opaintoen(true);
276            w.set_opampen(true);
277        });
278
279        OpAmpInternalOutput { _inner: self }
280    }
281
282    /// Configure the OpAmp as a standalone DAC with the inverting input
283    /// connected to the provided pin, and the output connected to the
284    /// provided pin.
285    ///
286    /// The input pin is configured for analogue mode but not consumed,
287    /// so it may be subsequently used for ADC or comparator inputs.
288    ///
289    /// The output pin is held within the returned [`OpAmpOutput`] struct,
290    /// preventing it being used elsewhere. The opamp will be disabled when
291    /// the [`OpAmpOutput`] is dropped.
292    #[cfg(opamp_v5)]
293    pub fn standalone_dac_ext(
294        &mut self,
295        m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>,
296        out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>,
297    ) -> OpAmpOutput<'_, T> {
298        m_pin.set_as_analog();
299        out_pin.set_as_analog();
300
301        T::regs().csr().modify(|w| {
302            use crate::pac::opamp::vals::*;
303            w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx
304            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));
305            w.set_opaintoen(false);
306            w.set_opampen(true);
307        });
308
309        OpAmpOutput { _inner: self }
310    }
311
312    /// Configure the OpAmp in standalone mode with the non-inverting input
313    /// connected to the provided `p_pin`, the inverting input connected to
314    /// the `m_pin`, and output to the provided `out_pin`.
315    ///
316    /// The input pins are configured for analogue mode but not consumed,
317    /// allowing their subsequent use for ADC or comparator inputs.
318    ///
319    /// The output pin is held within the returned [`OpAmpOutput`] struct,
320    /// preventing it being used elsewhere. The opamp will be disabled when
321    /// the [`OpAmpOutput`] is dropped.
322    #[cfg(opamp_v5)]
323    pub fn standalone_ext(
324        &mut self,
325        p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>,
326        m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>,
327        out_pin: Peri<'d, impl OutputPin<T> + crate::gpio::Pin>,
328    ) -> OpAmpOutput<'_, T> {
329        p_pin.set_as_analog();
330        m_pin.set_as_analog();
331        out_pin.set_as_analog();
332
333        T::regs().csr().modify(|w| {
334            use crate::pac::opamp::vals::*;
335            w.set_vp_sel(VpSel::from_bits(p_pin.channel()));
336            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));
337            w.set_opaintoen(false);
338            w.set_opampen(true);
339        });
340
341        OpAmpOutput { _inner: self }
342    }
343
344    /// Configure the OpAmp in standalone mode with the non-inverting input
345    /// connected to the provided `p_pin`, the inverting input connected to
346    /// the `m_pin`, and output is connected to the DAC.
347    ///
348    /// The input pins are configured for analogue mode but not consumed,
349    /// allowing their subsequent use for ADC or comparator inputs.
350    ///
351    /// The returned `OpAmpOutput` struct may be used as an ADC
352    /// input. The opamp output will be disabled when it is dropped.
353    #[cfg(opamp_v5)]
354    pub fn standalone_int(
355        &mut self,
356        p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>,
357        m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>,
358    ) -> OpAmpOutput<'_, T> {
359        p_pin.set_as_analog();
360        m_pin.set_as_analog();
361
362        T::regs().csr().modify(|w| {
363            use crate::pac::opamp::vals::*;
364            w.set_vp_sel(VpSel::from_bits(p_pin.channel()));
365            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));
366            w.set_opaintoen(true);
367            w.set_opampen(true);
368        });
369
370        OpAmpOutput { _inner: self }
371    }
372
373    /// Calibrates the operational amplifier.
374    ///
375    /// This function enables the opamp and sets the user trim mode for calibration.
376    /// Depending on the speed mode of the opamp, it calibrates the differential pair inputs.
377    /// For normal speed, both the P and N differential pairs are calibrated,
378    /// while for high-speed mode, only the P differential pair is calibrated.
379    ///
380    /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method).
381    #[cfg(opamp_v5)]
382    pub fn calibrate(&mut self) {
383        T::regs().csr().modify(|w| {
384            w.set_opampen(true);
385            w.set_calon(true);
386            w.set_usertrim(true);
387        });
388
389        if T::regs().csr().read().opahsm() {
390            self.calibrate_differential_pair(OpAmpDifferentialPair::P);
391        } else {
392            self.calibrate_differential_pair(OpAmpDifferentialPair::P);
393            self.calibrate_differential_pair(OpAmpDifferentialPair::N);
394        }
395
396        T::regs().csr().modify(|w| {
397            w.set_calon(false);
398            w.set_opampen(false);
399        });
400    }
401
402    /// Calibrate differential pair.
403    ///
404    /// The calibration is done by trying different offset values and
405    /// measuring the outcal bit.
406    ///
407    /// The calibration range is from 0 to 31.
408    ///
409    /// The result is stored in the OPAMP_CSR register.
410    #[cfg(opamp_v5)]
411    fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) {
412        let mut low = 0;
413        let mut high = 31;
414
415        let calsel = match pair {
416            OpAmpDifferentialPair::P => Calsel::PERCENT10,
417            OpAmpDifferentialPair::N => Calsel::PERCENT90,
418        };
419
420        T::regs().csr().modify(|w| {
421            w.set_calsel(calsel);
422        });
423
424        while low <= high {
425            let mid = (low + high) / 2;
426
427            T::regs().csr().modify(|w| match pair {
428                OpAmpDifferentialPair::P => {
429                    #[cfg(feature = "defmt")]
430                    defmt::debug!("opamp p calibration. offset: {}", mid);
431                    w.set_trimoffsetp(mid);
432                }
433                OpAmpDifferentialPair::N => {
434                    #[cfg(feature = "defmt")]
435                    defmt::debug!("opamp n calibration. offset: {}", mid);
436                    w.set_trimoffsetn(mid);
437                }
438            });
439
440            // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize
441            // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7
442            blocking_delay_ms(2);
443
444            if !T::regs().csr().read().calout() {
445                if mid == 0 {
446                    break;
447                }
448                high = mid - 1;
449            } else {
450                if mid == 31 {
451                    break;
452                }
453                low = mid + 1;
454            }
455        }
456    }
457}
458
459#[cfg(not(any(stm32g4, stm32f3)))]
460impl<'d, T: Instance> Drop for OpAmp<'d, T> {
461    fn drop(&mut self) {
462        T::info().rcc.disable();
463    }
464}
465
466impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> {
467    fn drop(&mut self) {
468        T::regs().csr().modify(|w| {
469            w.set_opampen(false);
470        });
471    }
472}
473
474#[cfg(opamp_v5)]
475impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> {
476    fn drop(&mut self) {
477        T::regs().csr().modify(|w| {
478            w.set_opampen(false);
479        });
480    }
481}
482
483#[cfg(not(any(stm32g4, stm32f3)))]
484pub(crate) struct Info {
485    rcc: RccInfo,
486}
487
488pub(crate) trait SealedInstance {
489    #[cfg(not(any(stm32g4, stm32f3)))]
490    fn info() -> &'static Info;
491    fn regs() -> crate::pac::opamp::Opamp;
492}
493
494pub(crate) trait SealedNonInvertingPin<T: Instance> {
495    fn channel(&self) -> u8;
496}
497
498pub(crate) trait SealedInvertingPin<T: Instance> {
499    #[allow(unused)]
500    fn channel(&self) -> u8;
501}
502
503pub(crate) trait SealedOutputPin<T: Instance> {}
504
505/// Opamp instance trait.
506#[allow(private_bounds)]
507pub trait Instance: SealedInstance + PeripheralType + 'static {}
508/// Non-inverting pin trait.
509#[allow(private_bounds)]
510pub trait NonInvertingPin<T: Instance>: SealedNonInvertingPin<T> {}
511/// Inverting pin trait.
512#[allow(private_bounds)]
513pub trait InvertingPin<T: Instance>: SealedInvertingPin<T> {}
514/// Output pin trait.
515#[allow(private_bounds)]
516pub trait OutputPin<T: Instance>: SealedOutputPin<T> {}
517
518macro_rules! impl_opamp_external_output {
519    ($inst:ident, $adc:ident, $ch:expr) => {
520        foreach_adc!(
521            ($adc, $common_inst:ident, $adc_clock:ident) => {
522                impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>
523                    for OpAmpOutput<'d, crate::peripherals::$inst>
524                {
525                    fn channel(&self) -> u8 {
526                        $ch
527                    }
528                }
529
530                impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>
531                    for OpAmpOutput<'d, crate::peripherals::$inst>
532                {
533                }
534            };
535        );
536    };
537}
538
539foreach_peripheral!(
540    (opamp, OPAMP1) => {
541        impl_opamp_external_output!(OPAMP1, ADC1, 3);
542    };
543    (opamp, OPAMP2) => {
544        impl_opamp_external_output!(OPAMP2, ADC2, 3);
545    };
546    (opamp, OPAMP3) => {
547        impl_opamp_external_output!(OPAMP3, ADC1, 12);
548        impl_opamp_external_output!(OPAMP3, ADC3, 1);
549    };
550    // OPAMP4 only in STM32G4 Cat 3 devices
551    (opamp, OPAMP4) => {
552        impl_opamp_external_output!(OPAMP4, ADC1, 11);
553        impl_opamp_external_output!(OPAMP4, ADC4, 3);
554    };
555    // OPAMP5 only in STM32G4 Cat 3 devices
556    (opamp, OPAMP5) => {
557        impl_opamp_external_output!(OPAMP5, ADC5, 1);
558    };
559    // OPAMP6 only in STM32G4 Cat 3/4 devices
560    (opamp, OPAMP6) => {
561        impl_opamp_external_output!(OPAMP6, ADC1, 14);
562        impl_opamp_external_output!(OPAMP6, ADC2, 14);
563    };
564);
565
566#[cfg(opamp_v5)]
567macro_rules! impl_opamp_internal_output {
568    ($inst:ident, $adc:ident, $ch:expr) => {
569        foreach_adc!(
570            ($adc, $common_inst:ident, $adc_clock:ident) => {
571                impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>
572                    for OpAmpInternalOutput<'d, crate::peripherals::$inst>
573                {
574                    fn channel(&self) -> u8 {
575                        $ch
576                    }
577                }
578
579                impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>
580                    for OpAmpInternalOutput<'d, crate::peripherals::$inst>
581                {
582                }
583            };
584        );
585    };
586}
587
588#[cfg(opamp_v5)]
589foreach_peripheral!(
590    (opamp, OPAMP1) => {
591        impl_opamp_internal_output!(OPAMP1, ADC1, 13);
592    };
593    (opamp, OPAMP2) => {
594        impl_opamp_internal_output!(OPAMP2, ADC2, 16);
595    };
596    (opamp, OPAMP3) => {
597        impl_opamp_internal_output!(OPAMP3, ADC2, 18);
598        // Only in Cat 3/4 devices
599        impl_opamp_internal_output!(OPAMP3, ADC3, 13);
600    };
601    // OPAMP4 only in Cat 3 devices
602    (opamp, OPAMP4) => {
603        impl_opamp_internal_output!(OPAMP4, ADC5, 5);
604    };
605    // OPAMP5 only in Cat 3 devices
606    (opamp, OPAMP5) => {
607        impl_opamp_internal_output!(OPAMP5, ADC5, 3);
608    };
609    // OPAMP6 only in Cat 3/4 devices
610    (opamp, OPAMP6) => {
611        // Only in Cat 3 devices
612        impl_opamp_internal_output!(OPAMP6, ADC4, 17);
613        // Only in Cat 4 devices
614        impl_opamp_internal_output!(OPAMP6, ADC3, 17);
615    };
616);
617
618foreach_peripheral! {
619    (opamp, $inst:ident) => {
620        impl SealedInstance for crate::peripherals::$inst {
621            // G4 and F3 use SYSCFGEN, which is always enabled
622            #[cfg(not(any(stm32g4, stm32f3)))]
623            fn info() -> &'static Info {
624                use crate::rcc::SealedRccPeripheral;
625                static INFO: Info = Info {
626                    rcc: crate::peripherals::$inst::RCC_INFO,
627                };
628                &INFO
629            }
630            fn regs() -> crate::pac::opamp::Opamp {
631                crate::pac::$inst
632            }
633        }
634
635        impl Instance for crate::peripherals::$inst {
636        }
637    };
638}
639
640#[allow(unused_macros)]
641macro_rules! impl_opamp_vp_pin {
642    ($inst:ident, $pin:ident, $ch:expr) => {
643        impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
644        impl crate::opamp::SealedNonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
645            fn channel(&self) -> u8 {
646                $ch
647            }
648        }
649    };
650}
651
652#[allow(unused_macros)]
653macro_rules! impl_opamp_vn_pin {
654    ($inst:ident, $pin:ident, $ch:expr) => {
655        impl crate::opamp::InvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
656        impl crate::opamp::SealedInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
657            fn channel(&self) -> u8 {
658                $ch
659            }
660        }
661    };
662}
663
664#[allow(unused_macros)]
665macro_rules! impl_opamp_vout_pin {
666    ($inst:ident, $pin:ident) => {
667        impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
668        impl crate::opamp::SealedOutputPin<peripherals::$inst> for crate::peripherals::$pin {}
669    };
670}