diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-10-18 03:16:36 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-10-18 05:01:11 +0200 |
| commit | 67010d123c874383f48ccd5c1b2287907677e460 (patch) | |
| tree | 806c4259519c3883e697c1327cf1231b92ea1005 | |
| parent | 361fde35cf37e5c8171ab470449e85ad44da4e52 (diff) | |
stm32/rcc: refactor f7.
| -rw-r--r-- | embassy-stm32/src/rcc/f7.rs | 503 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/eth.rs | 22 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/hello.rs | 4 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/sdmmc.rs | 23 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/usb_serial.rs | 25 | ||||
| -rw-r--r-- | tests/stm32/src/common.rs | 18 |
6 files changed, 333 insertions, 262 deletions
diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs index 7c6c150d9..a984e4f4b 100644 --- a/embassy-stm32/src/rcc/f7.rs +++ b/embassy-stm32/src/rcc/f7.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | use crate::pac::pwr::vals::Vos; | 1 | pub use crate::pac::rcc::vals::{ |
| 2 | use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllp, Pllq, Pllsrc, Ppre, Sw}; | 2 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource, |
| 3 | Ppre as APBPrescaler, Sw as Sysclk, | ||
| 4 | }; | ||
| 3 | use crate::pac::{FLASH, PWR, RCC}; | 5 | use crate::pac::{FLASH, PWR, RCC}; |
| 4 | use crate::rcc::{set_freqs, Clocks}; | 6 | use crate::rcc::{set_freqs, Clocks}; |
| 5 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| @@ -7,299 +9,304 @@ use crate::time::Hertz; | |||
| 7 | /// HSI speed | 9 | /// HSI speed |
| 8 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 10 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 9 | 11 | ||
| 10 | /// Clocks configuration | 12 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 11 | #[non_exhaustive] | 13 | pub enum HseMode { |
| 12 | #[derive(Default)] | 14 | /// crystal/ceramic oscillator (HSEBYP=0) |
| 13 | pub struct Config { | 15 | Oscillator, |
| 14 | pub hse: Option<Hertz>, | 16 | /// external analog clock (low swing) (HSEBYP=1) |
| 15 | pub bypass_hse: bool, | 17 | Bypass, |
| 16 | pub hclk: Option<Hertz>, | ||
| 17 | pub sys_ck: Option<Hertz>, | ||
| 18 | pub pclk1: Option<Hertz>, | ||
| 19 | pub pclk2: Option<Hertz>, | ||
| 20 | |||
| 21 | pub pll48: bool, | ||
| 22 | pub ls: super::LsConfig, | ||
| 23 | } | 18 | } |
| 24 | 19 | ||
| 25 | fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults { | 20 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 26 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | 21 | pub struct Hse { |
| 27 | if pllsysclk.is_none() && !pll48clk { | 22 | /// HSE frequency. |
| 28 | RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8))); | 23 | pub freq: Hertz, |
| 29 | 24 | /// HSE mode. | |
| 30 | return PllResults { | 25 | pub mode: HseMode, |
| 31 | use_pll: false, | 26 | } |
| 32 | pllsysclk: None, | ||
| 33 | pll48clk: None, | ||
| 34 | }; | ||
| 35 | } | ||
| 36 | // Input divisor from PLL source clock, must result to frequency in | ||
| 37 | // the range from 1 to 2 MHz | ||
| 38 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 39 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 40 | |||
| 41 | // Sysclk output divisor must be one of 2, 4, 6 or 8 | ||
| 42 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); | ||
| 43 | |||
| 44 | let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div }; | ||
| 45 | |||
| 46 | // Find the lowest pllm value that minimize the difference between | ||
| 47 | // target frequency and the real vco_out frequency. | ||
| 48 | let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { | ||
| 49 | let vco_in = pllsrcclk / pllm; | ||
| 50 | let plln = target_freq / vco_in; | ||
| 51 | target_freq - vco_in * plln | ||
| 52 | })); | ||
| 53 | |||
| 54 | let vco_in = pllsrcclk / pllm; | ||
| 55 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 56 | |||
| 57 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 58 | // and <= 432MHz, min 50, max 432 | ||
| 59 | let plln = if pll48clk { | ||
| 60 | // try the different valid pllq according to the valid | ||
| 61 | // main scaller values, and take the best | ||
| 62 | let pllq = unwrap!((4..=9).min_by_key(|pllq| { | ||
| 63 | let plln = 48_000_000 * pllq / vco_in; | ||
| 64 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 65 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 66 | (pll48_diff, sysclk_diff) | ||
| 67 | })); | ||
| 68 | 48_000_000 * pllq / vco_in | ||
| 69 | } else { | ||
| 70 | sysclk * sysclk_div / vco_in | ||
| 71 | }; | ||
| 72 | 27 | ||
| 73 | let pllp = (sysclk_div / 2) - 1; | 28 | #[derive(Clone, Copy)] |
| 29 | pub struct Pll { | ||
| 30 | /// PLL pre-divider (DIVM). | ||
| 31 | pub prediv: PllPreDiv, | ||
| 74 | 32 | ||
| 75 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; | 33 | /// PLL multiplication factor. |
| 76 | let real_pll48clk = vco_in * plln / pllq; | 34 | pub mul: PllMul, |
| 77 | 35 | ||
| 78 | RCC.pllcfgr().modify(|w| { | 36 | /// PLL P division factor. If None, PLL P output is disabled. |
| 79 | w.set_pllm(Pllm::from_bits(pllm as u8)); | 37 | pub divp: Option<Pllp>, |
| 80 | w.set_plln(Plln::from_bits(plln as u16)); | 38 | /// PLL Q division factor. If None, PLL Q output is disabled. |
| 81 | w.set_pllp(Pllp::from_bits(pllp as u8)); | 39 | pub divq: Option<Pllq>, |
| 82 | w.set_pllq(Pllq::from_bits(pllq as u8)); | 40 | /// PLL R division factor. If None, PLL R output is disabled. |
| 83 | w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); | 41 | pub divr: Option<Pllr>, |
| 84 | }); | 42 | } |
| 85 | 43 | ||
| 86 | let real_pllsysclk = vco_in * plln / sysclk_div; | 44 | /// Configuration of the core clocks |
| 45 | #[non_exhaustive] | ||
| 46 | pub struct Config { | ||
| 47 | pub hsi: bool, | ||
| 48 | pub hse: Option<Hse>, | ||
| 49 | pub sys: Sysclk, | ||
| 87 | 50 | ||
| 88 | PllResults { | 51 | pub pll_src: PllSource, |
| 89 | use_pll: true, | ||
| 90 | pllsysclk: Some(real_pllsysclk), | ||
| 91 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 92 | } | ||
| 93 | } | ||
| 94 | 52 | ||
| 95 | fn flash_setup(sysclk: u32) { | 53 | pub pll: Option<Pll>, |
| 96 | use crate::pac::flash::vals::Latency; | 54 | pub plli2s: Option<Pll>, |
| 55 | pub pllsai: Option<Pll>, | ||
| 97 | 56 | ||
| 98 | // Be conservative with voltage ranges | 57 | pub ahb_pre: AHBPrescaler, |
| 99 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | 58 | pub apb1_pre: APBPrescaler, |
| 59 | pub apb2_pre: APBPrescaler, | ||
| 100 | 60 | ||
| 101 | critical_section::with(|_| { | 61 | pub ls: super::LsConfig, |
| 102 | FLASH | ||
| 103 | .acr() | ||
| 104 | .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 105 | }); | ||
| 106 | } | 62 | } |
| 107 | 63 | ||
| 108 | pub(crate) unsafe fn init(config: Config) { | 64 | impl Default for Config { |
| 109 | if let Some(hse) = config.hse { | 65 | fn default() -> Self { |
| 110 | if config.bypass_hse { | 66 | Self { |
| 111 | assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0)); | 67 | hsi: true, |
| 112 | } else { | 68 | hse: None, |
| 113 | assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0)); | 69 | sys: Sysclk::HSI, |
| 70 | pll_src: PllSource::HSI, | ||
| 71 | pll: None, | ||
| 72 | plli2s: None, | ||
| 73 | pllsai: None, | ||
| 74 | |||
| 75 | ahb_pre: AHBPrescaler::DIV1, | ||
| 76 | apb1_pre: APBPrescaler::DIV1, | ||
| 77 | apb2_pre: APBPrescaler::DIV1, | ||
| 78 | |||
| 79 | ls: Default::default(), | ||
| 114 | } | 80 | } |
| 115 | } | 81 | } |
| 82 | } | ||
| 116 | 83 | ||
| 117 | let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0); | 84 | pub(crate) unsafe fn init(config: Config) { |
| 118 | let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | 85 | // always enable overdrive for now. Make it configurable in the future. |
| 119 | let sysclk_on_pll = sysclk != pllsrcclk; | 86 | PWR.cr1().modify(|w| w.set_oden(true)); |
| 87 | while !PWR.csr1().read().odrdy() {} | ||
| 88 | |||
| 89 | PWR.cr1().modify(|w| w.set_odswen(true)); | ||
| 90 | while !PWR.csr1().read().odswrdy() {} | ||
| 91 | |||
| 92 | // Configure HSI | ||
| 93 | let hsi = match config.hsi { | ||
| 94 | false => { | ||
| 95 | RCC.cr().modify(|w| w.set_hsion(false)); | ||
| 96 | None | ||
| 97 | } | ||
| 98 | true => { | ||
| 99 | RCC.cr().modify(|w| w.set_hsion(true)); | ||
| 100 | while !RCC.cr().read().hsirdy() {} | ||
| 101 | Some(HSI_FREQ) | ||
| 102 | } | ||
| 103 | }; | ||
| 120 | 104 | ||
| 121 | assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk)); | 105 | // Configure HSE |
| 106 | let hse = match config.hse { | ||
| 107 | None => { | ||
| 108 | RCC.cr().modify(|w| w.set_hseon(false)); | ||
| 109 | None | ||
| 110 | } | ||
| 111 | Some(hse) => { | ||
| 112 | match hse.mode { | ||
| 113 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | ||
| 114 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | ||
| 115 | } | ||
| 116 | |||
| 117 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | ||
| 118 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 119 | while !RCC.cr().read().hserdy() {} | ||
| 120 | Some(hse.freq) | ||
| 121 | } | ||
| 122 | }; | ||
| 122 | 123 | ||
| 123 | let plls = setup_pll( | 124 | // Configure PLLs. |
| 124 | pllsrcclk, | 125 | let pll_input = PllInput { |
| 125 | config.hse.is_some(), | 126 | hse, |
| 126 | if sysclk_on_pll { Some(sysclk) } else { None }, | 127 | hsi, |
| 127 | config.pll48, | 128 | source: config.pll_src, |
| 128 | ); | 129 | }; |
| 130 | let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); | ||
| 131 | let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input); | ||
| 132 | let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input); | ||
| 133 | |||
| 134 | // Configure sysclk | ||
| 135 | let sys = match config.sys { | ||
| 136 | Sysclk::HSI => unwrap!(hsi), | ||
| 137 | Sysclk::HSE => unwrap!(hse), | ||
| 138 | Sysclk::PLL1_P => unwrap!(pll.p), | ||
| 139 | _ => unreachable!(), | ||
| 140 | }; | ||
| 129 | 141 | ||
| 130 | if config.pll48 { | 142 | let hclk = sys / config.ahb_pre; |
| 131 | let freq = unwrap!(plls.pll48clk); | 143 | let (pclk1, pclk1_tim) = calc_pclk(hclk, config.apb1_pre); |
| 144 | let (pclk2, pclk2_tim) = calc_pclk(hclk, config.apb2_pre); | ||
| 132 | 145 | ||
| 133 | assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32); | 146 | assert!(max::SYSCLK.contains(&sys)); |
| 134 | } | 147 | assert!(max::HCLK.contains(&hclk)); |
| 148 | assert!(max::PCLK1.contains(&pclk1)); | ||
| 149 | assert!(max::PCLK2.contains(&pclk2)); | ||
| 135 | 150 | ||
| 136 | let sysclk = if sysclk_on_pll { unwrap!(plls.pllsysclk) } else { sysclk }; | 151 | let rtc = config.ls.init(); |
| 137 | |||
| 138 | // AHB prescaler | ||
| 139 | let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 140 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { | ||
| 141 | 0 => unreachable!(), | ||
| 142 | 1 => (Hpre::DIV1, 1), | ||
| 143 | 2 => (Hpre::DIV2, 2), | ||
| 144 | 3..=5 => (Hpre::DIV4, 4), | ||
| 145 | 6..=11 => (Hpre::DIV8, 8), | ||
| 146 | 12..=39 => (Hpre::DIV16, 16), | ||
| 147 | 40..=95 => (Hpre::DIV64, 64), | ||
| 148 | 96..=191 => (Hpre::DIV128, 128), | ||
| 149 | 192..=383 => (Hpre::DIV256, 256), | ||
| 150 | _ => (Hpre::DIV512, 512), | ||
| 151 | }; | ||
| 152 | 152 | ||
| 153 | // Calculate real AHB clock | 153 | flash_setup(hclk); |
| 154 | let hclk = sysclk / hpre_div; | ||
| 155 | 154 | ||
| 156 | assert!(hclk <= max::HCLK_MAX); | 155 | RCC.cfgr().modify(|w| { |
| 156 | w.set_sw(config.sys); | ||
| 157 | w.set_hpre(config.ahb_pre); | ||
| 158 | w.set_ppre1(config.apb1_pre); | ||
| 159 | w.set_ppre2(config.apb2_pre); | ||
| 160 | }); | ||
| 161 | while RCC.cfgr().read().sws() != config.sys {} | ||
| 157 | 162 | ||
| 158 | let pclk1 = config | 163 | set_freqs(Clocks { |
| 159 | .pclk1 | 164 | sys, |
| 160 | .map(|p| p.0) | 165 | hclk1: hclk, |
| 161 | .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); | 166 | hclk2: hclk, |
| 167 | hclk3: hclk, | ||
| 168 | pclk1, | ||
| 169 | pclk2, | ||
| 170 | pclk1_tim, | ||
| 171 | pclk2_tim, | ||
| 172 | rtc, | ||
| 173 | pll1_q: pll.q, | ||
| 174 | }); | ||
| 175 | } | ||
| 162 | 176 | ||
| 163 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | 177 | struct PllInput { |
| 164 | 0 => unreachable!(), | 178 | source: PllSource, |
| 165 | 1 => (0b000, 1), | 179 | hsi: Option<Hertz>, |
| 166 | 2 => (0b100, 2), | 180 | hse: Option<Hertz>, |
| 167 | 3..=5 => (0b101, 4), | 181 | } |
| 168 | 6..=11 => (0b110, 8), | ||
| 169 | _ => (0b111, 16), | ||
| 170 | }; | ||
| 171 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 172 | |||
| 173 | // Calculate real APB1 clock | ||
| 174 | let pclk1 = hclk / ppre1; | ||
| 175 | assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1)); | ||
| 176 | |||
| 177 | let pclk2 = config | ||
| 178 | .pclk2 | ||
| 179 | .map(|p| p.0) | ||
| 180 | .unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk)); | ||
| 181 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { | ||
| 182 | 0 => unreachable!(), | ||
| 183 | 1 => (0b000, 1), | ||
| 184 | 2 => (0b100, 2), | ||
| 185 | 3..=5 => (0b101, 4), | ||
| 186 | 6..=11 => (0b110, 8), | ||
| 187 | _ => (0b111, 16), | ||
| 188 | }; | ||
| 189 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 190 | 182 | ||
| 191 | // Calculate real APB2 clock | 183 | #[derive(Default)] |
| 192 | let pclk2 = hclk / ppre2; | 184 | #[allow(unused)] |
| 193 | assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2)); | 185 | struct PllOutput { |
| 186 | p: Option<Hertz>, | ||
| 187 | q: Option<Hertz>, | ||
| 188 | r: Option<Hertz>, | ||
| 189 | } | ||
| 194 | 190 | ||
| 195 | flash_setup(sysclk); | 191 | #[derive(PartialEq, Eq, Clone, Copy)] |
| 192 | enum PllInstance { | ||
| 193 | Pll, | ||
| 194 | Plli2s, | ||
| 195 | Pllsai, | ||
| 196 | } | ||
| 196 | 197 | ||
| 197 | if config.hse.is_some() { | 198 | fn pll_enable(instance: PllInstance, enabled: bool) { |
| 198 | RCC.cr().modify(|w| { | 199 | match instance { |
| 199 | w.set_hsebyp(config.bypass_hse); | 200 | PllInstance::Pll => { |
| 200 | w.set_hseon(true); | 201 | RCC.cr().modify(|w| w.set_pllon(enabled)); |
| 201 | }); | 202 | while RCC.cr().read().pllrdy() != enabled {} |
| 202 | while !RCC.cr().read().hserdy() {} | 203 | } |
| 204 | PllInstance::Plli2s => { | ||
| 205 | RCC.cr().modify(|w| w.set_plli2son(enabled)); | ||
| 206 | while RCC.cr().read().plli2srdy() != enabled {} | ||
| 207 | } | ||
| 208 | PllInstance::Pllsai => { | ||
| 209 | RCC.cr().modify(|w| w.set_pllsaion(enabled)); | ||
| 210 | while RCC.cr().read().pllsairdy() != enabled {} | ||
| 211 | } | ||
| 203 | } | 212 | } |
| 213 | } | ||
| 204 | 214 | ||
| 205 | if plls.use_pll { | 215 | fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput { |
| 206 | RCC.cr().modify(|w| w.set_pllon(false)); | 216 | // Disable PLL |
| 217 | pll_enable(instance, false); | ||
| 207 | 218 | ||
| 208 | // setup VOSScale | 219 | let Some(pll) = config else { return PllOutput::default() }; |
| 209 | let vos_scale = if sysclk <= 144_000_000 { | ||
| 210 | 3 | ||
| 211 | } else if sysclk <= 168_000_000 { | ||
| 212 | 2 | ||
| 213 | } else { | ||
| 214 | 1 | ||
| 215 | }; | ||
| 216 | PWR.cr1().modify(|w| { | ||
| 217 | w.set_vos(match vos_scale { | ||
| 218 | 3 => Vos::SCALE3, | ||
| 219 | 2 => Vos::SCALE2, | ||
| 220 | 1 => Vos::SCALE1, | ||
| 221 | _ => panic!("Invalid VOS Scale."), | ||
| 222 | }) | ||
| 223 | }); | ||
| 224 | |||
| 225 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 226 | |||
| 227 | if hclk > max::HCLK_OVERDRIVE_FREQUENCY { | ||
| 228 | PWR.cr1().modify(|w| w.set_oden(true)); | ||
| 229 | while !PWR.csr1().read().odrdy() {} | ||
| 230 | |||
| 231 | PWR.cr1().modify(|w| w.set_odswen(true)); | ||
| 232 | while !PWR.csr1().read().odswrdy() {} | ||
| 233 | } | ||
| 234 | 220 | ||
| 235 | while !RCC.cr().read().pllrdy() {} | 221 | let pll_src = match input.source { |
| 236 | } | 222 | PllSource::HSE => input.hse, |
| 223 | PllSource::HSI => input.hsi, | ||
| 224 | }; | ||
| 237 | 225 | ||
| 238 | RCC.cfgr().modify(|w| { | 226 | let pll_src = pll_src.unwrap(); |
| 239 | w.set_ppre2(Ppre::from_bits(ppre2_bits)); | 227 | |
| 240 | w.set_ppre1(Ppre::from_bits(ppre1_bits)); | 228 | let in_freq = pll_src / pll.prediv; |
| 241 | w.set_hpre(hpre_bits); | 229 | assert!(max::PLL_IN.contains(&in_freq)); |
| 242 | }); | 230 | let vco_freq = in_freq * pll.mul; |
| 231 | assert!(max::PLL_VCO.contains(&vco_freq)); | ||
| 232 | |||
| 233 | let p = pll.divp.map(|div| vco_freq / div); | ||
| 234 | let q = pll.divq.map(|div| vco_freq / div); | ||
| 235 | let r = pll.divr.map(|div| vco_freq / div); | ||
| 236 | |||
| 237 | macro_rules! write_fields { | ||
| 238 | ($w:ident) => { | ||
| 239 | $w.set_plln(pll.mul); | ||
| 240 | if let Some(divp) = pll.divp { | ||
| 241 | $w.set_pllp(divp); | ||
| 242 | } | ||
| 243 | if let Some(divq) = pll.divq { | ||
| 244 | $w.set_pllq(divq); | ||
| 245 | } | ||
| 246 | if let Some(divr) = pll.divr { | ||
| 247 | $w.set_pllr(divr); | ||
| 248 | } | ||
| 249 | }; | ||
| 250 | } | ||
| 243 | 251 | ||
| 244 | // Wait for the new prescalers to kick in | 252 | match instance { |
| 245 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | 253 | PllInstance::Pll => RCC.pllcfgr().write(|w| { |
| 246 | cortex_m::asm::delay(16); | 254 | w.set_pllm(pll.prediv); |
| 255 | w.set_pllsrc(input.source); | ||
| 256 | write_fields!(w); | ||
| 257 | }), | ||
| 258 | PllInstance::Plli2s => RCC.plli2scfgr().write(|w| { | ||
| 259 | write_fields!(w); | ||
| 260 | }), | ||
| 261 | PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| { | ||
| 262 | write_fields!(w); | ||
| 263 | }), | ||
| 264 | } | ||
| 247 | 265 | ||
| 248 | RCC.cfgr().modify(|w| { | 266 | // Enable PLL |
| 249 | w.set_sw(if sysclk_on_pll { | 267 | pll_enable(instance, true); |
| 250 | Sw::PLL1_P | ||
| 251 | } else if config.hse.is_some() { | ||
| 252 | Sw::HSE | ||
| 253 | } else { | ||
| 254 | Sw::HSI | ||
| 255 | }) | ||
| 256 | }); | ||
| 257 | 268 | ||
| 258 | let rtc = config.ls.init(); | 269 | PllOutput { p, q, r } |
| 259 | 270 | } | |
| 260 | set_freqs(Clocks { | ||
| 261 | sys: Hertz(sysclk), | ||
| 262 | pclk1: Hertz(pclk1), | ||
| 263 | pclk2: Hertz(pclk2), | ||
| 264 | 271 | ||
| 265 | pclk1_tim: Hertz(pclk1 * timer_mul1), | 272 | fn flash_setup(clk: Hertz) { |
| 266 | pclk2_tim: Hertz(pclk2 * timer_mul2), | 273 | use crate::pac::flash::vals::Latency; |
| 267 | 274 | ||
| 268 | hclk1: Hertz(hclk), | 275 | // Be conservative with voltage ranges |
| 269 | hclk2: Hertz(hclk), | 276 | const FLASH_LATENCY_STEP: u32 = 30_000_000; |
| 270 | hclk3: Hertz(hclk), | ||
| 271 | 277 | ||
| 272 | pll1_q: plls.pll48clk.map(Hertz), | 278 | let latency = (clk.0 - 1) / FLASH_LATENCY_STEP; |
| 279 | debug!("flash: latency={}", latency); | ||
| 273 | 280 | ||
| 274 | rtc, | 281 | let latency = Latency::from_bits(latency as u8); |
| 282 | FLASH.acr().write(|w| { | ||
| 283 | w.set_latency(latency); | ||
| 275 | }); | 284 | }); |
| 285 | while FLASH.acr().read().latency() != latency {} | ||
| 276 | } | 286 | } |
| 277 | 287 | ||
| 278 | struct PllResults { | 288 | fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz) |
| 279 | use_pll: bool, | 289 | where |
| 280 | pllsysclk: Option<u32>, | 290 | Hertz: core::ops::Div<D, Output = Hertz>, |
| 281 | pll48clk: Option<u32>, | 291 | { |
| 292 | let pclk = hclk / ppre; | ||
| 293 | let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 }; | ||
| 294 | (pclk, pclk_tim) | ||
| 282 | } | 295 | } |
| 283 | 296 | ||
| 284 | mod max { | 297 | mod max { |
| 285 | pub(crate) const HSE_OSC_MIN: u32 = 4_000_000; | 298 | use core::ops::RangeInclusive; |
| 286 | pub(crate) const HSE_OSC_MAX: u32 = 26_000_000; | ||
| 287 | pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000; | ||
| 288 | pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000; | ||
| 289 | |||
| 290 | pub(crate) const HCLK_MAX: u32 = 216_000_000; | ||
| 291 | pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000; | ||
| 292 | 299 | ||
| 293 | pub(crate) const SYSCLK_MIN: u32 = 12_500_000; | 300 | use crate::time::Hertz; |
| 294 | pub(crate) const SYSCLK_MAX: u32 = 216_000_000; | ||
| 295 | 301 | ||
| 296 | pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN; | 302 | pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000); |
| 297 | pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4; | 303 | pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000); |
| 298 | 304 | ||
| 299 | pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN; | 305 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000); |
| 300 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | 306 | pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000); |
| 307 | pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 4); | ||
| 308 | pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 2); | ||
| 301 | 309 | ||
| 302 | // USB specification allows +-0.25% | 310 | pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000); |
| 303 | pub(crate) const PLL_48_CLK: u32 = 48_000_000; | 311 | pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000); |
| 304 | pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; | ||
| 305 | } | 312 | } |
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index d50473b9d..7c6c419a6 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI; | |||
| 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; |
| 11 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 12 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 13 | use embassy_stm32::time::mhz; | 13 | use embassy_stm32::time::Hertz; |
| 14 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 14 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 15 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| 16 | use embedded_io_async::Write; | 16 | use embedded_io_async::Write; |
| @@ -33,7 +33,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! { | |||
| 33 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 34 | async fn main(spawner: Spawner) -> ! { | 34 | async fn main(spawner: Spawner) -> ! { |
| 35 | let mut config = Config::default(); | 35 | let mut config = Config::default(); |
| 36 | config.rcc.sys_ck = Some(mhz(200)); | 36 | { |
| 37 | use embassy_stm32::rcc::*; | ||
| 38 | config.rcc.hse = Some(Hse { | ||
| 39 | freq: Hertz(8_000_000), | ||
| 40 | mode: HseMode::Bypass, | ||
| 41 | }); | ||
| 42 | config.rcc.pll_src = PllSource::HSE; | ||
| 43 | config.rcc.pll = Some(Pll { | ||
| 44 | prediv: PllPreDiv::DIV4, | ||
| 45 | mul: PllMul::MUL216, | ||
| 46 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz | ||
| 47 | divq: None, | ||
| 48 | divr: None, | ||
| 49 | }); | ||
| 50 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 51 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 52 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 53 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 54 | } | ||
| 37 | let p = embassy_stm32::init(config); | 55 | let p = embassy_stm32::init(config); |
| 38 | 56 | ||
| 39 | info!("Hello World!"); | 57 | info!("Hello World!"); |
diff --git a/examples/stm32f7/src/bin/hello.rs b/examples/stm32f7/src/bin/hello.rs index 27ee83aa5..a2a287110 100644 --- a/examples/stm32f7/src/bin/hello.rs +++ b/examples/stm32f7/src/bin/hello.rs | |||
| @@ -4,15 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::time::Hertz; | ||
| 8 | use embassy_stm32::Config; | 7 | use embassy_stm32::Config; |
| 9 | use embassy_time::Timer; | 8 | use embassy_time::Timer; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 10 | ||
| 12 | #[embassy_executor::main] | 11 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) -> ! { | 12 | async fn main(_spawner: Spawner) -> ! { |
| 14 | let mut config = Config::default(); | 13 | let config = Config::default(); |
| 15 | config.rcc.sys_ck = Some(Hertz(84_000_000)); | ||
| 16 | let _p = embassy_stm32::init(config); | 14 | let _p = embassy_stm32::init(config); |
| 17 | 15 | ||
| 18 | loop { | 16 | loop { |
diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 9d43892a0..430aa781f 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::sdmmc::Sdmmc; | 7 | use embassy_stm32::sdmmc::Sdmmc; |
| 8 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::{mhz, Hertz}; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| @@ -16,8 +16,25 @@ bind_interrupts!(struct Irqs { | |||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 17 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 18 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| 19 | config.rcc.sys_ck = Some(mhz(200)); | 19 | { |
| 20 | config.rcc.pll48 = true; | 20 | use embassy_stm32::rcc::*; |
| 21 | config.rcc.hse = Some(Hse { | ||
| 22 | freq: Hertz(8_000_000), | ||
| 23 | mode: HseMode::Bypass, | ||
| 24 | }); | ||
| 25 | config.rcc.pll_src = PllSource::HSE; | ||
| 26 | config.rcc.pll = Some(Pll { | ||
| 27 | prediv: PllPreDiv::DIV4, | ||
| 28 | mul: PllMul::MUL216, | ||
| 29 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz | ||
| 30 | divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz | ||
| 31 | divr: None, | ||
| 32 | }); | ||
| 33 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 34 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 35 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 36 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 37 | } | ||
| 21 | let p = embassy_stm32::init(config); | 38 | let p = embassy_stm32::init(config); |
| 22 | 39 | ||
| 23 | info!("Hello World!"); | 40 | info!("Hello World!"); |
diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index a2c76178b..2f832c234 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{panic, *}; | 5 | use defmt::{panic, *}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::time::mhz; | 7 | use embassy_stm32::time::Hertz; |
| 8 | use embassy_stm32::usb_otg::{Driver, Instance}; | 8 | use embassy_stm32::usb_otg::{Driver, Instance}; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; |
| 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; |
| @@ -22,10 +22,25 @@ async fn main(_spawner: Spawner) { | |||
| 22 | info!("Hello World!"); | 22 | info!("Hello World!"); |
| 23 | 23 | ||
| 24 | let mut config = Config::default(); | 24 | let mut config = Config::default(); |
| 25 | config.rcc.hse = Some(mhz(8)); | 25 | { |
| 26 | config.rcc.pll48 = true; | 26 | use embassy_stm32::rcc::*; |
| 27 | config.rcc.sys_ck = Some(mhz(200)); | 27 | config.rcc.hse = Some(Hse { |
| 28 | 28 | freq: Hertz(8_000_000), | |
| 29 | mode: HseMode::Bypass, | ||
| 30 | }); | ||
| 31 | config.rcc.pll_src = PllSource::HSE; | ||
| 32 | config.rcc.pll = Some(Pll { | ||
| 33 | prediv: PllPreDiv::DIV4, | ||
| 34 | mul: PllMul::MUL216, | ||
| 35 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz | ||
| 36 | divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz | ||
| 37 | divr: None, | ||
| 38 | }); | ||
| 39 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 40 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 41 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 42 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 43 | } | ||
| 29 | let p = embassy_stm32::init(config); | 44 | let p = embassy_stm32::init(config); |
| 30 | 45 | ||
| 31 | // Create the driver, from the HAL. | 46 | // Create the driver, from the HAL. |
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 9f1307ce5..7bc741416 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -233,7 +233,23 @@ pub fn config() -> Config { | |||
| 233 | 233 | ||
| 234 | #[cfg(feature = "stm32f767zi")] | 234 | #[cfg(feature = "stm32f767zi")] |
| 235 | { | 235 | { |
| 236 | config.rcc.sys_ck = Some(Hertz(200_000_000)); | 236 | use embassy_stm32::rcc::*; |
| 237 | config.rcc.hse = Some(Hse { | ||
| 238 | freq: Hertz(8_000_000), | ||
| 239 | mode: HseMode::Bypass, | ||
| 240 | }); | ||
| 241 | config.rcc.pll_src = PllSource::HSE; | ||
| 242 | config.rcc.pll = Some(Pll { | ||
| 243 | prediv: PllPreDiv::DIV4, | ||
| 244 | mul: PllMul::MUL216, | ||
| 245 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz. | ||
| 246 | divq: None, | ||
| 247 | divr: None, | ||
| 248 | }); | ||
| 249 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 250 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 251 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 252 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 237 | } | 253 | } |
| 238 | 254 | ||
| 239 | #[cfg(feature = "stm32h563zi")] | 255 | #[cfg(feature = "stm32h563zi")] |
