Skip to content

Commit 5046e88

Browse files
authored
Added support for low power run and shutdown modes (#274)
* Added support for lower power run mode and low poser voltage regulator mode * Added shutdown lower power mode support. * Added wake-up reason reading * Removed 'allow_unused' from some of the PWR registers * Added PWR specific error type * WKUP fields management in PWR re-designed with bitfield!{} * WKUP fields management in PWR re-designed with bitfield!{} * Fixed formatting * Rebased over latest main
1 parent 62f19f3 commit 5046e88

File tree

2 files changed

+140
-5
lines changed

2 files changed

+140
-5
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ stm32l4 = "0.14.0"
2727
embedded-dma = "0.1"
2828
bxcan = ">=0.4, <0.7"
2929
fugit = "0.3.5"
30+
bitfield = "0.13.2"
3031

3132
[dependencies.rand_core]
3233
version = "0.6.2"

src/pwr.rs

Lines changed: 139 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,125 @@
11
//! Power management
22
3-
use crate::rcc::{Enable, APB1R1};
3+
use crate::rcc::{Clocks, Enable, APB1R1};
44
use crate::stm32::{pwr, PWR};
5+
use bitfield::{bitfield, BitRange};
6+
use cortex_m::peripheral::SCB;
7+
use fugit::RateExtU32;
8+
9+
/// PWR error
10+
#[non_exhaustive]
11+
#[derive(Debug)]
12+
pub enum Error {
13+
/// Power regulator con not be switched to the low-power voltage due to the system clock frequency being higher than 26MHz
14+
SysClkTooHighVos,
15+
/// System can not be switched to the low-power run mode due to the system clock frequency being higher than 2MHz
16+
SysClkTooHighLpr,
17+
}
18+
19+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20+
pub enum VosRange {
21+
#[doc = "High-Performance range, 1.2V, up to 80 MHz"]
22+
HighPerformance = 0b01,
23+
#[doc = "Low-power range, 1.0V, up to 26MHz"]
24+
LowPower = 0b10,
25+
}
26+
27+
bitfield! {
28+
pub struct WakeUpSource(u16);
29+
impl Debug;
30+
// The fields default to u16
31+
pub wkup1, set_wkup1: 0;
32+
pub wkup2, set_wkup2: 1;
33+
pub wkup3, set_wkup3: 2;
34+
pub wkup4, set_wkup4: 3;
35+
pub wkup5, set_wkup5: 4;
36+
pub internal_wkup, set_internal_wkup: 15;
37+
}
538

639
pub struct Pwr {
740
pub cr1: CR1,
841
pub cr2: CR2,
942
pub cr3: CR3,
1043
pub cr4: CR4,
44+
pub scr: SCR,
45+
pub sr1: SR1,
46+
}
47+
48+
impl Pwr {
49+
/// Configures dynamic voltage regulator range
50+
///
51+
/// Will panic if low-power range is selected for higher system clock
52+
pub fn set_power_range(&mut self, range: VosRange, clocks: &Clocks) -> Result<(), Error> {
53+
match range {
54+
VosRange::HighPerformance => unsafe {
55+
{
56+
self.cr1
57+
.reg()
58+
.modify(|_, w| w.vos().bits(VosRange::HighPerformance as u8))
59+
}
60+
Ok(())
61+
},
62+
VosRange::LowPower => {
63+
if clocks.sysclk() > 26.MHz::<1, 1>() {
64+
Err(Error::SysClkTooHighVos)
65+
} else {
66+
unsafe {
67+
self.cr1
68+
.reg()
69+
.modify(|_, w| w.vos().bits(VosRange::LowPower as u8))
70+
}
71+
Ok(())
72+
}
73+
}
74+
}
75+
}
76+
77+
/// Switches the system into low power run mode
78+
pub fn low_power_run(&mut self, clocks: &Clocks) -> Result<(), Error> {
79+
if clocks.sysclk() > 2.MHz::<1, 1>() {
80+
Err(Error::SysClkTooHighLpr)
81+
} else {
82+
self.cr1.reg().modify(|_, w| w.lpr().set_bit());
83+
Ok(())
84+
}
85+
}
86+
87+
/// Enters 'Shutdown' low power mode.
88+
pub fn shutdown(&mut self, wkup: &WakeUpSource, scb: &mut SCB) -> ! {
89+
unsafe {
90+
self.cr3.reg().modify(|_, w| w.bits(wkup.bit_range(0, 7)));
91+
}
92+
93+
if wkup.internal_wkup() {
94+
// Can't apply directly due to the APC and RPS bits
95+
self.cr3.reg().modify(|_, w| w.ewf().set_bit())
96+
}
97+
scb.set_sleepdeep();
98+
self.scr.reg().write(|w| {
99+
w.wuf1()
100+
.set_bit()
101+
.wuf2()
102+
.set_bit()
103+
.wuf3()
104+
.set_bit()
105+
.wuf4()
106+
.set_bit()
107+
.wuf5()
108+
.set_bit()
109+
.sbf()
110+
.set_bit()
111+
});
112+
unsafe { self.cr1.reg().modify(|_, w| w.lpms().bits(0b111)) };
113+
cortex_m::asm::dsb();
114+
cortex_m::asm::wfi();
115+
loop {}
116+
}
117+
118+
/// Returns the reason, why wakeup from shutdown happened. In case there is more then one,
119+
/// a single random reason will be returned
120+
pub fn read_wakeup_reason(&mut self) -> WakeUpSource {
121+
WakeUpSource(self.sr1.reg().read().bits() as u16)
122+
}
11123
}
12124

13125
/// Extension trait that constrains the `PWR` peripheral
@@ -25,6 +137,8 @@ impl PwrExt for PWR {
25137
cr2: CR2 { _0: () },
26138
cr3: CR3 { _0: () },
27139
cr4: CR4 { _0: () },
140+
scr: SCR { _0: () },
141+
sr1: SR1 { _0: () },
28142
}
29143
}
30144
}
@@ -35,8 +149,6 @@ pub struct CR1 {
35149
}
36150

