Skip to content

Commit 3d08dbd

Browse files
authored
Merge pull request #54 from jonas-schievink/lptim
Low-Power Timer support
2 parents 0332e38 + 6243849 commit 3d08dbd

File tree

4 files changed

+627
-13
lines changed

4 files changed

+627
-13
lines changed

examples/lptim.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//! Low-Power Timer wakeup.
2+
3+
#![no_main]
4+
#![no_std]
5+
6+
7+
extern crate panic_semihosting;
8+
9+
use nb::block;
10+
use cortex_m::{asm, peripheral::NVIC};
11+
use cortex_m_rt::entry;
12+
use stm32l0xx_hal::{
13+
prelude::*,
14+
exti,
15+
gpio::{
16+
self,
17+
Output,
18+
PushPull,
19+
gpiob::PB,
20+
},
21+
lptim::{
22+
self,
23+
LpTimer,
24+
ClockSrc,
25+
},
26+
pac,
27+
pwr::{
28+
self,
29+
PWR,
30+
},
31+
rcc,
32+
syscfg::SYSCFG,
33+
};
34+
35+
36+
#[entry]
37+
fn main() -> ! {
38+
let cp = pac::CorePeripherals::take().unwrap();
39+
let dp = pac::Peripherals::take().unwrap();
40+
41+
let mut scb = cp.SCB;
42+
let mut rcc = dp.RCC.freeze(rcc::Config::msi(rcc::MSIRange::Range0));
43+
let mut exti = dp.EXTI;
44+
let mut pwr = PWR::new(dp.PWR, &mut rcc);
45+
let gpiob = dp.GPIOB.split(&mut rcc);
46+
47+
let mut led = gpiob.pb2.into_push_pull_output().downgrade();
48+
49+
let mut syscfg = SYSCFG::new(dp.SYSCFG, &mut rcc);
50+
let mut lptim = LpTimer::init_periodic(dp.LPTIM, &mut pwr, &mut rcc, ClockSrc::Lse);
51+
52+
let exti_line = 29; // LPTIM1 wakeup
53+
54+
lptim.enable_interrupts(lptim::Interrupts {
55+
autoreload_match: true,
56+
..lptim::Interrupts::default()
57+
});
58+
exti.listen(
59+
&mut syscfg,
60+
gpio::Port::PA, // argument ignored; next argument is not a GPIO line
61+
exti_line,
62+
exti::TriggerEdge::Rising,
63+
);
64+
65+
// Blink twice to signal the start of the program
66+
blink(&mut led);
67+
blink(&mut led);
68+
69+
// 1 seconds of regular run mode
70+
lptim.start(1.hz());
71+
block!(lptim.wait()).unwrap();
72+
73+
exti.clear_irq(exti_line);
74+
NVIC::unpend(pac::Interrupt::LPTIM1);
75+
76+
blink(&mut led);
77+
78+
// 1 seconds of low-power run mode
79+
pwr.enter_low_power_run_mode(rcc.clocks);
80+
block!(lptim.wait()).unwrap();
81+
pwr.exit_low_power_run_mode();
82+
exti.clear_irq(exti_line);
83+
cortex_m::peripheral::NVIC::unpend(pac::Interrupt::LPTIM1);
84+
85+
blink(&mut led);
86+
87+
// 1 seconds of sleep mode
88+
exti.wait_for_irq(
89+
exti_line,
90+
pwr.sleep_mode(&mut scb),
91+
);
92+
lptim.wait().unwrap(); // returns immediately; we just got the interrupt
93+
exti.clear_irq(exti_line);
94+
cortex_m::peripheral::NVIC::unpend(pac::Interrupt::LPTIM1);
95+
96+
blink(&mut led);
97+
98+
// 1 seconds of low-power sleep mode
99+
exti.wait_for_irq(
100+
exti_line,
101+
pwr.low_power_sleep_mode(&mut scb, &mut rcc),
102+
);
103+
lptim.wait().unwrap(); // returns immediately; we just got the interrupt
104+
exti.clear_irq(exti_line);
105+
cortex_m::peripheral::NVIC::unpend(pac::Interrupt::LPTIM1);
106+
107+
blink(&mut led);
108+
109+
// 1 seconds of stop mode
110+
exti.wait_for_irq(
111+
exti_line,
112+
pwr.stop_mode(
113+
&mut scb,
114+
&mut rcc,
115+
pwr::StopModeConfig {
116+
ultra_low_power: true,
117+
},
118+
),
119+
);
120+
lptim.wait().unwrap(); // returns immediately; we just got the interrupt
121+
122+
blink(&mut led);
123+
124+
// 1 second of standby mode
125+
cortex_m::peripheral::NVIC::unpend(pac::Interrupt::LPTIM1);
126+
exti.wait_for_irq(
127+
exti_line,
128+
pwr.standby_mode(&mut scb),
129+
);
130+
131+
// The microcontroller resets after leaving standby mode. We should never
132+
// reach this point.
133+
loop {
134+
blink(&mut led);
135+
}
136+
}
137+
138+
139+
fn blink(led: &mut PB<Output<PushPull>>) {
140+
led.set_high().unwrap();
141+
delay();
142+
led.set_low().unwrap();
143+
delay();
144+
}
145+
146+
fn delay() {
147+
// We can't use `Delay`, as that requires a frequency of at least one MHz.
148+
// Given our clock selection, the following loop should give us a nice delay
149+
// when compiled in release mode.
150+
for _ in 0 .. 1_000 { asm::nop() }
151+
}

