diff --git a/src/rcc.rs b/src/rcc.rs index 09ccb5c..78a16d5 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -1,2 +1,188 @@ +//! Reset and Clock Control +//! + +use crate::stm32::RCC; +use crate::time::Hertz; + +#[cfg(feature = "log")] +use log::debug; + mod core_clocks; mod reset_reason; + +pub use core_clocks::CoreClocks; +pub use reset_reason::ResetReason; + +/// Configuration of the core clocks +pub struct Config { + hse: Option, + bypass_hse: bool, + lse: Option, + sys_ck: Option, + per_ck: Option, + audio_ck: Option, + rcc_hclk: Option, + rcc_pclk1: Option, + rcc_pclk2: Option, + rcc_pclk3: Option, + #[cfg(feature = "rm0481")] + rcc_pclk4: Option, +} + +/// Extension trait that constrains the `RCC` peripheral +pub trait RccExt { + /// Constrains the `RCC` peripheral so it plays nicely with the + /// other abstractions + fn constrain(self) -> Rcc; +} + +impl RccExt for RCC { + fn constrain(self) -> Rcc { + Rcc { + config: Config { + hse: None, + bypass_hse: false, + lse: None, + sys_ck: None, + per_ck: None, + audio_ck: None, + rcc_hclk: None, + rcc_pclk1: None, + rcc_pclk2: None, + rcc_pclk3: None, + #[cfg(feature = "rm0481")] + rcc_pclk4: None, + }, + rb: self, + } + } +} + +/// Constrained RCC peripheral +/// +/// Generated by calling `constrain` on the PAC's RCC peripheral. +/// +/// ```rust +/// let dp = stm32::Peripherals::take().unwrap(); +/// let rcc = dp.RCC.constrain(); +/// ``` +pub struct Rcc { + config: Config, + pub(crate) rb: RCC, +} + +impl Rcc { + /// Gets and clears the reason of why the mcu was reset + pub fn get_reset_reason(&mut self) -> ResetReason { + reset_reason::get_reset_reason(&mut self.rb) + } +} + +/// Core Clock Distribution and Reset (CCDR) +/// +/// Generated when the RCC is frozen. The configuration of the Sys_Ck `sys_ck`, +/// AHB clocks `hclk`, APB clocks `pclkN` and PLL outputs `pllN_X_ck` are +/// frozen. However the distribution of some clocks may still be modified and +/// peripherals enabled / reset by passing this object to other implementations +/// in this stack. +pub struct Ccdr { + /// A record of the frozen core clock frequencies + pub clocks: CoreClocks, +} + +const MAX_SYSCLK_FREQ_HZ: u32 = 250_000_000; + +/// Setter defintion for pclk 1 - 4 +macro_rules! pclk_setter { + ($($name:ident: $pclk:ident,)+) => { + $( + /// Set the peripheral clock frequency for APB + /// peripherals. + #[must_use] + pub fn $name(mut self, freq: Hertz) -> Self { + assert!(freq.raw() <= MAX_SYSCLK_FREQ_HZ, + "Max frequency is {MAX_SYSCLK_FREQ_HZ}Hz"); + self.config.$pclk = Some(freq.raw()); + self + } + )+ + }; +} + +impl Rcc { + /// Uses HSE (external oscillator) instead of HSI (internal RC + /// oscillator) as the clock source. Will result in a hang if an + /// external oscillator is not connected or it fails to start. + #[must_use] + pub fn use_hse(mut self, freq: Hertz) -> Self { + self.config.hse = Some(freq.raw()); + self + } + + /// Use an external clock signal rather than a crystal oscillator, + /// bypassing the XTAL driver. + #[must_use] + pub fn bypass_hse(mut self) -> Self { + self.config.bypass_hse = true; + self + } + + /// Set SYSCLK frequency + #[must_use] + pub fn sys_ck(mut self, freq: Hertz) -> Self { + assert!( + freq.raw() <= MAX_SYSCLK_FREQ_HZ, + "Max frequency is {MAX_SYSCLK_FREQ_HZ}Hz" + ); + self.config.sys_ck = Some(freq.raw()); + self + } + + /// Set SYSCLK frequency - ALIAS + #[must_use] + pub fn sysclk(self, freq: Hertz) -> Self { + self.sys_ck(freq) + } + + /// Set peripheral clock frequency + #[must_use] + pub fn per_ck(mut self, freq: Hertz) -> Self { + self.config.per_ck = Some(freq.raw()); + self + } + + /// Set low speed external clock frequency + pub fn lse_ck(mut self, freq: Hertz) -> Self { + self.config.lse = Some(freq.raw()); + self + } + + /// Set external AUDIOCLK frequency + #[must_use] + pub fn audio_ck(mut self, freq: Hertz) -> Self { + self.config.audio_ck = Some(freq.raw()); + self + } + + /// Set the peripheral clock frequency for AHB peripherals. + #[must_use] + pub fn hclk(mut self, freq: Hertz) -> Self { + assert!( + freq.raw() <= MAX_SYSCLK_FREQ_HZ, + "Max frequency is {MAX_SYSCLK_FREQ_HZ}Hz" + ); + self.config.rcc_hclk = Some(freq.raw()); + self + } + + pclk_setter! { + pclk1: rcc_pclk1, + pclk2: rcc_pclk2, + pclk3: rcc_pclk3, + } + + #[cfg(feature = "rm0481")] + pclk_setter! { + pclk4: rcc_pclk4, + } +} diff --git a/src/rcc/core_clocks.rs b/src/rcc/core_clocks.rs index e65036f..3992802 100644 --- a/src/rcc/core_clocks.rs +++ b/src/rcc/core_clocks.rs @@ -79,7 +79,6 @@ macro_rules! pll_getter { }; } -#[allow(dead_code)] impl CoreClocks { /// Returns the frequency of AHB1,2,3 busses pub fn hclk(&self) -> Hertz { diff --git a/src/rcc/reset_reason.rs b/src/rcc/reset_reason.rs index 1f31097..a05285b 100644 --- a/src/rcc/reset_reason.rs +++ b/src/rcc/reset_reason.rs @@ -5,7 +5,6 @@ use core::fmt::Display; /// Gets and clears the reason of why the mcu was reset #[rustfmt::skip] -#[allow(dead_code)] pub fn get_reset_reason(rcc: &mut crate::stm32::RCC) -> ResetReason { let reset_reason = rcc.rsr().read(); @@ -55,7 +54,6 @@ pub fn get_reset_reason(rcc: &mut crate::stm32::RCC) -> ResetReason { /// Gives the reason why the mcu was reset #[derive(Debug, Copy, Clone)] -#[allow(dead_code)] pub enum ResetReason { /// The mcu went from not having power to having power and resetting PowerOnReset,