37151
impl CR1 {
38-
// TODO remove `allow`
39-
#[allow(dead_code)]
40152
pub(crate) fn reg(&mut self) -> &pwr::CR1 {
41153
// NOTE(unsafe) this proxy grants exclusive access to this register
42154
unsafe { &(*PWR::ptr()).cr1 }
@@ -61,8 +173,6 @@ pub struct CR3 {
61173
}
62174

63175
impl CR3 {
64-
// TODO remove `allow`
65-
#[allow(dead_code)]
66176
pub(crate) fn reg(&mut self) -> &pwr::CR3 {
67177
// NOTE(unsafe) this proxy grants exclusive access to this register
68178
unsafe { &(*PWR::ptr()).cr3 }
@@ -81,3 +191,27 @@ impl CR4 {
81191
unsafe { &(*PWR::ptr()).cr4 }
82192
}
83193
}
194+
195+
/// SCR
196+
pub struct SCR {
197+
_0: (),
198+
}
199+
200+
impl SCR {
201+
pub(crate) fn reg(&mut self) -> &pwr::SCR {
202+
// NOTE(unsafe) this proxy grants exclusive access to this register
203+
unsafe { &(*PWR::ptr()).scr }
204+
}
205+
}
206+
207+
/// SCR
208+
pub struct SR1 {
209+
_0: (),
210+
}
211+
212+
impl SR1 {
213+
pub(crate) fn reg(&mut self) -> &pwr::SR1 {
214+
// NOTE(unsafe) this proxy grants exclusive access to this register
215+
unsafe { &(*PWR::ptr()).sr1 }
216+
}
217+
}

0 commit comments

Comments
 (0)