src/exti.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,12 @@ pub fn line_is_triggered(reg: u32, line: u8) -> bool {
3434
}
3535

3636
impl ExtiExt for EXTI {
37-
// `port`, `line` and `edge` are almost always constants, so make sure they can be
38-
// constant-propagated by marking the function as `#[inline]`. This saves ~600 Bytes in some
39-
// simple apps (eg. the `pwr.rs` example).
37+
38+
// `port` and `line` are almost always constants, so make sure they can get constant-propagated
39+
// by inlining the method. Saves ~600 Bytes in the `lptim.rs` example.
4040
#[inline]
4141
fn listen(&self, syscfg: &mut SYSCFG, port: gpio::Port, line: u8, edge: TriggerEdge) {
42-
assert!(line <= 22);
43-
assert_ne!(line, 18);
42+
assert_line_valid(line);
4443

4544
// translate port into bit values for EXTIn registers
4645
let port_bm = match port {
@@ -120,17 +119,15 @@ impl ExtiExt for EXTI {
120119
}
121120

122121
fn unlisten(&self, line: u8) {
123-
assert!(line <= 22);
124-
assert_ne!(line, 18);
122+
assert_line_valid(line);
125123

126124
bb::clear(&self.rtsr, line);
127125
bb::clear(&self.ftsr, line);
128126
bb::clear(&self.imr, line);
129127
}
130128

131129
fn pend_interrupt(&self, line: u8) {
132-
assert!(line <= 22);
133-
assert_ne!(line, 18);
130+
assert_line_valid(line);
134131

135132
bb::set(&self.swier, line);
136133
}
@@ -140,8 +137,7 @@ impl ExtiExt for EXTI {
140137
}
141138

142139
fn clear_irq(&self, line: u8) {
143-
assert!(line <= 22);
144-
assert_ne!(line, 18);
140+
assert_line_valid(line);
145141

146142
self.pr.modify(|_, w| unsafe { w.bits(0b1 << line) });
147143
}
@@ -161,7 +157,14 @@ impl ExtiExt for EXTI {
161157
0..=1 => pac::Interrupt::EXTI0_1,
162158
2..=3 => pac::Interrupt::EXTI2_3,
163159
4..=15 => pac::Interrupt::EXTI4_15,
164-
20 => pac::Interrupt::RTC,
160+
16 => pac::Interrupt::PVD,
161+
17 | 19 | 20 => pac::Interrupt::RTC, // also LSE CSS
162+
21 | 22 => pac::Interrupt::ADC_COMP,
163+
23 => pac::Interrupt::I2C1,
164+
25 => pac::Interrupt::USART1,
165+
26 => pac::Interrupt::USART2,
166+
28 => pac::Interrupt::AES_RNG_LPUART1,
167+
29 => pac::Interrupt::LPTIM1,
165168
line => panic!("Line {} not supported", line),
166169
};
167170

@@ -178,3 +181,12 @@ impl ExtiExt for EXTI {
178181
});
179182
}
180183
}
184+
185+
fn assert_line_valid(line: u8) {
186+
assert!(line <= 29);
187+
assert_ne!(line, 27);
188+
189+
// Line 18 is used by the USB peripheral. On the l0x1, it is reserved.
190+
#[cfg(feature = "stm32l0x1")]
191+
assert_ne!(line, 18);
192+
}

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![no_std]
1+
#![cfg_attr(not(test), no_std)]
22
#![allow(non_camel_case_types)]
33

44
#[cfg(not(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3")))]
@@ -27,6 +27,7 @@ pub mod dma;
2727
pub mod exti;
2828
pub mod gpio;
2929
pub mod i2c;
30+
pub mod lptim;
3031
pub mod prelude;
3132
pub mod pwm;
3233
pub mod pwr;

0 commit comments

Comments
 (0)