1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
//! This module contains code used to place the MCU in low power modes.
//! Reference section 5.3.3: `Low power modes` of the L4 Reference Manual.
#[cfg(not(feature = "h7"))]
use crate::pac::PWR;
#[cfg(any(feature = "l4", feature = "l5"))]
use crate::pac;
#[cfg(any(feature = "l4", feature = "l5"))]
use crate::clocks::{Clocks, MsiRange};
use cortex_m::{asm::wfi, Peripherals};
use cfg_if::cfg_if;
// See L4 Reference Manual section 5.3.6. The values correspond to the PWR_CR1 LPMS bits.
// todo PWR_CR1, LPMS field.
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum StopMode {
Zero = 0,
One = 1,
#[cfg(not(feature = "g4"))]
Two = 2,
}
/// L4 RM, table 24
/// This assumes you're using MSI as the clock source, and changes speed by lowering the MSI speed.
/// You must select an MSI speed of 2Mhz or lower. Note that you may need to adjust peripheral
/// implementations that rely on system clock or APB speed.
#[cfg(any(feature = "l4", feature = "l5"))]
pub fn low_power_run(clocks: &mut Clocks, speed: MsiRange) {
let rcc = unsafe { &(*pac::RCC::ptr()) };
let pwr = unsafe { &(*PWR::ptr()) };
// Decrease the system clock frequency below 2 MHz
if speed as u8 > MsiRange::R2M as u8 {
panic!("Selected Msi speed must be 2Mhz or lower to enter use low power run.")
}
clocks.change_msi_speed(speed);
// LPR = 1
pwr.cr1.modify(|_, w| w.lpr().set_bit())
}
/// L4 RM, table 24
/// Return to normal run mode from low-power run. Requires you to increase the clock speed
/// manually after running this.
#[cfg(any(feature = "l4", feature = "l5"))]
pub fn return_from_low_power_run() {
let pwr = unsafe { &(*PWR::ptr()) };
// LPR = 0
pwr.cr1.modify(|_, w| w.lpr().clear_bit());
// Wait until REGLPF = 0
while pwr.sr2.read().reglpf().bit_is_set() {}
// Increase the system clock frequency
}
/// Place the system in sleep now mode. To enter `low-power sleep now`, enter low power mode
/// (eg `low_power_mode()`) before running this. RM, table 25 and 26
#[cfg(not(feature = "h7"))]
pub fn sleep_now() {
sleep();
}
/// F303 RM, table 19.
pub fn sleep_on_exit() {
let mut scb = unsafe { Peripherals::steal().SCB };
// WFI (Wait for Interrupt) (eg `cortext_m::asm::wfi()) or WFE (Wait for Event) while:
// SLEEPDEEP = 0 and SLEEPONEXIT = 1
scb.clear_sleepdeep();
// Sleep-on-exit: if the SLEEPONEXIT bit is set, the MCU enters Sleep mode as soon
// as it exits the lowest priority ISR.
scb.set_sleeponexit();
wfi();
}
cfg_if! {
if #[cfg(any(feature = "f3", feature = "f4"))] {
/// Enter `Stop` mode: the middle of the 3 low-power states avail on the
/// STM32f3.
/// To exit: Any EXTI Line configured in Interrupt mode (the corresponding EXTI
/// Interrupt vector must be enabled in the NVIC). Refer to Table 82.
/// F303 RM, table 20. F4 RM, Table 27. H742 RM, Table 38. (CSrtop on H7).
/// Run `Clocks::reselect_input()` after to re-enable PLL etc after exiting this mode.
pub fn stop() {
let mut scb = unsafe { Peripherals::steal().SCB };
let pwr = unsafe { &(*PWR::ptr()) };
// todo: On some F4 variants, you may need to `select voltage regulator
// todo mode by configuring LPDS, MRUDS, LPUDS and UDEN bits in PWR_CR.`
//WFI (Wait for Interrupt) or WFE (Wait for Event) while:
// Set SLEEPDEEP bit in ARM® Cortex®-M4 System Control register
scb.set_sleepdeep();
// Clear PDDS bit in Power Control register (PWR_CR)
// This bit is set and cleared by software. It works together with the LPDS bit.
// 0: Enter Stop mode when the CPU enters Deepsleep. The regulator status
// depends on the LPDS bit.
// 1: Enter Stop mode when the CPU enters Deepsleep.
pwr.cr.modify(|_, w| {
w.pdds().clear_bit();
// Select the voltage regulator mode by configuring LPDS bit in PWR_CR
// This bit is set and cleared by software. It works together with the PDDS bit.
// 0: Voltage regulator on during Stop mode
// 1: Voltage regulator in low-power mode during Stop mode
// pwr.cr.modify(|_, w| w.pdds().clear_bit());
w.lpds().set_bit()
});
wfi();
}
/// Enter `Standby` mode.
/// To exit: WKUP pin rising edge, RTC alarm event’s rising edge, external Reset in
/// NRST pin, IWDG Reset.
/// F303 RM, table 21.
/// Run `Clocks::reselect_input()` after to re-enable PLL etc after exiting this mode.
pub fn standby() {
let mut scb = unsafe { Peripherals::steal().SCB };
let pwr = unsafe { &(*PWR::ptr()) };
// WFI (Wait for Interrupt) or WFE (Wait for Event) while:
// Set SLEEPDEEP bit in ARM® Cortex®-M4 System Control register
scb.set_sleepdeep();
// Set PDDS bit in Power Control register (PWR_CR)
// This bit is set and cleared by software. It works together with the LPDS bit.
// 0: Enter Stop mode when the CPU enters Deepsleep. The regulator status
// depends on the LPDS bit.
// 1: Enter Standby mode when the CPU enters Deepsleep.
pwr.cr.modify(|_, w| {
w.pdds().set_bit();
// Clear WUF bit in Power Control/Status register (PWR_CSR) (Must do this by setting CWUF bit in
// PWR_CR.)
w.cwuf().set_bit()
});
wfi();
}
} else if #[cfg(any(feature = "l4", feature = "l5", feature = "g0", feature = "g4"))] {
/// Enter Stop 0, Stop 1, or Stop 2 modes. L4 Reference manual, section 5.3.6. Tables 27, 28, and 29.
/// G0 RMs, tables 30, 31, 32.
/// G4 Table 45, 47, 47.
/// Run `Clocks::reselect_input()` after to re-enable PLL etc after exiting this mode.
pub fn stop(mode: StopMode) {
let mut scb = unsafe { Peripherals::steal().SCB };
let pwr = unsafe { &(*PWR::ptr()) };
// WFI (Wait for Interrupt) or WFE (Wait for Event) while:
// – SLEEPDEEP bit is set in Cortex®-M4 System Control register
scb.set_sleepdeep();
// – No interrupt (for WFI) or event (for WFE) is pending
// – LPMS = (according to mode) in PWR_CR1
pwr.cr1.modify(|_, w| unsafe { w.lpms().bits(mode as u8) });
// Or, unimplemented:
// On Return from ISR while:
// – SLEEPDEEP bit is set in Cortex®-M4 System Control register
// – SLEEPONEXIT = 1
// – No interrupt is pending
// – LPMS = “000” in PWR_CR1
wfi();
}
/// Enter `Standby` mode. See L44 RM table 28. G4 table 47.
/// Run `Clocks::reselect_input()` after to re-enable PLL etc after exiting this mode.
pub fn standby() {
let mut scb = unsafe { Peripherals::steal().SCB };
let pwr = unsafe { &(*PWR::ptr()) };
// – SLEEPDEEP bit is set in Cortex®-M4 System Control register
scb.set_sleepdeep();
// – No interrupt (for WFI) or event (for WFE) is pending
// – LPMS = “011” in PWR_CR1
pwr.cr1.modify(|_, w| unsafe { w.lpms().bits(0b011) });
// – WUFx bits are cleared in power status register 1 (PWR_SR1)
// (Clear by setting cwfuf bits in `pwr_scr`.)
cfg_if! {
if #[cfg(feature = "l4")] {
pwr.scr.write(|w| {
w.wuf1().set_bit();
w.wuf2().set_bit();
w.wuf3().set_bit();
w.wuf4().set_bit();
w.wuf5().set_bit()
});
} else if #[cfg(feature = "g0")] {
pwr.scr.write(|w| {
w.cwuf1().set_bit();
w.cwuf2().set_bit();
// w.cwuf3().set_bit(); // todo: PAC ommission?
w.cwuf4().set_bit();
w.cwuf5().set_bit();
w.cwuf6().set_bit()
});
} else {
pwr.scr.write(|w| {
w.cwuf1().set_bit();
w.cwuf2().set_bit();
w.cwuf3().set_bit();
w.cwuf4().set_bit();
w.cwuf5().set_bit()
});
}
}
// todo: `The RTC flag corresponding to the chosen wakeup source (RTC Alarm
// todo: A, RTC Alarm B, RTC wakeup, tamper or timestamp flags) is cleared`.
// Or, unimplemented:
// On return from ISR while:
// – SLEEPDEEP bit is set in Cortex®-M4 System Control register
// – SLEEPONEXIT = 1
// – No interrupt is pending
// – LPMS = “011” in PWR_CR1 and
// – WUFx bits are cleared in power status register 1 (PWR_SR1)
// – The RTC flag corresponding to the chosen wakeup source (RTC Alarm
// A, RTC Alarm B, RTC wakeup, tamper or timestamp flags) is cleared
wfi();
}
/// Enter `Shutdown mode` mode: the lowest-power of the 3 low-power states avail. See
/// L4 Table 31. G4 table 48.
pub fn shutdown() {
let mut scb = unsafe { Peripherals::steal().SCB };
let pwr = unsafe { &(*PWR::ptr()) };
// – SLEEPDEEP bit is set in Cortex®-M4 System Control register
scb.set_sleepdeep();
// – No interrupt (for WFI) or event (for WFE) is pending
// – LPMS = “011” in PWR_CR1
pwr.cr1.modify(|_, w| unsafe { w.lpms().bits(0b100) });
// – WUFx bits are cleared in power status register 1 (PWR_SR1)
// (Clear by setting cwfuf bits in `pwr_scr`.)
// pwr.scr.write(|_, w| {
// w.cwuf1().set_bit();
// w.cwuf2().set_bit();
// w.cwuf3().set_bit();
// w.cwuf4().set_bit();
// w.cwuf5().set_bit();
// })
// Or, unimplemented:
// On return from ISR while:
// – SLEEPDEEP bit is set in Cortex®-M4 System Control register
// – SLEEPONEXT = 1
// – No interrupt is pending
// – LPMS = “1XX” in PWR_CR1 and
// – WUFx bits are cleared in power status register 1 (PWR_SR1)
// – The RTC flag corresponding to the chosen wakeup source (RTC
// Alarm A, RTC Alarm B, RTC wakeup, tamper or timestamp flags) is
// cleared
wfi();
}
} else { // H7
/// The CSleep mode applies only to the CPU subsystem. In CSleep mode, the CPU clock is
/// stopped. The CPU subsystem peripheral clocks operate according to the values of
/// PERxLPEN bits in RCC_C1_xxxxENR or RCC_DnxxxxENR. See H743 RM, Table 37.
pub fn csleep() {
sleep();
}
/// Stops clocks on the CPU subsystem. H742 RM, Table 38.
pub fn cstop() {
let mut scb = unsafe { Peripherals::steal().SCB };
// WFI (Wait for Interrupt) or WFE (Wait for Event) while:
// Set SLEEPDEEP bit in ARM® Cortex®-M4 System Control register
scb.set_sleepdeep();
// – CPU NVIC interrupts and events cleared.
// – All CPU EXTI Wakeup sources are cleared.
wfi();
}
// /// Stops clocks on the D1 and D2 domain. H742 RM, Table 40.
// pub fn dstop(scb: &mut SCB, pwr: &mut PWR) {
//
// // -The domain CPU subsystem enters CStop.
// // (Don't call self.cstop, since that handles WFI and reselecting clocks as well).
// scb.set_sleepdeep();
//
//
// // todo?
// // – The CPU subsystem has an allocated peripheral in the D2 domain and
// // enters CStop.
// // – The CPU subsystem deallocated its last peripheral in the D2 domain.
// // – The PDDS_Dn bit for the domain selects Stop mode.
//
// wfi();
// }
// /// Enter `Standby` mode: the lowest-power of the 3 low-power states avail on the
// /// STM32f3.
// /// To exit: WKUP pin rising edge, RTC alarm event’s rising edge, external Reset in
// /// NRST pin, IWDG Reset.
// /// RM, table 21.
// /// Run `Clocks::reselect_input()` after to re-enable PLL etc after exiting this mode.
// pub fn standby() {
// // WFI (Wait for Interrupt) or WFE (Wait for Event) while:
//
// // Set SLEEPDEEP bit in ARM® Cortex®-M4 System Control register
// scb.set_sleepdeep();
//
// // Set PDDS bit in Power Control register (PWR_CR)
// // This bit is set and cleared by software. It works together with the LPDS bit.
// // 0: Enter Stop mode when the CPU enters Deepsleep. The regulator status
// // depends on the LPDS bit.
// // 1: Enter Standby mode when the CPU enters Deepsleep.
// pwr.cr.modify(|_, w| {
// w.pdds().set_bit();
// // Clear WUF bit in Power Control/Status register (PWR_CSR) (Must do this by setting CWUF bit in
// // PWR_CR.)
// w.cwuf().set_bit()
// });
// wfi();
// }
}
}
/// This function is used by both `sleep_now` (non-H7), and `csleep` (H7), so that the names
/// can correctly reflect functionality.
fn sleep() {
// todo: This function is identical to `sleep_now`, but we'd like to keep the separate
// todo name for H7.
let mut scb = unsafe { Peripherals::steal().SCB };
// WFI (Wait for Interrupt) (eg `cortext_m::asm::wfi()) or WFE (Wait for Event) while:
// – SLEEPDEEP = 0
// – No interrupt (for WFI) or event (for WFE) is pending
scb.clear_sleepdeep();
// Or, unimplemented:
// On return from ISR while:
// // SLEEPDEEP = 0 and SLEEPONEXIT = 1
// scb.clear_sleepdeep();
// scb.set_sleeponexit();
// Sleep-now: if the SLEEPONEXIT bit is cleared, the MCU enters Sleep mode as soon
// as WFI or WFE instruction is executed.
scb.clear_sleeponexit();
wfi();
}