stm32_hrtim/
control.rs

1#[cfg(feature = "hrtim_v2")]
2use crate::adc_trigger;
3#[cfg(feature = "hrtim_v2")]
4use crate::fault::FltMonitor6;
5use crate::fault::{
6    FltMonitor1, FltMonitor2, FltMonitor3, FltMonitor4, FltMonitor5, FltMonitorSys,
7};
8
9use crate::timer::{self, HrTimer};
10use crate::{pac, pac::HRTIM_COMMON};
11
12use super::{external_event::EevInputs, fault::FaultInputs};
13
14impl HrTimOngoingCalibration {
15    /// Look in the hal for an corresponding extension trait for `HRTIM_COMMON`.
16    ///
17    /// ..unless you are the one implementing the hal
18    ///
19    /// # Safety
20    /// The user is expected to have setup and enabled rcc clock to the peripheral
21    pub unsafe fn hr_control() -> HrTimOngoingCalibration {
22        #[allow(unused_variables)]
23        let common = unsafe { &*HRTIM_COMMON::ptr() };
24
25        // Start calibration procedure
26        #[cfg(not(feature = "stm32h7"))]
27        common
28            .dllcr()
29            .write(|w| w.cal().set_bit().calen().clear_bit());
30
31        HrTimOngoingCalibration {
32            #[cfg(feature = "stm32g4")]
33            adc_trigger1_postscaler: AdcTriggerPostscaler::None,
34            #[cfg(feature = "stm32g4")]
35            adc_trigger2_postscaler: AdcTriggerPostscaler::None,
36            #[cfg(feature = "stm32g4")]
37            adc_trigger3_postscaler: AdcTriggerPostscaler::None,
38            #[cfg(feature = "stm32g4")]
39            adc_trigger4_postscaler: AdcTriggerPostscaler::None,
40
41            #[cfg(feature = "stm32g4")]
42            adc_trigger5_postscaler: AdcTriggerPostscaler::None,
43            #[cfg(feature = "stm32g4")]
44            adc_trigger6_postscaler: AdcTriggerPostscaler::None,
45            #[cfg(feature = "stm32g4")]
46            adc_trigger7_postscaler: AdcTriggerPostscaler::None,
47            #[cfg(feature = "stm32g4")]
48            adc_trigger8_postscaler: AdcTriggerPostscaler::None,
49            #[cfg(feature = "stm32g4")]
50            adc_trigger9_postscaler: AdcTriggerPostscaler::None,
51            #[cfg(feature = "stm32g4")]
52            adc_trigger10_postscaler: AdcTriggerPostscaler::None,
53
54            flt_divider: SamplingClkDiv::None,
55            eev_divider: SamplingClkDiv::None,
56        }
57    }
58}
59
60pub struct HrTimOngoingCalibration {
61    #[cfg(feature = "stm32g4")]
62    adc_trigger1_postscaler: AdcTriggerPostscaler,
63    #[cfg(feature = "stm32g4")]
64    adc_trigger2_postscaler: AdcTriggerPostscaler,
65    #[cfg(feature = "stm32g4")]
66    adc_trigger3_postscaler: AdcTriggerPostscaler,
67    #[cfg(feature = "stm32g4")]
68    adc_trigger4_postscaler: AdcTriggerPostscaler,
69
70    #[cfg(feature = "stm32g4")]
71    adc_trigger5_postscaler: AdcTriggerPostscaler,
72    #[cfg(feature = "stm32g4")]
73    adc_trigger6_postscaler: AdcTriggerPostscaler,
74    #[cfg(feature = "stm32g4")]
75    adc_trigger7_postscaler: AdcTriggerPostscaler,
76    #[cfg(feature = "stm32g4")]
77    adc_trigger8_postscaler: AdcTriggerPostscaler,
78    #[cfg(feature = "stm32g4")]
79    adc_trigger9_postscaler: AdcTriggerPostscaler,
80    #[cfg(feature = "stm32g4")]
81    adc_trigger10_postscaler: AdcTriggerPostscaler,
82
83    flt_divider: SamplingClkDiv,
84    eev_divider: SamplingClkDiv,
85}
86
87impl HrTimOngoingCalibration {
88    /// SAFETY: Calibration needs to be done before calling this
89    unsafe fn init(self) {
90        let common = unsafe { &*HRTIM_COMMON::ptr() };
91
92        let Self {
93            #[cfg(feature = "stm32g4")]
94            adc_trigger1_postscaler,
95            #[cfg(feature = "stm32g4")]
96            adc_trigger2_postscaler,
97            #[cfg(feature = "stm32g4")]
98            adc_trigger3_postscaler,
99            #[cfg(feature = "stm32g4")]
100            adc_trigger4_postscaler,
101
102            #[cfg(feature = "stm32g4")]
103            adc_trigger5_postscaler,
104            #[cfg(feature = "stm32g4")]
105            adc_trigger6_postscaler,
106            #[cfg(feature = "stm32g4")]
107            adc_trigger7_postscaler,
108            #[cfg(feature = "stm32g4")]
109            adc_trigger8_postscaler,
110            #[cfg(feature = "stm32g4")]
111            adc_trigger9_postscaler,
112            #[cfg(feature = "stm32g4")]
113            adc_trigger10_postscaler,
114
115            flt_divider,
116            eev_divider,
117        } = self;
118
119        unsafe {
120            // Enable periodic calibration
121            // with f_hrtim at 170MHz, these settings leads to
122            // a period of about 6.2ms
123            #[cfg(not(feature = "stm32h7"))]
124            common
125                .dllcr()
126                .modify(|_r, w| w.calrte().bits(0b00).cal().set_bit().calen().clear_bit());
127            common
128                .fltinr2()
129                .write(|w| w.fltsd().bits(flt_divider as u8));
130
131            common.eecr3().write(|w| w.eevsd().bits(eev_divider as u8));
132
133            #[cfg(feature = "stm32g4")]
134            common.adcps1().write(|w| {
135                w.adc1psc()
136                    .bits(adc_trigger1_postscaler as u8)
137                    .adc2psc()
138                    .bits(adc_trigger2_postscaler as u8)
139                    .adc3psc()
140                    .bits(adc_trigger3_postscaler as u8)
141                    .adc4psc()
142                    .bits(adc_trigger4_postscaler as u8)
143                    .adc5psc()
144                    .bits(adc_trigger5_postscaler as u8)
145            });
146
147            #[cfg(feature = "stm32g4")]
148            common.adcps2().write(|w| {
149                w.adc6psc()
150                    .bits(adc_trigger6_postscaler as u8)
151                    .adc7psc()
152                    .bits(adc_trigger7_postscaler as u8)
153                    .adc8psc()
154                    .bits(adc_trigger8_postscaler as u8)
155                    .adc9psc()
156                    .bits(adc_trigger9_postscaler as u8)
157                    .adc10psc()
158                    .bits(adc_trigger10_postscaler as u8)
159            });
160
161            // TODO: Adc trigger 5-10
162        }
163    }
164
165    pub fn wait_for_calibration(self) -> (HrTimCalibrated, FaultInputs, EevInputs) {
166        #[cfg(not(feature = "stm32h7"))]
167        {
168            let common = unsafe { &*HRTIM_COMMON::ptr() };
169            while common.isr().read().dllrdy().bit_is_clear() {
170                // Wait until ready
171            }
172        }
173
174        // Calibration is now done, it is safe to continue
175        unsafe { self.init() };
176
177        (HrTimCalibrated, unsafe { FaultInputs::new() }, unsafe {
178            EevInputs::new()
179        })
180    }
181
182    #[cfg(feature = "stm32g4")]
183    pub fn set_adc1_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self {
184        self.adc_trigger1_postscaler = post_scaler;
185        self
186    }
187
188    #[cfg(feature = "stm32g4")]
189    pub fn set_adc2_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self {
190        self.adc_trigger2_postscaler = post_scaler;
191        self
192    }
193
194    #[cfg(feature = "stm32g4")]
195    pub fn set_adc3_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self {
196        self.adc_trigger3_postscaler = post_scaler;
197        self
198    }
199
200    #[cfg(feature = "stm32g4")]
201    pub fn set_adc4_trigger_psc(mut self, post_scaler: AdcTriggerPostscaler) -> Self {
202        self.adc_trigger4_postscaler = post_scaler;
203        self
204    }
205
206    pub fn set_fault_sampling_division(mut self, divider: SamplingClkDiv) -> Self {
207        self.flt_divider = divider;
208        self
209    }
210
211    pub fn set_eev_sampling_division(mut self, divider: SamplingClkDiv) -> Self {
212        self.eev_divider = divider;
213        self
214    }
215}
216
217/// This object may be used for things that needs to be done before any timers have been started but after the calibration has been completed. Its existence is proof that no timers have started.
218///
219/// Once done with setup, use the `constrain` to get a `HrPwmControl` which can be used to start the timers.
220#[non_exhaustive]
221pub struct HrTimCalibrated;
222
223impl HrTimCalibrated {
224    pub fn constrain(self) -> HrPwmControl {
225        HrPwmControl {
226            control: HrPwmCtrl,
227            fault_sys: FltMonitorSys,
228            fault_1: FltMonitor1,
229            fault_2: FltMonitor2,
230            fault_3: FltMonitor3,
231            fault_4: FltMonitor4,
232            fault_5: FltMonitor5,
233            #[cfg(feature = "hrtim_v2")]
234            fault_6: FltMonitor6,
235
236            #[cfg(feature = "stm32g4")]
237            adc_trigger1: adc_trigger::AdcTrigger1,
238            #[cfg(feature = "stm32g4")]
239            adc_trigger2: adc_trigger::AdcTrigger2,
240            #[cfg(feature = "stm32g4")]
241            adc_trigger3: adc_trigger::AdcTrigger3,
242            #[cfg(feature = "stm32g4")]
243            adc_trigger4: adc_trigger::AdcTrigger4,
244            #[cfg(feature = "stm32g4")]
245            adc_trigger5: adc_trigger::AdcTrigger5,
246            #[cfg(feature = "stm32g4")]
247            adc_trigger6: adc_trigger::AdcTrigger6,
248            #[cfg(feature = "stm32g4")]
249            adc_trigger7: adc_trigger::AdcTrigger7,
250            #[cfg(feature = "stm32g4")]
251            adc_trigger8: adc_trigger::AdcTrigger8,
252            #[cfg(feature = "stm32g4")]
253            adc_trigger9: adc_trigger::AdcTrigger9,
254            #[cfg(feature = "stm32g4")]
255            adc_trigger10: adc_trigger::AdcTrigger10,
256        }
257    }
258}
259
260impl<'a> From<&'a mut HrPwmControl> for &'a mut HrPwmCtrl {
261    fn from(val: &'a mut HrPwmControl) -> Self {
262        &mut val.control
263    }
264}
265
266/// Used as a token to guarantee unique access to resources common to multiple timers
267///
268/// An instance of this object can be obtained from [`HrPwmControl`].control
269#[non_exhaustive]
270pub struct HrPwmCtrl;
271
272pub struct Foo<'a>(&'a mut pac::hrtim_master::cr::W);
273
274impl<'a> Foo<'a> {
275    pub fn start<T: HrTimer>(self, _t: &mut T) -> Self {
276        use crate::timer::Instance;
277
278        let w = self.0;
279        Foo(match T::Timer::TIMX {
280            timer::Timer::Master => w.mcen().set_bit(),
281            timer::Timer::Tim(v) => w.tcen(v as _).set_bit(),
282        })
283    }
284    pub fn stop<T: HrTimer>(self, _t: &mut T) -> Self {
285        use crate::timer::Instance;
286
287        let w = self.0;
288        Foo(match T::Timer::TIMX {
289            timer::Timer::Master => w.mcen().clear_bit(),
290            timer::Timer::Tim(v) => w.tcen(v as _).clear_bit(),
291        })
292    }
293}
294
295impl HrPwmCtrl {
296    /// Start/stop multiple timers at the exact same time
297    ///
298    /// ```
299    /// let mut timer_a = ...;
300    /// let mut timer_b = ...;
301    /// let mut timer_c = ...;
302    /// hr_control.start_stop_timers(|w| w
303    ///     .start(&mut timer_a)
304    ///     .start(&mut timer_b)
305    ///     .stop(&mut timer_c)
306    /// );
307    /// ```
308    pub fn start_stop_timers(&mut self, p: impl FnOnce(Foo) -> Foo) {
309        let master = unsafe { pac::HRTIM_MASTER::steal() };
310        master.cr().modify(|_, w| p(Foo(w)).0);
311    }
312}
313
314/// Used as a token to guarantee unique access to resources common to multiple timers
315#[non_exhaustive]
316pub struct HrPwmControl {
317    pub control: HrPwmCtrl,
318
319    pub fault_sys: FltMonitorSys,
320    pub fault_1: FltMonitor1,
321    pub fault_2: FltMonitor2,
322    pub fault_3: FltMonitor3,
323    pub fault_4: FltMonitor4,
324    pub fault_5: FltMonitor5,
325    #[cfg(feature = "stm32g4")]
326    pub fault_6: FltMonitor6,
327
328    #[cfg(feature = "stm32g4")]
329    pub adc_trigger1: adc_trigger::AdcTrigger1,
330    #[cfg(feature = "stm32g4")]
331    pub adc_trigger2: adc_trigger::AdcTrigger2,
332    #[cfg(feature = "stm32g4")]
333    pub adc_trigger3: adc_trigger::AdcTrigger3,
334    #[cfg(feature = "stm32g4")]
335    pub adc_trigger4: adc_trigger::AdcTrigger4,
336
337    #[cfg(feature = "stm32g4")]
338    pub adc_trigger5: adc_trigger::AdcTrigger5,
339    #[cfg(feature = "stm32g4")]
340    pub adc_trigger6: adc_trigger::AdcTrigger6,
341    #[cfg(feature = "stm32g4")]
342    pub adc_trigger7: adc_trigger::AdcTrigger7,
343    #[cfg(feature = "stm32g4")]
344    pub adc_trigger8: adc_trigger::AdcTrigger8,
345    #[cfg(feature = "stm32g4")]
346    pub adc_trigger9: adc_trigger::AdcTrigger9,
347    #[cfg(feature = "stm32g4")]
348    pub adc_trigger10: adc_trigger::AdcTrigger10,
349}
350
351#[cfg(feature = "stm32g4")]
352pub enum AdcTriggerPostscaler {
353    None = 0,
354    Div2 = 1,
355    Div3 = 2,
356    Div4 = 3,
357    Div5 = 4,
358    Div6 = 5,
359    Div7 = 6,
360    Div8 = 7,
361    Div9 = 8,
362    Div10 = 9,
363    Div11 = 10,
364    Div12 = 11,
365    Div13 = 12,
366    Div14 = 13,
367    Div15 = 14,
368    Div16 = 15,
369    Div17 = 16,
370    Div18 = 17,
371    Div19 = 18,
372    Div20 = 19,
373    Div21 = 20,
374    Div22 = 21,
375    Div23 = 22,
376    Div24 = 23,
377    Div25 = 24,
378    Div26 = 25,
379    Div27 = 26,
380    Div28 = 27,
381    Div29 = 28,
382    Div30 = 29,
383    Div31 = 30,
384    Div32 = 31,
385}
386
387/// The divsion ratio between f_hrtim and the fault signal sampling clock for digital filters
388pub enum SamplingClkDiv {
389    /// No division
390    ///
391    /// fault signal sampling clock f_flts = f_hrtim
392    None = 0b00,
393
394    /// 1/2
395    ///
396    /// fault signal sampling clock f_flts = f_hrtim / 2
397    Two = 0b01,
398
399    /// 1/4
400    ///
401    /// fault signal sampling clock f_flts = f_hrtim / 4
402    Four = 0b10,
403
404    /// 1/8
405    ///
406    /// fault signal sampling clock f_flts = f_hrtim / 8
407    Eight = 0b11,
408}