Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pwr: Add power configuration module #7

Merged
merged 1 commit into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
astapleton marked this conversation as resolved.
Show resolved Hide resolved
//!
//! 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 }
}
}