stm32l4xx_hal/
lptimer.rs

1//! Low power timers
2use crate::rcc::{Clocks, Enable, RccBus, Reset, CCIPR};
3
4use crate::stm32::{LPTIM1, LPTIM2, RCC};
5
6/// Clock sources available for timers
7pub enum ClockSource {
8    /// Use PCLK as clock source
9    PCLK = 0b00,
10    /// Use LSI as clock source
11    LSI = 0b01,
12    /// Use HSI16 as clock source
13    HSI16 = 0b10,
14    /// Use LSE as clock source
15    LSE = 0b11,
16}
17
18/// The prescaler value to use for a timer
19///
20/// Allow missing docs because the type is self explanatory
21#[allow(missing_docs)]
22pub enum PreScaler {
23    U1 = 0b000,
24    U2 = 0b001,
25    U4 = 0b010,
26    U8 = 0b011,
27    U16 = 0b100,
28    U32 = 0b101,
29    U64 = 0b110,
30    U128 = 0b111,
31}
32
33/// Count modes that are available.
34///
35/// All ClockSources currently supported require the Internal count mode
36#[derive(PartialEq)]
37pub enum CountMode {
38    /// Use an internal clock source (which also includes LSE)
39    Internal,
40    // External,
41}
42
43/// All currently supported interrupt events
44pub enum Event {
45    /// Occurs when the compare value is the same as the counter value
46    CompareMatch,
47    /// Occurs when the arr value is the same as the counter value.
48    /// When this event occurs, the counter value is set to 0 (by hardware)
49    AutoReloadMatch,
50}
51
52/// Configuration of a low power timer
53pub struct LowPowerTimerConfig {
54    clock_source: ClockSource,
55    prescaler: PreScaler,
56    count_mode: CountMode,
57    compare_value: u16,
58    arr_value: u16,
59}
60
61impl Default for LowPowerTimerConfig {
62    fn default() -> Self {
63        Self {
64            clock_source: ClockSource::LSI,
65            prescaler: PreScaler::U1,
66            count_mode: CountMode::Internal,
67            compare_value: 0x0,
68            arr_value: 0xFFFF,
69        }
70    }
71}
72
73impl LowPowerTimerConfig {
74    /// Select which clock source should be used
75    pub fn clock_source(mut self, clock_source: ClockSource) -> Self {
76        self.clock_source = clock_source;
77        self
78    }
79
80    /// Select which prescaler value should be used
81    pub fn prescaler(mut self, prescaler: PreScaler) -> Self {
82        self.prescaler = prescaler;
83        self
84    }
85
86    /// Select the count mode that should be used
87    pub fn count_mode(mut self, count_mode: CountMode) -> Self {
88        self.count_mode = count_mode;
89        self
90    }
91
92    /// Set the value of the compare register
93    pub fn compare_value(mut self, compare_value: u16) -> Self {
94        self.compare_value = compare_value;
95        self
96    }
97
98    /// Set the value of the auto reload register
99    pub fn arr_value(mut self, arr_value: u16) -> Self {
100        self.arr_value = arr_value;
101        self
102    }
103}
104
105/// A low power hardware timer
106///
107/// Supported things:
108/// * Compare match
109/// * Auto reload matches
110pub struct LowPowerTimer<LPTIM> {
111    lptim: LPTIM,
112}
113
114macro_rules! hal {
115    ($timer_type: ident, $lptimX: ident, $timXsel: ident) => {
116        impl LowPowerTimer<$timer_type> {
117            #[inline(always)]
118            fn enable(&mut self) {
119                self.set_enable(true);
120            }
121
122            #[inline(always)]
123            fn disable(&mut self) {
124                self.set_enable(false);
125            }
126
127            #[inline(always)]
128            fn set_enable(&mut self, enabled: bool) {
129                self.lptim.cr.modify(|_, w| w.enable().bit(enabled));
130            }
131
132            #[inline(always)]
133            fn start_continuous_mode(&mut self) {
134                self.lptim.cr.modify(|_, w| w.cntstrt().set_bit());
135            }
136
137            /// Consume the LPTIM and produce a LowPowerTimer that encapsulates
138            /// said LPTIM.
139            ///
140            /// `config` contains details about the desired configuration for the
141            /// LowPowerTimer
142            ///
143            /// # Panics
144            /// This function panics if the value of ARR is less than or equal to CMP,
145            /// and if the clock source is HSI16, LSI, or LSE and that clock is not enabled.
146            pub fn $lptimX(
147                lptim: $timer_type,
148                config: LowPowerTimerConfig,
149                apb1rn: &mut <$timer_type as RccBus>::Bus,
150                ccipr: &mut CCIPR,
151                clocks: Clocks,
152            ) -> Self {
153                let LowPowerTimerConfig {
154                    clock_source,
155                    count_mode,
156                    prescaler,
157                    compare_value,
158                    arr_value,
159                } = config;
160
161                // ARR value must be strictly greater than CMP value
162                assert!(arr_value > compare_value);
163
164                // The used clock source must actually be enabled
165                // PCLK is always on if a `Clocks` eixsts.
166                match clock_source {
167                    ClockSource::LSE => assert!(clocks.lse()),
168                    ClockSource::LSI => assert!(clocks.lsi()),
169                    // Check if HSI16 is enabled
170                    // This operation is sound, as it is an atomic memory access
171                    // that does not modify the memory/read value
172                    ClockSource::HSI16 => {
173                        assert!(unsafe { (&*RCC::ptr()).cr.read().hsion().bit_is_set() })
174                    }
175                    _ => {}
176                }
177
178                <$timer_type>::enable(apb1rn);
179                <$timer_type>::reset(apb1rn);
180
181                // This operation is sound as `ClockSource as u8` only produces valid values
182                ccipr
183                    .ccipr()
184                    .modify(|_, w| unsafe { w.$timXsel().bits(clock_source as u8) });
185
186                // This operation is sound as `PreScaler as u8` (which is the "unsafe" part) only
187                // produces valid values
188                lptim.cfgr.modify(|_, w| unsafe {
189                    w.enc()
190                        .clear_bit()
191                        .countmode()
192                        .bit(count_mode != CountMode::Internal)
193                        .presc()
194                        .bits(prescaler as u8)
195                        .cksel()
196                        .clear_bit()
197                });
198
199                let mut instance = LowPowerTimer { lptim };
200
201                instance.enable();
202
203                instance.start_continuous_mode();
204                instance.set_autoreload(arr_value);
205                instance.set_compare_match(compare_value);
206                instance
207            }
208
209            /// Enable interrupts for the specified event
210            pub fn listen(&mut self, event: Event) {
211                // LPTIM_IER may only be modified when LPTIM is disabled
212                self.disable();
213                self.lptim.ier.modify(|_, w| match event {
214                    Event::CompareMatch => w.cmpmie().set_bit(),
215                    Event::AutoReloadMatch => w.arrmie().set_bit(),
216                });
217                self.enable();
218                self.start_continuous_mode();
219            }
220
221            /// Disable interrupts for the specified event
222            pub fn unlisten(&mut self, event: Event) {
223                // LPTIM_IER may only be modified when LPTIM is disabled
224                self.disable();
225                self.lptim.ier.modify(|_, w| match event {
226                    Event::CompareMatch => w.cmpmie().clear_bit(),
227                    Event::AutoReloadMatch => w.arrmie().clear_bit(),
228                });
229                self.enable();
230                self.start_continuous_mode();
231            }
232
233            /// Check if the specified event has been triggered for this LowPowerTimer.
234            ///
235            /// If this function returns `true` for an `Event` that this LowPowerTimer is listening for,
236            /// [`LowPowerTimer::clear_event_flag`] must be called for that event to prevent the
237            /// interrupt from looping eternally. This is not done in a single function to
238            /// avoid using a mutable reference for an operation that does not require it.
239            pub fn is_event_triggered(&self, event: Event) -> bool {
240                let reg_val = self.lptim.isr.read();
241                match event {
242                    Event::CompareMatch => reg_val.cmpm().bit_is_set(),
243                    Event::AutoReloadMatch => reg_val.arrm().bit_is_set(),
244                }
245            }
246
247            /// Clear the interrupt flag for the specified event
248            pub fn clear_event_flag(&mut self, event: Event) {
249                self.lptim.icr.write(|w| match event {
250                    Event::CompareMatch => w.cmpmcf().set_bit(),
251                    Event::AutoReloadMatch => w.arrmcf().set_bit(),
252                });
253            }
254
255            /// Set the compare match field for this LowPowerTimer
256            #[inline]
257            pub fn set_compare_match(&mut self, value: u16) {
258                // clear compare register update ok flag
259                self.lptim.icr.write(|w| w.cmpokcf().set_bit());
260
261                // This operation is sound as compare_value is a u16, and there are 16 writeable bits
262                // Additionally, the LPTIM peripheral will always be in the enabled state when this code is called
263                self.lptim.cmp.write(|w| unsafe { w.bits(value as u32) });
264
265                // wait for compare register update ok interrupt to be signalled
266                // (see RM0394 Rev 4, sec 30.4.10 for further explanation and
267                // sec. 30.7.1, Bit 4 for register field description)
268                while self.lptim.isr.read().cmpok().bit_is_clear() {}
269            }
270
271            /// Set auto reload register
272            /// has to be used _after_ enabling of lptim
273            #[inline(always)]
274            pub fn set_autoreload(&mut self, arr_value: u16) {
275                // clear autoreload register OK interrupt flag
276                self.lptim.icr.write(|w| w.arrokcf().set_bit());
277
278                // Write autoreload value
279                // This operation is sound as arr_value is a u16, and there are 16 writeable bits
280                self.lptim
281                    .arr
282                    .write(|w| unsafe { w.bits(arr_value as u32) });
283
284                // wait for autoreload write ok interrupt to be signalled
285                // (see RM0394 Rev 4, sec 30.4.10 for further explanation and
286                // sec. 30.7.1, Bit 4 for register field description)
287                while self.lptim.isr.read().arrok().bit_is_clear() {}
288            }
289
290            /// Get the current counter value for this LowPowerTimer
291            #[inline]
292            pub fn get_counter(&self) -> u16 {
293                self.lptim.cnt.read().bits() as u16
294            }
295
296            /// Get the value of the ARR register for this
297            /// LowPowerTimer
298            #[inline]
299            pub fn get_arr(&self) -> u16 {
300                self.lptim.arr.read().bits() as u16
301            }
302
303            pub fn pause(&mut self) {
304                self.disable();
305            }
306
307            pub fn resume(&mut self) {
308                self.enable();
309                self.start_continuous_mode();
310            }
311        }
312    };
313}
314
315hal!(LPTIM1, lptim1, lptim1sel);
316hal!(LPTIM2, lptim2, lptim2sel);