diff options
| author | VasanthakumarV <[email protected]> | 2021-12-10 11:40:03 +0530 |
|---|---|---|
| committer | VasanthakumarV <[email protected]> | 2021-12-13 14:50:13 +0530 |
| commit | 3f33d307ffc6c122abfa113e6c72a9f7acda40e5 (patch) | |
| tree | 461674a6fa317091b66ee966657687565737ed05 | |
| parent | e2c074d133e3aa156eb0bc4d86bdd301aa9153e6 (diff) | |
[feature] Add rcc register support for F3
| -rw-r--r-- | embassy-stm32/src/rcc/f3/mod.rs | 374 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 5 |
2 files changed, 378 insertions, 1 deletions
diff --git a/embassy-stm32/src/rcc/f3/mod.rs b/embassy-stm32/src/rcc/f3/mod.rs new file mode 100644 index 000000000..ab1bd7607 --- /dev/null +++ b/embassy-stm32/src/rcc/f3/mod.rs | |||
| @@ -0,0 +1,374 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use embassy::util::Unborrow; | ||
| 3 | |||
| 4 | use crate::pac::{ | ||
| 5 | flash::vals::Latency, | ||
| 6 | rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}, | ||
| 7 | FLASH, RCC, | ||
| 8 | }; | ||
| 9 | use crate::peripherals; | ||
| 10 | use crate::rcc::{set_freqs, Clocks}; | ||
| 11 | use crate::time::Hertz; | ||
| 12 | |||
| 13 | const HSI: u32 = 8_000_000; | ||
| 14 | |||
| 15 | /// RCC peripheral | ||
| 16 | pub struct Rcc<'d> { | ||
| 17 | config: Config, | ||
| 18 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 19 | } | ||
| 20 | |||
| 21 | /// Clocks configutation | ||
| 22 | #[non_exhaustive] | ||
| 23 | #[derive(Default)] | ||
| 24 | pub struct Config { | ||
| 25 | /// Frequency of HSE oscillator | ||
| 26 | /// 4MHz to 32MHz | ||
| 27 | pub hse: Option<Hertz>, | ||
| 28 | /// Bypass HSE for an external clock | ||
| 29 | pub bypass_hse: bool, | ||
| 30 | /// Frequency of the System Clock | ||
| 31 | pub sysclk: Option<Hertz>, | ||
| 32 | /// Frequency of AHB bus | ||
| 33 | pub hclk: Option<Hertz>, | ||
| 34 | /// Frequency of APB1 bus | ||
| 35 | /// - Max frequency 36MHz | ||
| 36 | pub pclk1: Option<Hertz>, | ||
| 37 | /// Frequency of APB2 bus | ||
| 38 | /// - Max frequency with HSE is 72MHz | ||
| 39 | /// - Max frequency without HSE is 64MHz | ||
| 40 | pub pclk2: Option<Hertz>, | ||
| 41 | /// USB clock setup | ||
| 42 | /// It is valid only when, | ||
| 43 | /// - HSE is enabled, | ||
| 44 | /// - The System clock frequency is either 48MHz or 72MHz | ||
| 45 | /// - APB1 clock has a minimum frequency of 10MHz | ||
| 46 | pub pll48: bool, | ||
| 47 | } | ||
| 48 | |||
| 49 | // Information required to setup the PLL clock | ||
| 50 | struct PllConfig { | ||
| 51 | pll_src: Pllsrc, | ||
| 52 | pll_mul: Pllmul, | ||
| 53 | pll_div: Option<Prediv>, | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Initialize and Set the clock frequencies | ||
| 57 | pub unsafe fn init(config: Config) { | ||
| 58 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 59 | let clocks = Rcc::new(r, config).freeze(); | ||
| 60 | set_freqs(clocks); | ||
| 61 | } | ||
| 62 | |||
| 63 | impl<'d> Rcc<'d> { | ||
| 64 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 65 | Self { | ||
| 66 | config, | ||
| 67 | phantom: PhantomData, | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | fn freeze(self) -> Clocks { | ||
| 72 | // Calculate the real System clock, and PLL configuration if applicable | ||
| 73 | let (Hertz(sysclk), pll_config) = self.get_sysclk(); | ||
| 74 | assert!(sysclk <= 72_000_000); | ||
| 75 | |||
| 76 | // Calculate real AHB clock | ||
| 77 | let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 78 | let (hpre_bits, hpre_div) = match sysclk / hclk { | ||
| 79 | 0 => unreachable!(), | ||
| 80 | 1 => (Hpre::DIV1, 1), | ||
| 81 | 2 => (Hpre::DIV2, 2), | ||
| 82 | 3..=5 => (Hpre::DIV4, 4), | ||
| 83 | 6..=11 => (Hpre::DIV8, 8), | ||
| 84 | 12..=39 => (Hpre::DIV16, 16), | ||
| 85 | 40..=95 => (Hpre::DIV64, 64), | ||
| 86 | 96..=191 => (Hpre::DIV128, 128), | ||
| 87 | 192..=383 => (Hpre::DIV256, 256), | ||
| 88 | _ => (Hpre::DIV512, 512), | ||
| 89 | }; | ||
| 90 | let hclk = sysclk / hpre_div; | ||
| 91 | assert!(hclk <= 72_000_000); | ||
| 92 | |||
| 93 | // Calculate real APB1 clock | ||
| 94 | let pclk1 = self.config.pclk1.map(|p| p.0).unwrap_or(hclk); | ||
| 95 | let (ppre1_bits, ppre1) = match hclk / pclk1 { | ||
| 96 | 0 => unreachable!(), | ||
| 97 | 1 => (Ppre::DIV1, 1), | ||
| 98 | 2 => (Ppre::DIV2, 2), | ||
| 99 | 3..=5 => (Ppre::DIV4, 4), | ||
| 100 | 6..=11 => (Ppre::DIV8, 8), | ||
| 101 | _ => (Ppre::DIV16, 16), | ||
| 102 | }; | ||
| 103 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 104 | let pclk1 = hclk / ppre1; | ||
| 105 | assert!(pclk1 <= 36_000_000); | ||
| 106 | |||
| 107 | // Calculate real APB2 clock | ||
| 108 | let pclk2 = self.config.pclk2.map(|p| p.0).unwrap_or(hclk); | ||
| 109 | let (ppre2_bits, ppre2) = match hclk / pclk2 { | ||
| 110 | 0 => unreachable!(), | ||
| 111 | 1 => (Ppre::DIV1, 1), | ||
| 112 | 2 => (Ppre::DIV2, 2), | ||
| 113 | 3..=5 => (Ppre::DIV4, 4), | ||
| 114 | 6..=11 => (Ppre::DIV8, 8), | ||
| 115 | _ => (Ppre::DIV16, 16), | ||
| 116 | }; | ||
| 117 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 118 | let pclk2 = hclk / ppre2; | ||
| 119 | assert!(pclk2 <= 72_000_000); | ||
| 120 | |||
| 121 | // Set latency based on HCLK frquency | ||
| 122 | // NOTE(safety) Atomic write | ||
| 123 | unsafe { | ||
| 124 | FLASH.acr().write(|w| { | ||
| 125 | w.set_latency(if hclk <= 24_000_000 { | ||
| 126 | Latency::WS0 | ||
| 127 | } else if hclk <= 48_000_000 { | ||
| 128 | Latency::WS1 | ||
| 129 | } else { | ||
| 130 | Latency::WS2 | ||
| 131 | }); | ||
| 132 | }) | ||
| 133 | } | ||
| 134 | |||
| 135 | // Enable HSE | ||
| 136 | if self.config.hse.is_some() { | ||
| 137 | // NOTE(unsafe) We own the peripheral block | ||
| 138 | unsafe { | ||
| 139 | RCC.cr().write(|w| { | ||
| 140 | w.set_hsebyp(if self.config.bypass_hse { | ||
| 141 | Hsebyp::BYPASSED | ||
| 142 | } else { | ||
| 143 | Hsebyp::NOTBYPASSED | ||
| 144 | }); | ||
| 145 | // We turn on clock security to switch to HSI when HSE fails | ||
| 146 | w.set_csson(true); | ||
| 147 | w.set_hseon(true); | ||
| 148 | }); | ||
| 149 | while !RCC.cr().read().hserdy() {} | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | // Enable PLL | ||
| 154 | if let Some(ref pll_config) = pll_config { | ||
| 155 | // NOTE(unsafe) We own the peripheral block | ||
| 156 | unsafe { | ||
| 157 | RCC.cfgr().write(|w| { | ||
| 158 | w.set_pllmul(pll_config.pll_mul); | ||
| 159 | w.set_pllsrc(pll_config.pll_src); | ||
| 160 | }); | ||
| 161 | if let Some(pll_div) = pll_config.pll_div { | ||
| 162 | RCC.cfgr2().write(|w| w.set_prediv(pll_div)); | ||
| 163 | } | ||
| 164 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 165 | while !RCC.cr().read().pllrdy() {} | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | if self.config.pll48 { | ||
| 170 | let usb_pre = self.get_usb_pre(sysclk, pclk1, &pll_config); | ||
| 171 | // NOTE(unsafe) We own the peripheral block | ||
| 172 | unsafe { | ||
| 173 | RCC.cfgr().write(|w| { | ||
| 174 | w.set_usbpre(usb_pre); | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | // Set prescalers | ||
| 180 | unsafe { | ||
| 181 | // NOTE(unsafe) We own the peripheral block | ||
| 182 | RCC.cfgr().write(|w| { | ||
| 183 | w.set_ppre2(ppre2_bits); | ||
| 184 | w.set_ppre1(ppre1_bits); | ||
| 185 | w.set_hpre(hpre_bits); | ||
| 186 | }); | ||
| 187 | |||
| 188 | // Wait for the new prescalers to kick in | ||
| 189 | // "The clocks are divided with the new prescaler factor from | ||
| 190 | // 1 to 16 AHB cycles after write" | ||
| 191 | cortex_m::asm::delay(16); | ||
| 192 | |||
| 193 | // NOTE(unsafe) We own the peripheral block | ||
| 194 | RCC.cfgr().write(|w| { | ||
| 195 | w.set_sw(match (pll_config, self.config.hse) { | ||
| 196 | (Some(_), _) => Sw::PLL, | ||
| 197 | (None, Some(_)) => Sw::HSE, | ||
| 198 | (None, None) => Sw::HSI, | ||
| 199 | }) | ||
| 200 | }); | ||
| 201 | } | ||
| 202 | |||
| 203 | Clocks { | ||
| 204 | sys: Hertz(sysclk), | ||
| 205 | apb1: Hertz(pclk1), | ||
| 206 | apb2: Hertz(pclk2), | ||
| 207 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 208 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 209 | ahb: Hertz(hclk), | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | #[inline] | ||
| 214 | fn get_sysclk(&self) -> (Hertz, Option<PllConfig>) { | ||
| 215 | match (self.config.sysclk, self.config.hse) { | ||
| 216 | (Some(sysclk), Some(hse)) if sysclk == hse => (hse, None), | ||
| 217 | (Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None), | ||
| 218 | // If the user selected System clock is different from HSI or HSE | ||
| 219 | // we will have to setup PLL clock source | ||
| 220 | (Some(sysclk), _) => { | ||
| 221 | let (sysclk, pll_config) = self.calc_pll(sysclk); | ||
| 222 | (sysclk, Some(pll_config)) | ||
| 223 | } | ||
| 224 | (None, Some(hse)) => (hse, None), | ||
| 225 | (None, None) => (Hertz(HSI), None), | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | #[inline] | ||
| 230 | fn calc_pll(&self, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { | ||
| 231 | // Calculates the Multiplier and the Divisor to arrive at | ||
| 232 | // the required System clock from PLL source frequency | ||
| 233 | let get_mul_div = |sysclk, pllsrcclk| { | ||
| 234 | let common_div = gcd(sysclk, pllsrcclk); | ||
| 235 | let mut multiplier = sysclk / common_div; | ||
| 236 | let mut divisor = pllsrcclk / common_div; | ||
| 237 | // Minimum PLL multiplier is two | ||
| 238 | if multiplier == 1 { | ||
| 239 | multiplier *= 2; | ||
| 240 | divisor *= 2; | ||
| 241 | } | ||
| 242 | assert!(multiplier <= 16); | ||
| 243 | assert!(divisor <= 16); | ||
| 244 | (multiplier, divisor) | ||
| 245 | }; | ||
| 246 | // Based on the source of Pll, we calculate the actual system clock | ||
| 247 | // frequency, PLL's source identifier, multiplier and divisor | ||
| 248 | let (act_sysclk, pll_src, pll_mul, pll_div) = match self.config.hse { | ||
| 249 | Some(Hertz(hse)) => { | ||
| 250 | let (multiplier, divisor) = get_mul_div(sysclk, hse); | ||
| 251 | ( | ||
| 252 | Hertz((hse / divisor) * multiplier), | ||
| 253 | Pllsrc::HSE_DIV_PREDIV, | ||
| 254 | into_pll_mul(multiplier), | ||
| 255 | Some(into_pre_div(divisor)), | ||
| 256 | ) | ||
| 257 | } | ||
| 258 | None => { | ||
| 259 | cfg_if::cfg_if! { | ||
| 260 | // For some chips PREDIV is always two, and cannot be changed | ||
| 261 | if #[cfg(any( | ||
| 262 | feature="stm32f302xd", feature="stm32f302xe", feature="stm32f303xd", | ||
| 263 | feature="stm32f303xe", feature="stm32f398xe" | ||
| 264 | ))] { | ||
| 265 | let (multiplier, divisor) = get_mul_div(sysclk, HSI); | ||
| 266 | ( | ||
| 267 | Hertz((hse / divisor) * multiplier), | ||
| 268 | Pllsrc::HSI_DIV_PREDIV, | ||
| 269 | into_pll_mul(multiplier), | ||
| 270 | Some(into_pre_div(divisor)), | ||
| 271 | ) | ||
| 272 | } else { | ||
| 273 | let pllsrcclk = HSI / 2; | ||
| 274 | let multiplier = sysclk / pllsrcclk; | ||
| 275 | assert!(multiplier <= 16); | ||
| 276 | ( | ||
| 277 | Hertz(pllsrcclk * multiplier), | ||
| 278 | Pllsrc::HSI_DIV2, | ||
| 279 | into_pll_mul(multiplier), | ||
| 280 | None, | ||
| 281 | ) | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
| 285 | }; | ||
| 286 | ( | ||
| 287 | act_sysclk, | ||
| 288 | PllConfig { | ||
| 289 | pll_src, | ||
| 290 | pll_mul, | ||
| 291 | pll_div, | ||
| 292 | }, | ||
| 293 | ) | ||
| 294 | } | ||
| 295 | |||
| 296 | #[inline] | ||
| 297 | fn get_usb_pre(&self, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre { | ||
| 298 | cfg_if::cfg_if! { | ||
| 299 | // Some chips do not have USB | ||
| 300 | if #[cfg(any(stm32f301, stm32f318, stm32f334))] { | ||
| 301 | panic!("USB clock not supported by the chip"); | ||
| 302 | } else { | ||
| 303 | let usb_ok = self.config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000); | ||
| 304 | match (usb_ok, sysclk) { | ||
| 305 | (true, 72_000_000) => Usbpre::DIV1_5, | ||
| 306 | (true, 48_000_000) => Usbpre::DIV1, | ||
| 307 | _ => panic!( | ||
| 308 | "USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz" | ||
| 309 | ), | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | // This function assumes cases when multiplier is one and it | ||
| 317 | // being greater than 16 is made impossible | ||
| 318 | #[inline] | ||
| 319 | fn into_pll_mul(multiplier: u32) -> Pllmul { | ||
| 320 | match multiplier { | ||
| 321 | 2 => Pllmul::MUL2, | ||
| 322 | 3 => Pllmul::MUL3, | ||
| 323 | 4 => Pllmul::MUL4, | ||
| 324 | 5 => Pllmul::MUL5, | ||
| 325 | 6 => Pllmul::MUL6, | ||
| 326 | 7 => Pllmul::MUL7, | ||
| 327 | 8 => Pllmul::MUL8, | ||
| 328 | 9 => Pllmul::MUL9, | ||
| 329 | 10 => Pllmul::MUL10, | ||
| 330 | 11 => Pllmul::MUL11, | ||
| 331 | 12 => Pllmul::MUL12, | ||
| 332 | 13 => Pllmul::MUL13, | ||
| 333 | 14 => Pllmul::MUL14, | ||
| 334 | 15 => Pllmul::MUL15, | ||
| 335 | 16 => Pllmul::MUL16, | ||
| 336 | _ => unreachable!(), | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | // This function assumes the incoming divisor cannot be greater | ||
| 341 | // than 16 | ||
| 342 | #[inline] | ||
| 343 | fn into_pre_div(divisor: u32) -> Prediv { | ||
| 344 | match divisor { | ||
| 345 | 1 => Prediv::DIV1, | ||
| 346 | 2 => Prediv::DIV2, | ||
| 347 | 3 => Prediv::DIV3, | ||
| 348 | 4 => Prediv::DIV4, | ||
| 349 | 5 => Prediv::DIV5, | ||
| 350 | 6 => Prediv::DIV6, | ||
| 351 | 7 => Prediv::DIV7, | ||
| 352 | 8 => Prediv::DIV8, | ||
| 353 | 9 => Prediv::DIV9, | ||
| 354 | 10 => Prediv::DIV10, | ||
| 355 | 11 => Prediv::DIV11, | ||
| 356 | 12 => Prediv::DIV12, | ||
| 357 | 13 => Prediv::DIV13, | ||
| 358 | 14 => Prediv::DIV14, | ||
| 359 | 15 => Prediv::DIV15, | ||
| 360 | 16 => Prediv::DIV16, | ||
| 361 | _ => unreachable!(), | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | // Determine GCD using Euclidean algorithm | ||
| 366 | #[inline] | ||
| 367 | fn gcd(mut a: u32, mut b: u32) -> u32 { | ||
| 368 | while b != 0 { | ||
| 369 | let r = a % b; | ||
| 370 | a = b; | ||
| 371 | b = r; | ||
| 372 | } | ||
| 373 | a | ||
| 374 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 9883543df..619316df8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -26,7 +26,7 @@ pub struct Clocks { | |||
| 26 | #[cfg(any(rcc_wl5, rcc_u5))] | 26 | #[cfg(any(rcc_wl5, rcc_u5))] |
| 27 | pub apb3: Hertz, | 27 | pub apb3: Hertz, |
| 28 | 28 | ||
| 29 | #[cfg(any(rcc_l0, rcc_l1, rcc_f0, rcc_f1, rcc_f0x0, rcc_g0))] | 29 | #[cfg(any(rcc_l0, rcc_l1, rcc_f0, rcc_f1, rcc_f3, rcc_f0x0, rcc_g0))] |
| 30 | pub ahb: Hertz, | 30 | pub ahb: Hertz, |
| 31 | 31 | ||
| 32 | #[cfg(any(rcc_l4, rcc_f4, rcc_f7, rcc_h7, rcc_g4, rcc_u5, rcc_wb, rcc_wl5))] | 32 | #[cfg(any(rcc_l4, rcc_f4, rcc_f7, rcc_h7, rcc_g4, rcc_u5, rcc_wb, rcc_wl5))] |
| @@ -81,6 +81,9 @@ cfg_if::cfg_if! { | |||
| 81 | } else if #[cfg(rcc_f1)] { | 81 | } else if #[cfg(rcc_f1)] { |
| 82 | mod f1; | 82 | mod f1; |
| 83 | pub use f1::*; | 83 | pub use f1::*; |
| 84 | } else if #[cfg(rcc_f3)] { | ||
| 85 | mod f3; | ||
| 86 | pub use f3::*; | ||
| 84 | } else if #[cfg(rcc_f4)] { | 87 | } else if #[cfg(rcc_f4)] { |
| 85 | mod f4; | 88 | mod f4; |
| 86 | pub use f4::*; | 89 | pub use f4::*; |
