From 41babc0213bcd4d712214f6ca49a8f86e1e78542 Mon Sep 17 00:00:00 2001 From: astapleton Date: Thu, 7 Sep 2023 14:33:33 -0700 Subject: [PATCH] pwr: Add power configuration module --- examples/blinky.rs | 5 +- src/lib.rs | 3 + src/prelude.rs | 2 + src/pwr.rs | 180 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 src/pwr.rs diff --git a/examples/blinky.rs b/examples/blinky.rs index 6c846fd..0cfe412 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -4,7 +4,7 @@ mod utilities; use cortex_m_rt::entry; -use stm32h5xx_hal::pac; +use stm32h5xx_hal::{pac, prelude::*}; #[entry] fn main() -> ! { @@ -12,7 +12,8 @@ fn main() -> ! { let dp = pac::Peripherals::take().unwrap(); - // TODO: Power/clock config is required before blinky can... blink. + let pwr = dp.PWR.constrain(); + let _pwrcfg = pwr.vos0().freeze(); dp.GPIOA.moder().write(|w| w.mode5().output()); // output dp.GPIOA.pupdr().write(|w| w.pupd5().pull_up()); // pull-up diff --git a/src/lib.rs b/src/lib.rs index 2a9d825..e118b94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,3 +39,6 @@ pub use crate::stm32::interrupt; #[cfg(feature = "device-selected")] pub mod prelude; + +#[cfg(feature = "device-selected")] +pub mod pwr; diff --git a/src/prelude.rs b/src/prelude.rs index 187ad10..743c3f6 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,3 +1,5 @@ //! Prelude +pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt; + pub use fugit::{ExtU32 as _, RateExtU32 as _}; diff --git a/src/pwr.rs b/src/pwr.rs new file mode 100644 index 0000000..4dfc662 --- /dev/null +++ b/src/pwr.rs @@ -0,0 +1,180 @@ +//! Power configuration +//! +//! This module configures the PWR unit to provide the core voltage `VCORE`. +//! The processor supports multiple voltage scaling modes, from VOS3 (lowest +//! performance, lowest power consumption) to VOS0 (highest performance, +//! highest power consumption). +//! +//! After reset the system is in VOS3. This power configuration module allows +//! modes VOS0 to VOS3 to be selected. +//! +//! ```rust +//! let dp = pac::Peripherals::take().unwrap(); +//! +//! let pwr = dp.PWR.constrain(); +//! let pwrcfg = pwr.vos3().freeze(); +//! +//! assert_eq!(pwrcfg.vos(), VoltageScale::Scale3); +//! ``` +//! +//! If no mode is explicitly selected, it defaults to VOS0 after calling `freeze`. +//! +//! ```rust +//! let dp = pac::Peripherals::take().unwrap(); +//! +//! let pwr = dp.PWR.constrain(); +//! let pwrcfg = pwr.freeze(); +//! +//! assert_eq!(pwrcfg.vos(), VoltageScale::Scale0); +//! ``` +//! +//! +use crate::stm32::pwr::voscr::VOS; +use crate::stm32::pwr::vossr::ACTVOSR; +use crate::stm32::PWR; + +/// Extension trait that constrains the `PWR` peripheral +pub trait PwrExt { + fn constrain(self) -> Pwr; +} + +impl PwrExt for PWR { + fn constrain(self) -> Pwr { + Pwr { + rb: self, + target_vos: VoltageScale::Scale0, + } + } +} + +/// Constrained PWR peripheral +/// +/// Generated by calling `constrain` on the PAC's PWR peripheral. +pub struct Pwr { + pub(crate) rb: PWR, + target_vos: VoltageScale, +} + +/// Voltage Scale +/// +/// Represents the voltage range feeding the CPU core. The maximum core +/// clock frequency depends on this value. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum VoltageScale { + /// VOS 0 range VCORE 1.30V - 1.40V + Scale0, + /// VOS 1 range VCORE 1.15V - 1.26V + Scale1, + /// VOS 2 range VCORE 1.05V - 1.15V + Scale2, + /// VOS 3 range VCORE 0.95V - 1.05V + Scale3, +} + +impl From for VOS { + fn from(value: VoltageScale) -> Self { + match value { + VoltageScale::Scale3 => VOS::Vos3, + VoltageScale::Scale2 => VOS::Vos2, + VoltageScale::Scale1 => VOS::Vos1, + VoltageScale::Scale0 => VOS::Vos0, + } + } +} + +impl From for VoltageScale { + fn from(value: ACTVOSR) -> Self { + match value { + ACTVOSR::Vos3 => VoltageScale::Scale3, + ACTVOSR::Vos2 => VoltageScale::Scale2, + ACTVOSR::Vos1 => VoltageScale::Scale1, + ACTVOSR::Vos0 => VoltageScale::Scale0, + } + } +} + +/// Power Configuration +/// +/// Generated when the PWR peripheral is frozen. +/// longer be changed. +pub struct PowerConfiguration { + pub(crate) vos: VoltageScale, +} + +impl PowerConfiguration { + /// Gets the `VoltageScale` which was configured by `Pwr::freeze()`. + pub fn vos(&self) -> VoltageScale { + self.vos + } +} + +/// Internal power methods +impl Pwr { + /// Transition between voltage scaling levels + fn voltage_scaling_transition(&self, new_scale: VoltageScale) { + // ************************************ + // Note: STM32H503 Errata 2.2.13 states that for Rev A, Z this transition should only + // be executed from RAM. It's unclear if these silicon revisions saw wide release, so + // this is left executed from flash. + // ************************************ + + self.rb.voscr().write(|w| w.vos().variant(new_scale.into())); + while self.rb.vossr().read().vosrdy().is_not_ready() {} + } + + /// Returns a reference to the inner peripheral + pub fn inner(&self) -> &PWR { + &self.rb + } + + /// Returns a mutable reference to the inner peripheral + pub fn inner_mut(&mut self) -> &mut PWR { + &mut self.rb + } +} + +/// Builder methods +impl Pwr { + /// Configure Voltage Scale 0. This is the default configuration + #[must_use] + pub fn vos0(mut self) -> Self { + self.target_vos = VoltageScale::Scale0; + self + } + /// Configure Voltage Scale 1 + #[must_use] + pub fn vos1(mut self) -> Self { + self.target_vos = VoltageScale::Scale1; + self + } + /// Configure Voltage Scale 2 + #[must_use] + pub fn vos2(mut self) -> Self { + self.target_vos = VoltageScale::Scale2; + self + } + /// Configure Voltage Scale 3 + #[must_use] + pub fn vos3(mut self) -> Self { + self.target_vos = VoltageScale::Scale3; + self + } + + pub fn freeze(self) -> PowerConfiguration { + // Validate the supply configuration. If you are stuck here, it is + // because the voltages on your board do not match those specified + // in the VOSCR.VOS By default after reset VOS = Scale 3, so check + // that the voltage on the VCAP pins = 1.0V. + while self.rb.vossr().read().actvosrdy().is_not_ready() {} + + let current_scale = + VoltageScale::from(self.rb.vossr().read().actvos().variant()); + + let vos = self.target_vos; + if current_scale != vos { + self.voltage_scaling_transition(vos); + } + + PowerConfiguration { vos } + } +}