1#![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#[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#[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#[allow(missing_docs)]
42#[derive(Clone, Copy, PartialEq)]
43pub enum OpAmpSpeed {
44 Normal,
45 HighSpeed,
46}
47
48pub struct OpAmpOutput<'d, T: Instance> {
52 _inner: &'d OpAmp<'d, T>,
53}
54
55#[cfg(opamp_v5)]
59pub struct OpAmpInternalOutput<'d, T: Instance> {
60 _inner: &'d OpAmp<'d, T>,
61}
62
63pub struct OpAmp<'d, T: Instance> {
65 _inner: Peri<'d, T>,
66}
67
68impl<'d, T: Instance> OpAmp<'d, T> {
69 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 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 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 #[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 #[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 #[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 #[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); 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 #[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); 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 #[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 #[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 #[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 #[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 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#[allow(private_bounds)]
507pub trait Instance: SealedInstance + PeripheralType + 'static {}
508#[allow(private_bounds)]
510pub trait NonInvertingPin<T: Instance>: SealedNonInvertingPin<T> {}
511#[allow(private_bounds)]
513pub trait InvertingPin<T: Instance>: SealedInvertingPin<T> {}
514#[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 (opamp, OPAMP4) => {
552 impl_opamp_external_output!(OPAMP4, ADC1, 11);
553 impl_opamp_external_output!(OPAMP4, ADC4, 3);
554 };
555 (opamp, OPAMP5) => {
557 impl_opamp_external_output!(OPAMP5, ADC5, 1);
558 };
559 (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 impl_opamp_internal_output!(OPAMP3, ADC3, 13);
600 };
601 (opamp, OPAMP4) => {
603 impl_opamp_internal_output!(OPAMP4, ADC5, 5);
604 };
605 (opamp, OPAMP5) => {
607 impl_opamp_internal_output!(OPAMP5, ADC5, 3);
608 };
609 (opamp, OPAMP6) => {
611 impl_opamp_internal_output!(OPAMP6, ADC4, 17);
613 impl_opamp_internal_output!(OPAMP6, ADC3, 17);
615 };
616);
617
618foreach_peripheral! {
619 (opamp, $inst:ident) => {
620 impl SealedInstance for crate::peripherals::$inst {
621 #[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}