Skip to content

Commit 3ec6908

Browse files
authored
Merge pull request #210 from datdenkikniet/lp_timers
Low power timers
2 parents 31d6fe3 + 329303c commit 3ec6908

File tree

3 files changed

+302
-1
lines changed

3 files changed

+302
-1
lines changed

src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@ pub mod pwm;
142142
))]
143143
pub mod pwr;
144144

145+
#[cfg(any(
146+
feature = "stm32l4x1",
147+
feature = "stm32l4x2",
148+
feature = "stm32l4x3",
149+
feature = "stm32l4x5",
150+
feature = "stm32l4x6"
151+
))]
152+
pub mod lptimer;
145153
#[cfg(any(
146154
feature = "stm32l4x1",
147155
feature = "stm32l4x2",

src/lptimer.rs

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
//! Low power timers
2+
use crate::rcc::{Clocks, APB1R1, APB1R2, CCIPR};
3+
4+
use crate::stm32::{LPTIM1, LPTIM2, RCC};
5+
6+
/// Clock sources available for timers
7+
pub 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)]
22+
pub 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)]
37+
pub enum CountMode {
38+
/// Use an internal clock source (which also includes LSE)
39+
Internal,
40+
// External,
41+
}
42+
43+
/// All currently supported interrupt events
44+
pub 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
53+
pub struct LowPowerTimerConfig {
54+
clock_source: ClockSource,
55+
prescaler: PreScaler,
56+
count_mode: CountMode,
57+
compare_value: u16,
58+
arr_value: u16,
59+
}
60+
61+
impl 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+
73+
impl 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
110+
pub struct LowPowerTimer<LPTIM> {
111+
lptim: LPTIM,
112+
}
113+
114+
macro_rules! hal {
115+
($timer_type: ident, $lptimX: ident, $apb1rX: ident, $timXen: ident, $timXrst: 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+
/// Consume the LPTIM and produce a LowPowerTimer that encapsulates
133+
/// said LPTIM.
134+
///
135+
/// `config` contains details about the desired configuration for the
136+
/// LowPowerTimer
137+
///
138+
/// # Panics
139+
/// This function panics if the value of ARR is less than or equal to CMP,
140+
/// and if the clock source is HSI16, LSI, or LSE and that clock is not enabled.
141+
pub fn $lptimX(
142+
lptim: $timer_type,
143+
config: LowPowerTimerConfig,
144+
apb1rn: &mut $apb1rX,
145+
ccipr: &mut CCIPR,
146+
clocks: Clocks,
147+
) -> Self {
148+
let LowPowerTimerConfig {
149+
clock_source,
150+
count_mode,
151+
prescaler,
152+
compare_value,
153+
arr_value,
154+
} = config;
155+
156+
// ARR value must be strictly greater than CMP value
157+
assert!(arr_value > compare_value);
158+
159+
// The used clock source must actually be enabled
160+
// PCLK is always on if a `Clocks` eixsts.
161+
match clock_source {
162+
ClockSource::LSE => assert!(clocks.lse()),
163+
ClockSource::LSI => assert!(clocks.lsi()),
164+
// Check if HSI16 is enabled
165+
// This operation is sound, as it is an atomic memory access
166+
// that does not modify the memory/read value
167+
ClockSource::HSI16 => {
168+
assert!(unsafe { (&*RCC::ptr()).cr.read().hsion().bit_is_set() })
169+
}
170+
_ => {}
171+
}
172+
173+
apb1rn.enr().modify(|_, w| w.$timXen().set_bit());
174+
apb1rn.rstr().modify(|_, w| w.$timXrst().set_bit());
175+
apb1rn.rstr().modify(|_, w| w.$timXrst().clear_bit());
176+
177+
// This operation is sound as `ClockSource as u8` only produces valid values
178+
ccipr
179+
.ccipr()
180+
.modify(|_, w| unsafe { w.$timXsel().bits(clock_source as u8) });
181+
182+
// This operation is sound as `PreScaler as u8` (which is the "unsafe" part) only
183+
// produces valid values
184+
lptim.cfgr.modify(|_, w| unsafe {
185+
w.enc()
186+
.clear_bit()
187+
.countmode()
188+
.bit(count_mode != CountMode::Internal)
189+
.presc()
190+
.bits(prescaler as u8)
191+
.cksel()
192+
.clear_bit()
193+
});
194+
195+
let mut instance = LowPowerTimer { lptim };
196+
197+
instance.enable();
198+
199+
// Write compare, arr, and continous mode start register _after_ enabling lptim
200+
instance.lptim.cr.modify(|_, w| w.cntstrt().set_bit());
201+
202+
// This operation is sound as arr_value is a u16, and there are 16 writeable bits
203+
instance
204+
.lptim
205+
.arr
206+
.write(|w| unsafe { w.bits(arr_value as u32) });
207+
208+
instance.set_compare_match(compare_value);
209+
210+
instance
211+
}
212+
213+
/// Enable interrupts for the specified event
214+
pub fn listen(&mut self, event: Event) {
215+
// LPTIM_IER may only be modified when LPTIM is disabled
216+
self.disable();
217+
self.lptim.ier.modify(|_, w| match event {
218+
Event::CompareMatch => w.cmpmie().set_bit(),
219+
Event::AutoReloadMatch => w.arrmie().set_bit(),
220+
});
221+
self.enable();
222+
}
223+
224+
/// Disable interrupts for the specified event
225+
pub fn unlisten(&mut self, event: Event) {
226+
// LPTIM_IER may only be modified when LPTIM is disabled
227+
self.disable();
228+
self.lptim.ier.modify(|_, w| match event {
229+
Event::CompareMatch => w.cmpmie().clear_bit(),
230+
Event::AutoReloadMatch => w.arrmie().clear_bit(),
231+
});
232+
self.enable();
233+
}
234+
235+
/// Check if the specified event has been triggered for this LPTIM.
236+
///
237+
/// This function must be called if an event which this LPTIM listens to has
238+
/// generated an interrupt
239+
///
240+
/// If the event has occured, and `clear_interrupt` is true, the interrupt flag for this
241+
/// event will be cleared. Otherwise, the interrupt flag for this event will not
242+
/// be cleared.
243+
pub fn is_event_triggered(&self, event: Event, clear_interrupt: bool) -> bool {
244+
let reg_val = self.lptim.isr.read();
245+
let bit_is_set = match event {
246+
Event::CompareMatch => reg_val.cmpm().bit_is_set(),
247+
Event::AutoReloadMatch => reg_val.arrm().bit_is_set(),
248+
};
249+
if bit_is_set && clear_interrupt {
250+
self.lptim.icr.write(|w| match event {
251+
Event::CompareMatch => w.cmpmcf().set_bit(),
252+
Event::AutoReloadMatch => w.arrmcf().set_bit(),
253+
});
254+
}
255+
bit_is_set
256+
}
257+
258+
/// Set the compare match field for this LPTIM
259+
#[inline]
260+
pub fn set_compare_match(&mut self, value: u16) {
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+
266+
/// Get the current counter value for this LPTIM
267+
#[inline]
268+
pub fn get_counter(&mut self) -> u16 {
269+
self.lptim.cnt.read().bits() as u16
270+
}
271+
272+
/// Get the value of the LPTIM_ARR register for this
273+
/// LPTIM
274+
#[inline]
275+
pub fn get_arr(&mut self) -> u16 {
276+
self.lptim.arr.read().bits() as u16
277+
}
278+
}
279+
};
280+
}
281+
282+
hal!(LPTIM1, lptim1, APB1R1, lptim1en, lptim1rst, lptim1sel);
283+
hal!(LPTIM2, lptim2, APB1R2, lptim2en, lptim2rst, lptim2sel);

src/rcc.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,11 +913,16 @@ impl Clocks {
913913
self.msi
914914
}
915915

916-
/// Returns status of HSI48
916+
/// Returns status of the LSI
917917
pub fn lsi(&self) -> bool {
918918
self.lsi
919919
}
920920

921+
// Return the status of the LSE
922+
pub fn lse(&self) -> bool {
923+
self.lse
924+
}
925+
921926
/// Returns the frequency of the APB1
922927
pub fn pclk1(&self) -> Hertz {
923928
self.pclk1
@@ -928,6 +933,11 @@ impl Clocks {
928933
self.pclk2
929934
}
930935

936+
/// Get which source is being used for PLL
937+
pub fn pll_source(&self) -> Option<PllSource> {
938+
self.pll_source
939+
}
940+
931941
// TODO remove `allow`
932942
#[allow(dead_code)]
933943
pub(crate) fn ppre1(&self) -> u8 {

0 commit comments

Comments
 (0)