stm32_hal2/comp.rs
1//! Comparator
2//!
3//! TODO:
4//! - Window Mode Configuration (COMP1 and COMP2 have different configs)
5//! - Blanking Source Configuration (COMP1 and COMP2 have different configs)
6//! - More Inputs For Inverting Input (STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx)
7//! - Moving Peripheral into Struct (pac needs to change)
8//! - Add Configuration Defaults
9//! - Interrupts?
10
11use core::marker::PhantomData;
12
13use cfg_if::cfg_if;
14use paste::paste;
15
16use crate::pac;
17#[cfg(any(feature = "g473"))]
18use crate::pac::comp::{C1CSR, C2CSR, C3CSR, C4CSR, C5CSR, C6CSR, C7CSR};
19#[cfg(any(feature = "l4x6"))]
20use crate::pac::comp::{COMP1_CSR, COMP2_CSR};
21#[cfg(any(feature = "h747cm4", feature = "h747cm7"))]
22use crate::pac::comp1::{CFGR1, CFGR2};
23
24// Config enums
25/// Comparator power mode
26#[cfg(any(feature = "l4"))]
27pub enum PowerMode {
28 /// High speed/full power (Lowest propagation delay).
29 HighSpeed = 0x00000000,
30 /// Medium speed/medium power (Medium propagation delay).
31 MediumSpeed = 0x00000004,
32 /// Low speed/ultra-low power (Highest propagation delay).
33 LowSpeed = 0x0000000c,
34}
35
36/// Comparator input plus (Non-inverting Input)
37#[cfg(any(feature = "g473", feature = "h7"))]
38#[derive(Clone, Copy)]
39#[repr(u8)]
40// STM32G4 reference manual section 24.3.2 table 196
41pub enum NonInvertingInput {
42 Io1 = 0b0,
43 Io2 = 0b1,
44}
45
46#[cfg(any(feature = "l4"))]
47pub enum NonInvertingInput {
48 /// From the first GPIO pin connected to the comparator.
49 ///
50 /// The GPIO pin used depends on the MCU and comparator used.
51 Io1 = 0x00000000,
52 /// From the second GPIO pin connected to the comparator.
53 ///
54 /// The GPIO pin used depends on the MCU and comparator used.
55 Io2 = 0x00000080,
56 // PA1/PA3 for STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx
57 // TODO: Include stm32l471
58 #[cfg(any(feature = "stm32l4x1", feature = "stm32l4x2", feature = "stm32l4x3",))]
59 /// From the third GPIO pin connected to the comparator.
60 ///
61 /// The GPIO pin used depends on the MCU and comparator used.
62 Io3 = 0x00000100,
63}
64
65/// Comparator input minus (Inverted Input)
66#[cfg(any(feature = "g473", feature = "h7", feature = "l4"))]
67#[derive(Clone, Copy)]
68#[repr(u8)]
69// STM32G4 reference manual section 24.3.2 table 197
70pub enum InvertingInput {
71 OneQuarterVref = 0b000,
72 OneHalfVref = 0b001,
73 ThreeQuarterVref = 0b010,
74 Vref = 0b011,
75 Dac1 = 0b100,
76 Dac2 = 0b101,
77 Io1 = 0b110,
78 Io2 = 0b111,
79}
80
81/// Comparator hysterisis
82#[cfg(any(feature = "g473"))]
83#[derive(Clone, Copy)]
84pub enum Hysterisis {
85 None = 0b000,
86 TenMilliVolt = 0b001,
87 TwentyMilliVolt = 0b010,
88 ThirtyMilliVolt = 0b011,
89 FourtyMilliVolt = 0b100,
90 FiftyMilliVolt = 0b101,
91 SixtyMilliVolt = 0b110,
92 SeventyMilliVolt = 0b111,
93}
94
95#[cfg(any(feature = "l4", feature = "h7"))]
96pub enum Hysterisis {
97 /// No Hysterisis.
98 NoHysterisis = 0b00,
99 /// Low Hysterisis.
100 LowHysteresis = 0b01,
101 /// Medium Hysterisis.
102 MediumHysteresis = 0b10,
103 /// High Hysterisis.
104 HighHysteresis = 0b11,
105}
106
107/// Comparator output polarity
108///
109/// When [OutputPolarity::NotInverted] is used.
110/// The comparator output will be high (1) when [NonInvertingInput] has higher
111/// voltage than [InvertingInput]. The comparator output will be low (0) when
112/// [NonInvertingInput] has lower voltage than [InvertingInput].
113///
114/// When [OutputPolarity::Inverted] is used.
115/// The comparator output will be high (1) when [NonInvertingInput] has lower
116/// voltage than [InvertingInput]. The comparator output will be low (0) when
117/// [NonInvertingInput] has higher voltage than [InvertingInput].
118
119#[cfg(any(feature = "g473", feature = "h7"))]
120#[derive(Clone, Copy)]
121pub enum OutputPolarity {
122 NotInverted = 0b0,
123 Inverted = 0b1,
124}
125
126#[cfg(any(feature = "l4"))]
127pub enum OutputPolarity {
128 /// Comparator output will not be inverted.
129 NotInverted = 0x00000000,
130 /// Comparator output will be inverted.
131 Inverted = 0x00008000,
132}
133
134/// Comparator blanking source
135pub enum BlankingSource {
136 /// No Blanking.
137 None = 0x00000000,
138 /// TIM1 OC5 as the blanking source.
139 Timloc5 = 0x400000,
140}
141
142/// Comparator devices avaiable.
143#[cfg(any(feature = "g473"))]
144pub enum CompDevice {
145 One,
146 Two,
147 Three,
148 Four,
149 Five,
150 Six,
151 Seven,
152}
153
154// Structs
155/// Initial configuration data for the comparator peripheral.
156
157#[cfg(any(feature = "g473"))]
158#[derive(Clone, Copy)]
159pub struct CompConfig {
160 pub inpsel: NonInvertingInput,
161 pub inmsel: InvertingInput,
162 pub hyst: Hysterisis,
163 pub polarity: OutputPolarity,
164}
165
166#[cfg(any(feature = "l4"))]
167pub struct CompConfig {
168 /// Comparator power mode.
169 pub pwrmode: PowerMode,
170 /// Comparator non-inverting input.
171 pub inpsel: NonInvertingInput,
172 /// Comparator inverting input.
173 pub inmsel: InvertingInput,
174 /// Comparator hysterisis.
175 pub hyst: Hysterisis,
176 /// Comparator output polarity.
177 pub polarity: OutputPolarity,
178 // Comparator blanking source.
179 // pub blanking: BlankingSource,
180}
181
182/// Represents an Analog Comparator peripheral.
183pub struct Comp<T> {
184 phantom: PhantomData<T>,
185 /// The lock status of the comparator.
186 is_locked: bool,
187}
188
189// Macro to implement a comparator using generics
190// This will create `new_compX` methods to instantiate a new comparator
191// and provide a csr() method to access the register scoped to this comparator
192macro_rules! make_comp {
193 ($csr_type:ident, $csr_reg:ident, $comp:ident) => {
194 impl Comp<$csr_type> {
195 paste! {
196 pub fn [<new_ $comp>]() -> Self {
197 unsafe {
198 Self {
199 phantom: PhantomData,
200 is_locked: false
201 }
202 }
203 }
204 }
205
206 // Get a reference to the CSR from the COMP RegisterBlock for this comparator
207 pub fn csr(&self) -> &$csr_type {
208 #[cfg(any(feature = "g473", feature = "l4"))]
209 unsafe {
210 &(*pac::COMP::ptr()).$csr_reg
211 }
212 #[cfg(any(feature = "h747cm4", feature = "h747cm7"))]
213 unsafe {
214 &(*pac::COMP1::ptr()).$csr_reg
215 }
216 }
217
218 pub fn enable(&self) {
219 self.csr().modify(|_, w| w.en().set_bit());
220 }
221
222 pub fn disable(&self) {
223 self.csr().modify(|_, w| w.en().clear_bit());
224 }
225
226 // Sets the inverting input in the CSR
227 pub fn set_inverting_input(&self, input: InvertingInput) {
228 cfg_if! {
229 if #[cfg(feature = "l4")] {
230 unsafe { self.csr().modify(|_,w| w.inmsel().bits(input as u8)) };
231 } else {
232 self.csr().modify(|_,w| w.inmsel().variant(input as u8));
233 }
234 }
235 }
236
237 pub fn set_non_inverting_input(&self, input: NonInvertingInput) {
238 cfg_if! {
239 if #[cfg(feature = "l4")] {
240 self.csr().modify(|_,w| w.inpsel().bit(input as u8 != 0));
241 } else {
242 self.csr().modify(|_,w| w.inpsel().variant(input as u8 != 0));
243 }
244 }
245 }
246
247 pub fn set_polarity(&self, polarity: OutputPolarity) {
248 cfg_if! {
249 if #[cfg(feature = "l4")] {
250 self.csr().modify(|_,w| w.polarity().bit((polarity as u8) != 0));
251 } else {
252 #[cfg(feature = "g4")]
253 self.csr().modify(|_,w| w.pol().variant((polarity as u8) != 0));
254
255 #[cfg(feature = "h7")]
256 self.csr().modify(|_,w| w.polarity().variant((polarity as u8) != 0));
257 }
258 }
259 }
260
261 pub fn set_hysterisis(&self, hyst: Hysterisis) {
262 cfg_if! {
263 if #[cfg(feature = "l4")] {
264 self.csr().modify(|_,w| w.hyst().bits((hyst as u8)));
265 } else {
266 self.csr().modify(|_,w| w.hyst().variant((hyst as u8)));
267 }
268 }
269 }
270
271 pub fn set_blanking_source(&self, source: u8) {
272 #[cfg(feature = "g473")]
273 self.csr().modify(|_, w| w.blanksel().variant(source));
274
275 #[cfg(feature = "h7")]
276 self.csr().modify(|_, w| w.blanking().variant(source));
277 }
278
279 /// Locks the comparator.
280 ///
281 /// This locks the comparator registers making it only read-only.
282 ///
283 /// **Note:** The lock also applies to the lock bit itself. Therefore,
284 /// the comparator register/configuration **cannot** be changed until
285 /// a hardware reset.
286 pub fn lock(&mut self) {
287 self.is_locked = true;
288 self.csr().modify(|_, w| w.lock().set_bit());
289 }
290
291 /// Gets the output level of the comparator
292 ///
293 /// The output level depends on the configuration of the comparator.
294 /// If the [polarity](CompConfig::polarity) is [NotInverted](OutputPolarity::NotInverted)
295 /// - It will output high (1) if the non-inverting input is higher than
296 /// the output of inverting input.
297 /// - It will output low (0) if the non-inverting input is lower than
298 /// the output of the inverting input.
299 ///
300 /// The oposite will be out inverted if [polarity](CompConfig::polarity) is
301 /// [Inverted](OutputPolarity::NotInverted).
302 pub fn get_output_level(&self) -> bool {
303 cfg_if! {
304 if #[cfg(any(feature = "g4", feature = "l4"))] {
305 self.csr().read().value().bit()
306 }
307 else if #[cfg(feature = "h7")] {
308 // On the H7, the comparator output is in a separate status register
309 let status = unsafe { &(*pac::COMP1::ptr()).sr.read() };
310
311 // Check which channel this impl is for
312 match "$csr_reg" {
313 "c1csr" => {
314 status.c1val().bit_is_set()
315 }
316
317 "c2csr" => {
318 status.c2val().bit_is_set()
319 }
320
321 _ => todo!(),
322 }
323 }
324 }
325 }
326 }
327 };
328}
329
330cfg_if! {
331 if #[cfg(any(feature = "g4"))] {
332 make_comp!(C1CSR, c1csr, comp1);
333 make_comp!(C2CSR, c2csr, comp2);
334 make_comp!(C3CSR, c3csr, comp3);
335 make_comp!(C4CSR, c4csr, comp4);
336 make_comp!(C5CSR, c5csr, comp5);
337 make_comp!(C6CSR, c6csr, comp6);
338 make_comp!(C7CSR, c7csr, comp7);
339 } else if #[cfg(any(feature = "l4x6"))] {
340 make_comp!(COMP1_CSR, comp1_csr, comp1);
341 make_comp!(COMP2_CSR, comp2_csr, comp2);
342 } else if #[cfg(any(feature = "h747cm4", feature = "h747cm7"))] {
343 make_comp!(CFGR1, cfgr1, comp1);
344 make_comp!(CFGR2, cfgr2, comp2);
345 }
346}