Skip to content

Commit

Permalink
pwr: Add power configuration module
Browse files Browse the repository at this point in the history
  • Loading branch information
astapleton committed Jul 16, 2024
1 parent 40a8637 commit 35d3a65
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 2 deletions.
5 changes: 3 additions & 2 deletions examples/blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
mod utilities;

use cortex_m_rt::entry;
use stm32h5xx_hal::pac;
use stm32h5xx_hal::{pac, prelude::*};

#[entry]
fn main() -> ! {
utilities::logger::init();

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
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ pub use crate::stm32::interrupt;

#[cfg(feature = "device-selected")]
pub mod prelude;

#[cfg(feature = "device-selected")]
pub mod pwr;
2 changes: 2 additions & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Prelude

pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt;

pub use fugit::{ExtU32 as _, RateExtU32 as _};
180 changes: 180 additions & 0 deletions src/pwr.rs
Original file line number Diff line number Diff line change
@@ -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<VoltageScale> 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<ACTVOSR> 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 }
}
}

0 comments on commit 35d3a65

Please sign in to comment.