stm32g0xx_hal/analog/
comparator.rs

1//! Comparator
2
3use core::marker::PhantomData;
4
5use crate::analog::dac;
6use crate::exti::{Event as ExtiEvent, ExtiExt};
7use crate::gpio::*;
8use crate::rcc::{Clocks, Rcc};
9use crate::stm32::comp::{COMP1_CSR, COMP2_CSR};
10use crate::stm32::{COMP, EXTI};
11
12/// Enabled Comparator (type state)
13pub struct Enabled;
14
15/// Disabled Comparator (type state)
16pub struct Disabled;
17
18pub trait ED {}
19impl ED for Enabled {}
20impl ED for Disabled {}
21
22pub struct COMP1 {
23    _rb: PhantomData<()>,
24}
25
26impl COMP1 {
27    pub fn csr(&self) -> &COMP1_CSR {
28        // SAFETY: The COMP1 type is only constructed with logical ownership of
29        // these registers.
30        &unsafe { &*COMP::ptr() }.comp1_csr
31    }
32}
33
34pub struct COMP2 {
35    _rb: PhantomData<()>,
36}
37
38impl COMP2 {
39    pub fn csr(&self) -> &COMP2_CSR {
40        // SAFETY: The COMP1 type is only constructed with logical ownership of
41        // these registers.
42        &unsafe { &*COMP::ptr() }.comp2_csr
43    }
44}
45
46// TODO: Split COMP in PAC
47// TODO: COMP3 for STM32G0Bxx etc.
48
49#[derive(Copy, Clone, Eq, PartialEq)]
50pub struct Config {
51    power_mode: PowerMode,
52    hysteresis: Hysteresis,
53    inverted: bool,
54    output_xor: bool,
55}
56
57impl Default for Config {
58    fn default() -> Self {
59        Self {
60            hysteresis: Hysteresis::None,
61            inverted: false,
62            power_mode: PowerMode::HighSpeed,
63            output_xor: false,
64        }
65    }
66}
67
68impl Config {
69    pub fn hysteresis(mut self, hysteresis: Hysteresis) -> Self {
70        self.hysteresis = hysteresis;
71        self
72    }
73
74    pub fn output_inverted(mut self) -> Self {
75        self.inverted = true;
76        self
77    }
78
79    pub fn output_polarity(mut self, inverted: bool) -> Self {
80        self.inverted = inverted;
81        self
82    }
83
84    pub fn power_mode(mut self, power_mode: PowerMode) -> Self {
85        self.power_mode = power_mode;
86        self
87    }
88
89    /// Sets the output to be Comparator 1 XOR Comparator 2.
90    /// Used to implement window comparator mode.
91    pub fn output_xor(mut self) -> Self {
92        self.output_xor = true;
93        self
94    }
95}
96
97#[derive(Copy, Clone, Eq, PartialEq)]
98pub enum Hysteresis {
99    None = 0b00,
100    Low = 0b01,
101    Medium = 0b10,
102    High = 0b11,
103}
104
105#[derive(Copy, Clone, Eq, PartialEq)]
106pub enum PowerMode {
107    HighSpeed = 0b00,
108    MediumSpeed = 0b01,
109}
110
111/// Comparator positive input
112pub trait PositiveInput<C> {
113    fn setup(&self, comp: &C);
114}
115
116/// Comparator negative input
117pub trait NegativeInput<C> {
118    fn setup(&self, comp: &C);
119}
120
121/// Comparator negative input open (not connected)
122#[derive(Copy, Clone, Eq, PartialEq)]
123pub struct Open;
124
125/// Comparator 1 positive input used as positive input for Comparator 2.
126/// Used to implement window comparator mode.
127#[derive(Copy, Clone, Eq, PartialEq)]
128pub struct Comp1InP;
129
130/// Comparator 2 positive input used as positive input for Comparator 1.
131/// Used to implement window comparator mode.
132#[derive(Copy, Clone, Eq, PartialEq)]
133pub struct Comp2InP;
134
135macro_rules! window_input_pin {
136    ($COMP:ident, $pin:ty) => {
137        impl PositiveInput<$COMP> for $pin {
138            fn setup(&self, comp: &$COMP) {
139                comp.csr().modify(|_, w| w.winmode().set_bit())
140            }
141        }
142    };
143}
144
145window_input_pin!(COMP1, Comp2InP);
146window_input_pin!(COMP2, Comp1InP);
147
148macro_rules! positive_input_pin {
149    ($COMP:ident, $pin:ty, $bits:expr) => {
150        impl PositiveInput<$COMP> for $pin {
151            fn setup(&self, comp: &$COMP) {
152                comp.csr().modify(|_, w| unsafe { w.inpsel().bits($bits) })
153            }
154        }
155    };
156}
157
158positive_input_pin!(COMP1, PC5<Analog>, 0b00);
159positive_input_pin!(COMP1, PB2<Analog>, 0b01);
160positive_input_pin!(COMP1, PA1<Analog>, 0b10);
161positive_input_pin!(COMP1, Open, 0b11);
162
163positive_input_pin!(COMP2, PB4<Analog>, 0b00);
164positive_input_pin!(COMP2, PB6<Analog>, 0b01);
165positive_input_pin!(COMP2, PA3<Analog>, 0b10);
166positive_input_pin!(COMP2, Open, 0b11);
167
168macro_rules! negative_input_pin {
169    ($COMP:ident, $pin:ty, $bits:expr) => {
170        impl NegativeInput<$COMP> for $pin {
171            fn setup(&self, comp: &$COMP) {
172                comp.csr().modify(|_, w| unsafe { w.inmsel().bits($bits) })
173            }
174        }
175    };
176}
177
178negative_input_pin!(COMP1, PB1<Analog>, 0b0110);
179negative_input_pin!(COMP1, PC4<Analog>, 0b0111);
180negative_input_pin!(COMP1, PA0<Analog>, 0b1000);
181
182negative_input_pin!(COMP2, PB3<Analog>, 0b0110);
183negative_input_pin!(COMP2, PB7<Analog>, 0b0111);
184negative_input_pin!(COMP2, PA2<Analog>, 0b1000);
185
186#[derive(Copy, Clone, Eq, PartialEq)]
187pub enum RefintInput {
188    /// VRefint * 1/4
189    VRefintM14 = 0b0000,
190    /// VRefint * 1/2
191    VRefintM12 = 0b0001,
192    /// VRefint * 3/4
193    VRefintM34 = 0b0010,
194    /// VRefint
195    VRefint = 0b0011,
196}
197
198macro_rules! refint_input {
199    ($COMP:ident) => {
200        impl NegativeInput<$COMP> for RefintInput {
201            fn setup(&self, comp: &$COMP) {
202                comp.csr()
203                    .modify(|_, w| unsafe { w.inmsel().bits(*self as u8) })
204            }
205        }
206    };
207}
208
209refint_input!(COMP1);
210refint_input!(COMP2);
211
212macro_rules! dac_input {
213    ($COMP:ident, $channel:ty, $bits:expr) => {
214        impl<ED> NegativeInput<$COMP> for &$channel {
215            fn setup(&self, comp: &$COMP) {
216                comp.csr().modify(|_, w| unsafe { w.inmsel().bits($bits) })
217            }
218        }
219    };
220}
221
222#[cfg(any(feature = "stm32g071", feature = "stm32g081"))]
223dac_input!(COMP1, dac::Channel1<ED>, 0b0100);
224#[cfg(any(feature = "stm32g071", feature = "stm32g081"))]
225dac_input!(COMP1, dac::Channel2<ED>, 0b0101);
226
227#[cfg(any(feature = "stm32g071", feature = "stm32g081"))]
228dac_input!(COMP2, dac::Channel1<ED>, 0b0100);
229#[cfg(any(feature = "stm32g071", feature = "stm32g081"))]
230dac_input!(COMP2, dac::Channel2<ED>, 0b0101);
231
232pub struct Comparator<C, ED> {
233    regs: C,
234    _enabled: PhantomData<ED>,
235}
236
237pub trait ComparatorExt<COMP> {
238    /// Initializes a comparator
239    fn comparator<P: PositiveInput<COMP>, N: NegativeInput<COMP>>(
240        self,
241        positive_input: P,
242        negative_input: N,
243        config: Config,
244        clocks: &Clocks,
245    ) -> Comparator<COMP, Disabled>;
246}
247
248macro_rules! impl_comparator {
249    ($COMP:ty, $comp:ident, $Event:expr) => {
250        impl ComparatorExt<$COMP> for $COMP {
251            fn comparator<P: PositiveInput<$COMP>, N: NegativeInput<$COMP>>(
252                self,
253                positive_input: P,
254                negative_input: N,
255                config: Config,
256                clocks: &Clocks,
257            ) -> Comparator<$COMP, Disabled> {
258                positive_input.setup(&self);
259                negative_input.setup(&self);
260                // Delay for scaler voltage bridge initialization for certain negative inputs
261                let voltage_scaler_delay = clocks.sys_clk.raw() / (1_000_000 / 200); // 200us
262                cortex_m::asm::delay(voltage_scaler_delay);
263                self.csr().modify(|_, w| unsafe {
264                    w.hyst()
265                        .bits(config.hysteresis as u8)
266                        .polarity()
267                        .bit(config.inverted)
268                        .pwrmode()
269                        .bits(config.power_mode as u8)
270                        .winout()
271                        .bit(config.output_xor)
272                });
273
274                Comparator {
275                    regs: self,
276                    _enabled: PhantomData,
277                }
278            }
279        }
280
281        impl Comparator<$COMP, Disabled> {
282            /// Initializes a comparator
283            pub fn $comp<P: PositiveInput<$COMP>, N: NegativeInput<$COMP>>(
284                comp: $COMP,
285                positive_input: P,
286                negative_input: N,
287                config: Config,
288                clocks: &Clocks,
289            ) -> Self {
290                comp.comparator(positive_input, negative_input, config, clocks)
291            }
292
293            /// Enables the comparator
294            pub fn enable(self) -> Comparator<$COMP, Enabled> {
295                self.regs.csr().modify(|_, w| w.en().set_bit());
296                Comparator {
297                    regs: self.regs,
298                    _enabled: PhantomData,
299                }
300            }
301
302            /// Enables raising the `ADC_COMP` interrupt at the specified output signal edge
303            pub fn listen(&self, edge: SignalEdge, exti: &EXTI) {
304                exti.listen($Event, edge);
305            }
306        }
307
308        impl Comparator<$COMP, Enabled> {
309            /// Returns the value of the output of the comparator
310            pub fn output(&self) -> bool {
311                self.regs.csr().read().value().bit_is_set()
312            }
313
314            /// Disables the comparator
315            pub fn disable(self) -> Comparator<$COMP, Disabled> {
316                self.regs.csr().modify(|_, w| w.en().clear_bit());
317                Comparator {
318                    regs: self.regs,
319                    _enabled: PhantomData,
320                }
321            }
322        }
323
324        impl<ED> Comparator<$COMP, ED> {
325            /// Disables raising interrupts for the output signal
326            pub fn unlisten(&self, exti: &EXTI) {
327                exti.unlisten($Event);
328            }
329
330            /// Returns `true` if the output signal interrupt is pending for the `edge`
331            pub fn is_pending(&self, edge: SignalEdge, exti: &EXTI) -> bool {
332                exti.is_pending($Event, edge)
333            }
334
335            /// Unpends the output signal interrupt
336            pub fn unpend(&self, exti: &EXTI) {
337                exti.unpend($Event);
338            }
339
340            /// Configures a GPIO pin to output the signal of the comparator
341            ///
342            /// Multiple GPIO pins may be configured as the output simultaneously.
343            pub fn output_pin<P: OutputPin<$COMP>>(&self, pin: P) {
344                pin.setup();
345            }
346        }
347    };
348}
349
350impl_comparator!(COMP1, comp1, ExtiEvent::COMP1);
351impl_comparator!(COMP2, comp2, ExtiEvent::COMP2);
352
353/// Uses two comparators to implement a window comparator.
354/// See Figure 69 in RM0444 Rev 5.
355pub struct WindowComparator<U, L, ED> {
356    pub upper: Comparator<U, ED>,
357    pub lower: Comparator<L, ED>,
358}
359
360pub trait WindowComparatorExt<UC, LC> {
361    /// Uses two comparators to implement a window comparator
362    ///
363    /// See Figure 69 in RM0444 Rev 5. Ignores and overrides the `output_xor` setting in `config`.
364    fn window_comparator<I: PositiveInput<UC>, L: NegativeInput<LC>, U: NegativeInput<UC>>(
365        self,
366        input: I,
367        lower_threshold: L,
368        upper_threshold: U,
369        config: Config,
370        clocks: &Clocks,
371    ) -> WindowComparator<UC, LC, Disabled>;
372}
373
374macro_rules! impl_window_comparator {
375    ($UPPER:ident, $LOWER:ident, $LOTHR:expr) => {
376        impl WindowComparatorExt<$UPPER, $LOWER> for ($UPPER, $LOWER) {
377            fn window_comparator<
378                I: PositiveInput<$UPPER>,
379                L: NegativeInput<$LOWER>,
380                U: NegativeInput<$UPPER>,
381            >(
382                self,
383                input: I,
384                lower_threshold: L,
385                upper_threshold: U,
386                config: Config,
387                clocks: &Clocks,
388            ) -> WindowComparator<$UPPER, $LOWER, Disabled> {
389                let (upper, lower) = self;
390
391                let mut configu = config.clone();
392                configu.output_xor = true;
393                let upper = upper.comparator(input, upper_threshold, configu, clocks);
394
395                let mut configl = config;
396                configl.output_xor = false;
397                let lower = lower.comparator($LOTHR, lower_threshold, configl, clocks);
398
399                WindowComparator { upper, lower }
400            }
401        }
402
403        impl WindowComparator<$UPPER, $LOWER, Disabled> {
404            /// Enables the comparator
405            pub fn enable(self) -> WindowComparator<$UPPER, $LOWER, Enabled> {
406                WindowComparator {
407                    upper: self.upper.enable(),
408                    lower: self.lower.enable(),
409                }
410            }
411
412            /// Enables raising the `ADC_COMP` interrupt at the specified signal edge
413            pub fn listen(&self, edge: SignalEdge, exti: &mut EXTI) {
414                self.upper.listen(edge, exti)
415            }
416        }
417
418        impl WindowComparator<$UPPER, $LOWER, Enabled> {
419            /// Disables the comparator
420            pub fn disable(self) -> WindowComparator<$UPPER, $LOWER, Disabled> {
421                WindowComparator {
422                    upper: self.upper.disable(),
423                    lower: self.lower.disable(),
424                }
425            }
426
427            /// Returns the value of the output of the comparator
428            pub fn output(&self) -> bool {
429                self.upper.output()
430            }
431
432            /// Returns `true` if the input signal is above the lower threshold
433            pub fn above_lower(&self) -> bool {
434                self.lower.output()
435            }
436        }
437
438        impl<ED> WindowComparator<$UPPER, $LOWER, ED> {
439            /// Configures a GPIO pin to output the signal of the comparator
440            ///
441            /// Multiple GPIO pins may be configured as the output simultaneously.
442            pub fn output_pin<P: OutputPin<$UPPER>>(&self, pin: P) {
443                self.upper.output_pin(pin)
444            }
445
446            /// Disables raising interrupts for the output signal
447            pub fn unlisten(&self, exti: &mut EXTI) {
448                self.upper.unlisten(exti)
449            }
450
451            /// Returns `true` if the output signal interrupt is pending for the `edge`
452            pub fn is_pending(&self, edge: SignalEdge, exti: &EXTI) -> bool {
453                self.upper.is_pending(edge, exti)
454            }
455
456            /// Unpends the output signal interrupt
457            pub fn unpend(&self, exti: &EXTI) {
458                self.upper.unpend(exti)
459            }
460        }
461    };
462}
463
464impl_window_comparator!(COMP1, COMP2, Comp1InP);
465impl_window_comparator!(COMP2, COMP1, Comp2InP);
466
467pub fn window_comparator12<
468    I: PositiveInput<COMP1>,
469    L: NegativeInput<COMP2>,
470    U: NegativeInput<COMP1>,
471>(
472    comp: COMP,
473    input: I,
474    lower_threshold: L,
475    upper_threshold: U,
476    config: Config,
477    rcc: &mut Rcc,
478) -> WindowComparator<COMP1, COMP2, Disabled> {
479    let (comp1, comp2) = comp.split(rcc);
480    (comp1, comp2).window_comparator(input, lower_threshold, upper_threshold, config, &rcc.clocks)
481}
482
483pub fn window_comparator21<
484    I: PositiveInput<COMP2>,
485    L: NegativeInput<COMP1>,
486    U: NegativeInput<COMP2>,
487>(
488    comp: COMP,
489    input: I,
490    lower_threshold: L,
491    upper_threshold: U,
492    config: Config,
493    rcc: &mut Rcc,
494) -> WindowComparator<COMP2, COMP1, Disabled> {
495    let (comp1, comp2) = comp.split(rcc);
496    (comp2, comp1).window_comparator(input, lower_threshold, upper_threshold, config, &rcc.clocks)
497}
498
499/// Enables the comparator peripheral, and splits the [`COMP`] into independent [`COMP1`] and [`COMP2`]
500pub fn split(_comp: COMP, rcc: &mut Rcc) -> (COMP1, COMP2) {
501    // Enable COMP, SYSCFG, VREFBUF clocks
502    rcc.rb.apbenr2.modify(|_, w| w.syscfgen().set_bit());
503
504    // Reset COMP, SYSCFG, VREFBUF
505    rcc.rb.apbrstr2.modify(|_, w| w.syscfgrst().set_bit());
506    rcc.rb.apbrstr2.modify(|_, w| w.syscfgrst().clear_bit());
507
508    (COMP1 { _rb: PhantomData }, COMP2 { _rb: PhantomData })
509}
510
511pub trait ComparatorSplit {
512    /// Enables the comparator peripheral, and splits the [`COMP`] into independent [`COMP1`] and [`COMP2`]
513    fn split(self, rcc: &mut Rcc) -> (COMP1, COMP2);
514}
515
516impl ComparatorSplit for COMP {
517    fn split(self, rcc: &mut Rcc) -> (COMP1, COMP2) {
518        split(self, rcc)
519    }
520}
521
522pub trait OutputPin<COMP> {
523    fn setup(&self);
524    fn release(self) -> Self;
525}
526
527macro_rules! output_pin_push_pull {
528    ($COMP:ident, $pin:ty) => {
529        impl OutputPin<$COMP> for $pin {
530            fn setup(&self) {
531                self.set_alt_mode(AltFunction::AF7)
532            }
533
534            fn release(self) -> Self {
535                self.into_push_pull_output()
536            }
537        }
538    };
539}
540
541macro_rules! output_pin_open_drain {
542    ($COMP:ident, $pin:ty) => {
543        impl OutputPin<$COMP> for $pin {
544            fn setup(&self) {
545                self.set_alt_mode(AltFunction::AF7)
546            }
547
548            fn release(self) -> Self {
549                self.into_open_drain_output()
550            }
551        }
552    };
553}
554
555output_pin_push_pull!(COMP1, PA0<Output<PushPull>>);
556output_pin_open_drain!(COMP1, PA0<Output<OpenDrain>>);
557output_pin_push_pull!(COMP1, PA6<Output<PushPull>>);
558output_pin_open_drain!(COMP1, PA6<Output<OpenDrain>>);
559output_pin_push_pull!(COMP1, PA11<Output<PushPull>>);
560output_pin_open_drain!(COMP1, PA11<Output<OpenDrain>>);
561output_pin_push_pull!(COMP1, PB0<Output<PushPull>>);
562output_pin_open_drain!(COMP1, PB0<Output<OpenDrain>>);
563output_pin_push_pull!(COMP1, PB10<Output<PushPull>>);
564output_pin_open_drain!(COMP1, PB10<Output<OpenDrain>>);
565
566output_pin_push_pull!(COMP2, PA2<Output<PushPull>>);
567output_pin_open_drain!(COMP2, PA2<Output<OpenDrain>>);
568output_pin_push_pull!(COMP2, PA7<Output<PushPull>>);
569output_pin_open_drain!(COMP2, PA7<Output<OpenDrain>>);
570output_pin_push_pull!(COMP2, PA12<Output<PushPull>>);
571output_pin_open_drain!(COMP2, PA12<Output<OpenDrain>>);
572output_pin_push_pull!(COMP2, PB5<Output<PushPull>>);
573output_pin_open_drain!(COMP2, PB5<Output<OpenDrain>>);
574output_pin_push_pull!(COMP2, PB11<Output<PushPull>>);
575output_pin_open_drain!(COMP2, PB11<Output<OpenDrain>>);