|
| 1 | +//! Reset and Clock Control |
| 2 | +//! |
| 3 | +
|
| 4 | +use crate::stm32::RCC; |
| 5 | +use crate::time::Hertz; |
| 6 | + |
| 7 | +#[cfg(feature = "log")] |
| 8 | +use log::debug; |
| 9 | + |
1 | 10 | mod core_clocks;
|
2 | 11 | mod reset_reason;
|
| 12 | + |
| 13 | +pub use core_clocks::CoreClocks; |
| 14 | +pub use reset_reason::ResetReason; |
| 15 | + |
| 16 | +/// Configuration of the core clocks |
| 17 | +pub struct Config { |
| 18 | + hse: Option<u32>, |
| 19 | + bypass_hse: bool, |
| 20 | + lse: Option<u32>, |
| 21 | + sys_ck: Option<u32>, |
| 22 | + per_ck: Option<u32>, |
| 23 | + audio_ck: Option<u32>, |
| 24 | + rcc_hclk: Option<u32>, |
| 25 | + rcc_pclk1: Option<u32>, |
| 26 | + rcc_pclk2: Option<u32>, |
| 27 | + rcc_pclk3: Option<u32>, |
| 28 | + #[cfg(feature = "rm0481")] |
| 29 | + rcc_pclk4: Option<u32>, |
| 30 | +} |
| 31 | + |
| 32 | +/// Extension trait that constrains the `RCC` peripheral |
| 33 | +pub trait RccExt { |
| 34 | + /// Constrains the `RCC` peripheral so it plays nicely with the |
| 35 | + /// other abstractions |
| 36 | + fn constrain(self) -> Rcc; |
| 37 | +} |
| 38 | + |
| 39 | +impl RccExt for RCC { |
| 40 | + fn constrain(self) -> Rcc { |
| 41 | + Rcc { |
| 42 | + config: Config { |
| 43 | + hse: None, |
| 44 | + bypass_hse: false, |
| 45 | + lse: None, |
| 46 | + sys_ck: None, |
| 47 | + per_ck: None, |
| 48 | + audio_ck: None, |
| 49 | + rcc_hclk: None, |
| 50 | + rcc_pclk1: None, |
| 51 | + rcc_pclk2: None, |
| 52 | + rcc_pclk3: None, |
| 53 | + #[cfg(feature = "rm0481")] |
| 54 | + rcc_pclk4: None, |
| 55 | + }, |
| 56 | + rb: self, |
| 57 | + } |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +/// Constrained RCC peripheral |
| 62 | +/// |
| 63 | +/// Generated by calling `constrain` on the PAC's RCC peripheral. |
| 64 | +/// |
| 65 | +/// ```rust |
| 66 | +/// let dp = stm32::Peripherals::take().unwrap(); |
| 67 | +/// let rcc = dp.RCC.constrain(); |
| 68 | +/// ``` |
| 69 | +pub struct Rcc { |
| 70 | + config: Config, |
| 71 | + pub(crate) rb: RCC, |
| 72 | +} |
| 73 | + |
| 74 | +impl Rcc { |
| 75 | + /// Gets and clears the reason of why the mcu was reset |
| 76 | + pub fn get_reset_reason(&mut self) -> ResetReason { |
| 77 | + reset_reason::get_reset_reason(&mut self.rb) |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +/// Core Clock Distribution and Reset (CCDR) |
| 82 | +/// |
| 83 | +/// Generated when the RCC is frozen. The configuration of the Sys_Ck `sys_ck`, |
| 84 | +/// AHB clocks `hclk`, APB clocks `pclkN` and PLL outputs `pllN_X_ck` are |
| 85 | +/// frozen. However the distribution of some clocks may still be modified and |
| 86 | +/// peripherals enabled / reset by passing this object to other implementations |
| 87 | +/// in this stack. |
| 88 | +pub struct Ccdr { |
| 89 | + /// A record of the frozen core clock frequencies |
| 90 | + pub clocks: CoreClocks, |
| 91 | +} |
| 92 | + |
| 93 | +const MAX_SYSCLK_FREQ_HZ: u32 = 250_000_000; |
| 94 | + |
| 95 | +/// Setter defintion for pclk 1 - 4 |
| 96 | +macro_rules! pclk_setter { |
| 97 | + ($($name:ident: $pclk:ident,)+) => { |
| 98 | + $( |
| 99 | + /// Set the peripheral clock frequency for APB |
| 100 | + /// peripherals. |
| 101 | + #[must_use] |
| 102 | + pub fn $name(mut self, freq: Hertz) -> Self { |
| 103 | + assert!(freq.raw() <= MAX_SYSCLK_FREQ_HZ, |
| 104 | + "Max frequency is {MAX_SYSCLK_FREQ_HZ}Hz"); |
| 105 | + self.config.$pclk = Some(freq.raw()); |
| 106 | + self |
| 107 | + } |
| 108 | + )+ |
| 109 | + }; |
| 110 | +} |
| 111 | + |
| 112 | +impl Rcc { |
| 113 | + /// Uses HSE (external oscillator) instead of HSI (internal RC |
| 114 | + /// oscillator) as the clock source. Will result in a hang if an |
| 115 | + /// external oscillator is not connected or it fails to start. |
| 116 | + #[must_use] |
| 117 | + pub fn use_hse(mut self, freq: Hertz) -> Self { |
| 118 | + self.config.hse = Some(freq.raw()); |
| 119 | + self |
| 120 | + } |
| 121 | + |
| 122 | + /// Use an external clock signal rather than a crystal oscillator, |
| 123 | + /// bypassing the XTAL driver. |
| 124 | + #[must_use] |
| 125 | + pub fn bypass_hse(mut self) -> Self { |
| 126 | + self.config.bypass_hse = true; |
| 127 | + self |
| 128 | + } |
| 129 | + |
| 130 | + /// Set SYSCLK frequency |
| 131 | + #[must_use] |
| 132 | + pub fn sys_ck(mut self, freq: Hertz) -> Self { |
| 133 | + assert!( |
| 134 | + freq.raw() <= MAX_SYSCLK_FREQ_HZ, |
| 135 | + "Max frequency is {MAX_SYSCLK_FREQ_HZ}Hz" |
| 136 | + ); |
| 137 | + self.config.sys_ck = Some(freq.raw()); |
| 138 | + self |
| 139 | + } |
| 140 | + |
| 141 | + /// Set SYSCLK frequency - ALIAS |
| 142 | + #[must_use] |
| 143 | + pub fn sysclk(self, freq: Hertz) -> Self { |
| 144 | + self.sys_ck(freq) |
| 145 | + } |
| 146 | + |
| 147 | + /// Set peripheral clock frequency |
| 148 | + #[must_use] |
| 149 | + pub fn per_ck(mut self, freq: Hertz) -> Self { |
| 150 | + self.config.per_ck = Some(freq.raw()); |
| 151 | + self |
| 152 | + } |
| 153 | + |
| 154 | + /// Set low speed external clock frequency |
| 155 | + pub fn lse_ck(mut self, freq: Hertz) -> Self { |
| 156 | + self.config.lse = Some(freq.raw()); |
| 157 | + self |
| 158 | + } |
| 159 | + |
| 160 | + /// Set external AUDIOCLK frequency |
| 161 | + #[must_use] |
| 162 | + pub fn audio_ck(mut self, freq: Hertz) -> Self { |
| 163 | + self.config.audio_ck = Some(freq.raw()); |
| 164 | + self |
| 165 | + } |
| 166 | + |
| 167 | + /// Set the peripheral clock frequency for AHB peripherals. |
| 168 | + #[must_use] |
| 169 | + pub fn hclk(mut self, freq: Hertz) -> Self { |
| 170 | + assert!( |
| 171 | + freq.raw() <= MAX_SYSCLK_FREQ_HZ, |
| 172 | + "Max frequency is {MAX_SYSCLK_FREQ_HZ}Hz" |
| 173 | + ); |
| 174 | + self.config.rcc_hclk = Some(freq.raw()); |
| 175 | + self |
| 176 | + } |
| 177 | + |
| 178 | + pclk_setter! { |
| 179 | + pclk1: rcc_pclk1, |
| 180 | + pclk2: rcc_pclk2, |
| 181 | + pclk3: rcc_pclk3, |
| 182 | + } |
| 183 | + |
| 184 | + #[cfg(feature = "rm0481")] |
| 185 | + pclk_setter! { |
| 186 | + pclk4: rcc_pclk4, |
| 187 | + } |
| 188 | +} |
0 commit comments