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);