diff options
| author | Thales Fragoso <[email protected]> | 2021-07-27 23:09:48 -0300 |
|---|---|---|
| committer | Thales Fragoso <[email protected]> | 2021-07-29 18:43:13 -0300 |
| commit | 5abaf8e9d6a673bbbf7e900b15a77d6e7d1a2962 (patch) | |
| tree | 0d67073f748e34c29f560ba6a06be4c2112a16e5 | |
| parent | 1ed65cb9e0f495ede0976f83f87b4a987478d466 (diff) | |
Start working on the F4 PLL
| -rw-r--r-- | embassy-stm32/src/rcc/f4/mod.rs | 418 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 3 |
2 files changed, 263 insertions, 158 deletions
diff --git a/embassy-stm32/src/rcc/f4/mod.rs b/embassy-stm32/src/rcc/f4/mod.rs index d47510da7..de330d36d 100644 --- a/embassy-stm32/src/rcc/f4/mod.rs +++ b/embassy-stm32/src/rcc/f4/mod.rs | |||
| @@ -1,115 +1,186 @@ | |||
| 1 | pub use super::types::*; | 1 | use crate::pac::{FLASH, RCC}; |
| 2 | use crate::pac; | 2 | use crate::peripherals; |
| 3 | use crate::peripherals::{self, RCC}; | ||
| 4 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; |
| 5 | use crate::time::Hertz; | 4 | use crate::time::Hertz; |
| 6 | use crate::time::U32Ext; | ||
| 7 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 8 | use embassy::util::Unborrow; | 6 | use embassy::util::Unborrow; |
| 9 | use embassy_hal_common::unborrow; | ||
| 10 | use pac::rcc::vals::{Hpre, Ppre, Sw}; | ||
| 11 | 7 | ||
| 12 | /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | 8 | const HSI: u32 = 16_000_000; |
| 13 | /// and with the addition of the init function to configure a system clock. | ||
| 14 | 9 | ||
| 15 | /// Only the basic setup using the HSE and HSI clocks are supported as of now. | 10 | // TODO: This is for the F401, find a way to make it compile time configurable |
| 11 | const SYSCLK_MIN: u32 = 24_000_000; | ||
| 12 | const SYSCLK_MAX: u32 = 84_000_000; | ||
| 13 | const PCLK2_MAX: u32 = SYSCLK_MAX; | ||
| 14 | const PCLK1_MAX: u32 = PCLK2_MAX / 2; | ||
| 16 | 15 | ||
| 17 | /// HSI speed | 16 | /// Clocks configutation |
| 18 | pub const HSI_FREQ: u32 = 16_000_000; | 17 | #[non_exhaustive] |
| 18 | #[derive(Default)] | ||
| 19 | pub struct Config { | ||
| 20 | pub hse: Option<Hertz>, | ||
| 21 | pub bypass_hse: bool, | ||
| 22 | pub pll48: bool, | ||
| 23 | pub sys_ck: Option<Hertz>, | ||
| 24 | pub hclk: Option<Hertz>, | ||
| 25 | pub pclk1: Option<Hertz>, | ||
| 26 | pub pclk2: Option<Hertz>, | ||
| 27 | } | ||
| 19 | 28 | ||
| 20 | /// System clock mux source | 29 | /// RCC peripheral |
| 21 | #[derive(Clone, Copy)] | 30 | pub struct Rcc<'d> { |
| 22 | pub enum ClockSrc { | 31 | config: Config, |
| 23 | HSE(Hertz), | 32 | phantom: PhantomData<&'d mut peripherals::RCC>, |
| 24 | HSI16, | ||
| 25 | } | 33 | } |
| 26 | 34 | ||
| 27 | impl Into<Ppre> for APBPrescaler { | 35 | impl<'d> Rcc<'d> { |
| 28 | fn into(self) -> Ppre { | 36 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { |
| 29 | match self { | 37 | Self { |
| 30 | APBPrescaler::NotDivided => Ppre::DIV1, | 38 | config, |
| 31 | APBPrescaler::Div2 => Ppre::DIV2, | 39 | phantom: PhantomData, |
| 32 | APBPrescaler::Div4 => Ppre::DIV4, | ||
| 33 | APBPrescaler::Div8 => Ppre::DIV8, | ||
| 34 | APBPrescaler::Div16 => Ppre::DIV16, | ||
| 35 | } | 40 | } |
| 36 | } | 41 | } |
| 37 | } | ||
| 38 | 42 | ||
| 39 | impl Into<Hpre> for AHBPrescaler { | 43 | fn freeze(mut self) -> Clocks { |
| 40 | fn into(self) -> Hpre { | 44 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw}; |
| 41 | match self { | 45 | |
| 42 | AHBPrescaler::NotDivided => Hpre::DIV1, | 46 | let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI); |
| 43 | AHBPrescaler::Div2 => Hpre::DIV2, | 47 | let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); |
| 44 | AHBPrescaler::Div4 => Hpre::DIV4, | 48 | let sysclk_on_pll = sysclk != pllsrcclk; |
| 45 | AHBPrescaler::Div8 => Hpre::DIV8, | 49 | |
| 46 | AHBPrescaler::Div16 => Hpre::DIV16, | 50 | let plls = self.setup_pll( |
| 47 | AHBPrescaler::Div64 => Hpre::DIV64, | 51 | pllsrcclk, |
| 48 | AHBPrescaler::Div128 => Hpre::DIV128, | 52 | self.config.hse.is_some(), |
| 49 | AHBPrescaler::Div256 => Hpre::DIV256, | 53 | if sysclk_on_pll { Some(sysclk) } else { None }, |
| 50 | AHBPrescaler::Div512 => Hpre::DIV512, | 54 | self.config.pll48, |
| 55 | ); | ||
| 56 | |||
| 57 | if self.config.pll48 { | ||
| 58 | assert!( | ||
| 59 | // USB specification allows +-0.25% | ||
| 60 | plls.pll48clk | ||
| 61 | .map(|freq| (48_000_000 - freq as i32).abs() <= 120_000) | ||
| 62 | .unwrap_or(false) | ||
| 63 | ); | ||
| 51 | } | 64 | } |
| 52 | } | ||
| 53 | } | ||
| 54 | 65 | ||
| 55 | /// Clocks configutation | 66 | let sysclk = if sysclk_on_pll { |
| 56 | pub struct Config { | 67 | plls.pllsysclk.unwrap() |
| 57 | mux: ClockSrc, | 68 | } else { |
| 58 | ahb_pre: AHBPrescaler, | 69 | sysclk |
| 59 | apb1_pre: APBPrescaler, | 70 | }; |
| 60 | apb2_pre: APBPrescaler, | 71 | assert!((SYSCLK_MIN..=SYSCLK_MAX).contains(&sysclk)); |
| 61 | } | 72 | |
| 73 | let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 74 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { | ||
| 75 | 0 => unreachable!(), | ||
| 76 | 1 => (Hpre::DIV1, 1), | ||
| 77 | 2 => (Hpre::DIV2, 2), | ||
| 78 | 3..=5 => (Hpre::DIV4, 4), | ||
| 79 | 6..=11 => (Hpre::DIV8, 8), | ||
| 80 | 12..=39 => (Hpre::DIV16, 16), | ||
| 81 | 40..=95 => (Hpre::DIV64, 64), | ||
| 82 | 96..=191 => (Hpre::DIV128, 128), | ||
| 83 | 192..=383 => (Hpre::DIV256, 256), | ||
| 84 | _ => (Hpre::DIV512, 512), | ||
| 85 | }; | ||
| 86 | |||
| 87 | // Calculate real AHB clock | ||
| 88 | let hclk = sysclk / hpre_div; | ||
| 89 | |||
| 90 | let pclk1 = self | ||
| 91 | .config | ||
| 92 | .pclk1 | ||
| 93 | .map(|p| p.0) | ||
| 94 | .unwrap_or_else(|| core::cmp::min(PCLK1_MAX, hclk)); | ||
| 95 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | ||
| 96 | 0 => unreachable!(), | ||
| 97 | 1 => (0b000, 1), | ||
| 98 | 2 => (0b100, 2), | ||
| 99 | 3..=5 => (0b101, 4), | ||
| 100 | 6..=11 => (0b110, 8), | ||
| 101 | _ => (0b111, 16), | ||
| 102 | }; | ||
| 103 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 104 | |||
| 105 | // Calculate real APB1 clock | ||
| 106 | let pclk1 = hclk / ppre1; | ||
| 107 | assert!(pclk1 <= PCLK1_MAX); | ||
| 62 | 108 | ||
| 63 | impl Default for Config { | 109 | let pclk2 = self |
| 64 | #[inline] | 110 | .config |
| 65 | fn default() -> Config { | 111 | .pclk2 |
| 66 | Config { | 112 | .map(|p| p.0) |
| 67 | mux: ClockSrc::HSI16, | 113 | .unwrap_or_else(|| core::cmp::min(PCLK2_MAX, hclk)); |
| 68 | ahb_pre: AHBPrescaler::NotDivided, | 114 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { |
| 69 | apb1_pre: APBPrescaler::NotDivided, | 115 | 0 => unreachable!(), |
| 70 | apb2_pre: APBPrescaler::NotDivided, | 116 | 1 => (0b000, 1), |
| 117 | 2 => (0b100, 2), | ||
| 118 | 3..=5 => (0b101, 4), | ||
| 119 | 6..=11 => (0b110, 8), | ||
| 120 | _ => (0b111, 16), | ||
| 121 | }; | ||
| 122 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 123 | |||
| 124 | // Calculate real APB2 clock | ||
| 125 | let pclk2 = hclk / ppre2; | ||
| 126 | assert!(pclk2 <= PCLK2_MAX); | ||
| 127 | |||
| 128 | Self::flash_setup(sysclk); | ||
| 129 | |||
| 130 | if self.config.hse.is_some() { | ||
| 131 | // NOTE(unsafe) We own the peripheral block | ||
| 132 | unsafe { | ||
| 133 | RCC.cr().modify(|w| { | ||
| 134 | w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8)); | ||
| 135 | w.set_hseon(true); | ||
| 136 | }); | ||
| 137 | while !RCC.cr().read().hserdy() {} | ||
| 138 | } | ||
| 71 | } | 139 | } |
| 72 | } | ||
| 73 | } | ||
| 74 | 140 | ||
| 75 | impl Config { | 141 | if plls.use_pll { |
| 76 | #[inline] | 142 | unsafe { |
| 77 | pub fn clock_src(mut self, mux: ClockSrc) -> Self { | 143 | RCC.cr().modify(|w| w.set_pllon(true)); |
| 78 | self.mux = mux; | 144 | // TODO: PWR setup for HCLK > 168MHz |
| 79 | self | 145 | while !RCC.cr().read().pllrdy() {} |
| 80 | } | 146 | } |
| 147 | } | ||
| 81 | 148 | ||
| 82 | #[inline] | 149 | unsafe { |
| 83 | pub fn ahb_pre(mut self, pre: AHBPrescaler) -> Self { | 150 | RCC.cfgr().modify(|w| { |
| 84 | self.ahb_pre = pre; | 151 | w.set_ppre2(Ppre(ppre2_bits)); |
| 85 | self | 152 | w.set_ppre1(Ppre(ppre1_bits)); |
| 86 | } | 153 | w.set_hpre(hpre_bits); |
| 154 | }); | ||
| 87 | 155 | ||
| 88 | #[inline] | 156 | // Wait for the new prescalers to kick in |
| 89 | pub fn apb1_pre(mut self, pre: APBPrescaler) -> Self { | 157 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" |
| 90 | self.apb1_pre = pre; | 158 | cortex_m::asm::delay(16); |
| 91 | self | ||
| 92 | } | ||
| 93 | 159 | ||
| 94 | #[inline] | 160 | RCC.cfgr().modify(|w| { |
| 95 | pub fn apb2_pre(mut self, pre: APBPrescaler) -> Self { | 161 | w.set_sw(if sysclk_on_pll { |
| 96 | self.apb2_pre = pre; | 162 | Sw::PLL |
| 97 | self | 163 | } else if self.config.hse.is_some() { |
| 98 | } | 164 | Sw::HSE |
| 99 | } | 165 | } else { |
| 166 | Sw::HSI | ||
| 167 | }) | ||
| 168 | }); | ||
| 169 | } | ||
| 100 | 170 | ||
| 101 | /// RCC peripheral | 171 | Clocks { |
| 102 | pub struct Rcc<'d> { | 172 | sys: Hertz(sysclk), |
| 103 | _rb: peripherals::RCC, | 173 | apb1: Hertz(pclk1), |
| 104 | phantom: PhantomData<&'d mut peripherals::RCC>, | 174 | apb2: Hertz(pclk2), |
| 105 | } | ||
| 106 | 175 | ||
| 107 | impl<'d> Rcc<'d> { | 176 | apb1_tim: Hertz(pclk1 * timer_mul1), |
| 108 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | 177 | apb2_tim: Hertz(pclk2 * timer_mul2), |
| 109 | unborrow!(rcc); | 178 | |
| 110 | Self { | 179 | ahb1: Hertz(hclk), |
| 111 | _rb: rcc, | 180 | ahb2: Hertz(hclk), |
| 112 | phantom: PhantomData, | 181 | ahb3: Hertz(hclk), |
| 182 | |||
| 183 | pll48: plls.pll48clk.map(Hertz), | ||
| 113 | } | 184 | } |
| 114 | } | 185 | } |
| 115 | 186 | ||
| @@ -117,91 +188,122 @@ impl<'d> Rcc<'d> { | |||
| 117 | pub fn clocks(&self) -> &'static Clocks { | 188 | pub fn clocks(&self) -> &'static Clocks { |
| 118 | unsafe { get_freqs() } | 189 | unsafe { get_freqs() } |
| 119 | } | 190 | } |
| 120 | } | ||
| 121 | 191 | ||
| 122 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | 192 | fn setup_pll( |
| 123 | pub trait RccExt { | 193 | &mut self, |
| 124 | fn freeze(self, config: Config) -> Clocks; | 194 | pllsrcclk: u32, |
| 125 | } | 195 | use_hse: bool, |
| 196 | pllsysclk: Option<u32>, | ||
| 197 | pll48clk: bool, | ||
| 198 | ) -> PllResults { | ||
| 199 | use crate::pac::rcc::vals::{Pllp, Pllsrc}; | ||
| 126 | 200 | ||
| 127 | impl RccExt for RCC { | 201 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); |
| 128 | #[inline] | 202 | if pllsysclk.is_none() && !pll48clk { |
| 129 | fn freeze(self, cfgr: Config) -> Clocks { | 203 | // NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock |
| 130 | let rcc = pac::RCC; | 204 | unsafe { |
| 131 | let (sys_clk, sw) = match cfgr.mux { | 205 | RCC.pllcfgr() |
| 132 | ClockSrc::HSI16 => { | 206 | .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); |
| 133 | // Enable HSI16 | ||
| 134 | unsafe { | ||
| 135 | rcc.cr().modify(|w| w.set_hsion(true)); | ||
| 136 | while !rcc.cr().read().hsirdy() {} | ||
| 137 | } | ||
| 138 | |||
| 139 | (HSI_FREQ, Sw::HSI) | ||
| 140 | } | 207 | } |
| 141 | ClockSrc::HSE(freq) => { | ||
| 142 | // Enable HSE | ||
| 143 | unsafe { | ||
| 144 | rcc.cr().modify(|w| w.set_hseon(true)); | ||
| 145 | while !rcc.cr().read().hserdy() {} | ||
| 146 | } | ||
| 147 | |||
| 148 | (freq.0, Sw::HSE) | ||
| 149 | } | ||
| 150 | }; | ||
| 151 | 208 | ||
| 152 | unsafe { | 209 | return PllResults { |
| 153 | rcc.cfgr().modify(|w| { | 210 | use_pll: false, |
| 154 | w.set_sw(sw.into()); | 211 | pllsysclk: None, |
| 155 | w.set_hpre(cfgr.ahb_pre.into()); | 212 | pll48clk: None, |
| 156 | w.set_ppre1(cfgr.apb1_pre.into()); | 213 | }; |
| 157 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 158 | }); | ||
| 159 | } | 214 | } |
| 215 | // Input divisor from PLL source clock, must result to frequency in | ||
| 216 | // the range from 1 to 2 MHz | ||
| 217 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 218 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 160 | 219 | ||
| 161 | let ahb_freq: u32 = match cfgr.ahb_pre { | 220 | // Sysclk output divisor must be one of 2, 4, 6 or 8 |
| 162 | AHBPrescaler::NotDivided => sys_clk, | 221 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); |
| 163 | pre => { | ||
| 164 | let pre: Hpre = pre.into(); | ||
| 165 | let pre = 1 << (pre.0 as u32 - 7); | ||
| 166 | sys_clk / pre | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | 222 | ||
| 170 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | 223 | let target_freq = if pll48clk { |
| 171 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 224 | 48_000_000 |
| 172 | pre => { | 225 | } else { |
| 173 | let pre: Ppre = pre.into(); | 226 | sysclk * sysclk_div |
| 174 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 175 | let freq = ahb_freq / pre as u32; | ||
| 176 | (freq, freq * 2) | ||
| 177 | } | ||
| 178 | }; | 227 | }; |
| 179 | 228 | ||
| 180 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | 229 | // Find the lowest pllm value that minimize the difference between |
| 181 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 230 | // target frequency and the real vco_out frequency. |
| 182 | pre => { | 231 | let pllm = (pllm_min..=pllm_max) |
| 183 | let pre: Ppre = pre.into(); | 232 | .min_by_key(|pllm| { |
| 184 | let pre: u8 = 1 << (pre.0 - 3); | 233 | let vco_in = pllsrcclk / pllm; |
| 185 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | 234 | let plln = target_freq / vco_in; |
| 186 | (freq, freq * 2) | 235 | target_freq - vco_in * plln |
| 187 | } | 236 | }) |
| 237 | .unwrap(); | ||
| 238 | |||
| 239 | let vco_in = pllsrcclk / pllm; | ||
| 240 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 241 | |||
| 242 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 243 | // and <= 432MHz, min 50, max 432 | ||
| 244 | let plln = if pll48clk { | ||
| 245 | // try the different valid pllq according to the valid | ||
| 246 | // main scaller values, and take the best | ||
| 247 | let pllq = (4..=9) | ||
| 248 | .min_by_key(|pllq| { | ||
| 249 | let plln = 48_000_000 * pllq / vco_in; | ||
| 250 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 251 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 252 | (pll48_diff, sysclk_diff) | ||
| 253 | }) | ||
| 254 | .unwrap(); | ||
| 255 | 48_000_000 * pllq / vco_in | ||
| 256 | } else { | ||
| 257 | sysclk * sysclk_div / vco_in | ||
| 188 | }; | 258 | }; |
| 259 | assert!((192_000_000..=432_000_000).contains(&(vco_in * plln))); | ||
| 189 | 260 | ||
| 190 | Clocks { | 261 | let pllp = (sysclk_div / 2) - 1; |
| 191 | sys: sys_clk.hz(), | 262 | |
| 192 | ahb1: ahb_freq.hz(), | 263 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; |
| 193 | ahb2: ahb_freq.hz(), | 264 | let real_pll48clk = vco_in * plln / pllq; |
| 194 | ahb3: ahb_freq.hz(), | 265 | |
| 195 | apb1: apb1_freq.hz(), | 266 | unsafe { |
| 196 | apb2: apb2_freq.hz(), | 267 | RCC.pllcfgr().modify(|w| { |
| 197 | apb1_tim: apb1_tim_freq.hz(), | 268 | w.set_pllm(pllm as u8); |
| 198 | apb2_tim: apb2_tim_freq.hz(), | 269 | w.set_plln(plln as u16); |
| 270 | w.set_pllp(Pllp(pllp as u8)); | ||
| 271 | w.set_pllq(pllq as u8); | ||
| 272 | w.set_pllsrc(Pllsrc(use_hse as u8)); | ||
| 273 | }); | ||
| 274 | } | ||
| 275 | |||
| 276 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 277 | |||
| 278 | PllResults { | ||
| 279 | use_pll: true, | ||
| 280 | pllsysclk: Some(real_pllsysclk), | ||
| 281 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 199 | } | 282 | } |
| 200 | } | 283 | } |
| 284 | |||
| 285 | fn flash_setup(sysclk: u32) { | ||
| 286 | use crate::pac::flash::vals::Latency; | ||
| 287 | |||
| 288 | // Be conservative with voltage ranges | ||
| 289 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 290 | |||
| 291 | critical_section::with(|_| unsafe { | ||
| 292 | FLASH | ||
| 293 | .acr() | ||
| 294 | .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 295 | }); | ||
| 296 | } | ||
| 201 | } | 297 | } |
| 202 | 298 | ||
| 203 | pub unsafe fn init(config: Config) { | 299 | pub unsafe fn init(config: Config) { |
| 204 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | 300 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); |
| 205 | let clocks = r.freeze(config); | 301 | let clocks = Rcc::new(r, config).freeze(); |
| 206 | set_freqs(clocks); | 302 | set_freqs(clocks); |
| 207 | } | 303 | } |
| 304 | |||
| 305 | struct PllResults { | ||
| 306 | use_pll: bool, | ||
| 307 | pllsysclk: Option<u32>, | ||
| 308 | pll48clk: Option<u32>, | ||
| 309 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c02db58fe..87be0a5b3 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -31,6 +31,9 @@ pub struct Clocks { | |||
| 31 | 31 | ||
| 32 | #[cfg(any(rcc_h7))] | 32 | #[cfg(any(rcc_h7))] |
| 33 | pub apb4: Hertz, | 33 | pub apb4: Hertz, |
| 34 | |||
| 35 | #[cfg(rcc_f4)] | ||
| 36 | pub pll48: Option<Hertz>, | ||
| 34 | } | 37 | } |
| 35 | 38 | ||
| 36 | /// Frozen clock frequencies | 39 | /// Frozen clock frequencies |
