diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-07-31 11:08:46 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-07-31 11:08:46 +0200 |
| commit | 3835278567e05014158eeede15d62a57ee105518 (patch) | |
| tree | df0617979c36f81c5f8f68d1d7244a186d8b5ce6 | |
| parent | e9885a61f88e3746275297a6d0ea7f837b302757 (diff) | |
| parent | 21e3acaa00f405397811c3abf71dbb26ea932896 (diff) | |
Merge pull request #321 from thalesfragoso/f4-pll
F4 PLL
| -rw-r--r-- | embassy-stm32/build.rs | 11 | ||||
| -rw-r--r-- | embassy-stm32/src/pwr/f4.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/pwr/mod.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/f4/max.rs | 21 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/f4/mod.rs | 432 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 3 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/hello.rs | 34 | ||||
| m--------- | stm32-data | 0 | ||||
| -rw-r--r-- | stm32-metapac-gen/src/lib.rs | 8 |
9 files changed, 348 insertions, 163 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index df8af660b..a6434a7e8 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -9,7 +9,7 @@ fn main() { | |||
| 9 | .expect("No stm32xx Cargo feature enabled") | 9 | .expect("No stm32xx Cargo feature enabled") |
| 10 | .strip_prefix("CARGO_FEATURE_") | 10 | .strip_prefix("CARGO_FEATURE_") |
| 11 | .unwrap() | 11 | .unwrap() |
| 12 | .to_ascii_uppercase(); | 12 | .to_ascii_lowercase(); |
| 13 | 13 | ||
| 14 | let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | 14 | let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); |
| 15 | let out_file = out_dir.join("generated.rs").to_string_lossy().to_string(); | 15 | let out_file = out_dir.join("generated.rs").to_string_lossy().to_string(); |
| @@ -30,6 +30,15 @@ fn main() { | |||
| 30 | }; | 30 | }; |
| 31 | ); | 31 | ); |
| 32 | 32 | ||
| 33 | let mut chip_and_core = chip_name.split('_'); | ||
| 34 | let chip = chip_and_core.next().expect("Unexpected stm32xx feature"); | ||
| 35 | |||
| 36 | if let Some(core) = chip_and_core.next() { | ||
| 37 | println!("cargo:rustc-cfg={}_{}", &chip[..(chip.len() - 2)], core); | ||
| 38 | } else { | ||
| 39 | println!("cargo:rustc-cfg={}", &chip[..(chip.len() - 2)]); | ||
| 40 | } | ||
| 41 | |||
| 33 | println!("cargo:rerun-if-changed=build.rs"); | 42 | println!("cargo:rerun-if-changed=build.rs"); |
| 34 | println!("cargo:rerun-if-changed=gen.py"); | 43 | println!("cargo:rerun-if-changed=gen.py"); |
| 35 | } | 44 | } |
diff --git a/embassy-stm32/src/pwr/f4.rs b/embassy-stm32/src/pwr/f4.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/embassy-stm32/src/pwr/f4.rs | |||
| @@ -0,0 +1 @@ | |||
diff --git a/embassy-stm32/src/pwr/mod.rs b/embassy-stm32/src/pwr/mod.rs index 5b563d725..1bb104bd3 100644 --- a/embassy-stm32/src/pwr/mod.rs +++ b/embassy-stm32/src/pwr/mod.rs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #[cfg_attr(any(pwr_h7, pwr_h7smps), path = "h7.rs")] | 1 | #[cfg_attr(any(pwr_h7, pwr_h7smps), path = "h7.rs")] |
| 2 | #[cfg_attr(pwr_f4, path = "f4.rs")] | ||
| 2 | mod _version; | 3 | mod _version; |
| 3 | 4 | ||
| 4 | pub use _version::*; | 5 | pub use _version::*; |
diff --git a/embassy-stm32/src/rcc/f4/max.rs b/embassy-stm32/src/rcc/f4/max.rs new file mode 100644 index 000000000..dd8de3da9 --- /dev/null +++ b/embassy-stm32/src/rcc/f4/max.rs | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #[cfg(stm32f401)] | ||
| 2 | pub(crate) const SYSCLK_MAX: u32 = 84_000_000; | ||
| 3 | |||
| 4 | #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))] | ||
| 5 | pub(crate) const SYSCLK_MAX: u32 = 168_000_000; | ||
| 6 | |||
| 7 | #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 8 | pub(crate) const SYSCLK_MAX: u32 = 100_000_000; | ||
| 9 | |||
| 10 | #[cfg(any( | ||
| 11 | stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479, | ||
| 12 | ))] | ||
| 13 | pub(crate) const SYSCLK_MAX: u32 = 180_000_000; | ||
| 14 | |||
| 15 | #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 16 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX; | ||
| 17 | |||
| 18 | #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))] | ||
| 19 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | ||
| 20 | |||
| 21 | pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2; | ||
diff --git a/embassy-stm32/src/rcc/f4/mod.rs b/embassy-stm32/src/rcc/f4/mod.rs index d47510da7..1cf3e5bd9 100644 --- a/embassy-stm32/src/rcc/f4/mod.rs +++ b/embassy-stm32/src/rcc/f4/mod.rs | |||
| @@ -1,207 +1,325 @@ | |||
| 1 | pub use super::types::*; | 1 | use crate::pac::{FLASH, PWR, 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 | mod max; |
| 13 | /// and with the addition of the init function to configure a system clock. | 9 | use max::{PCLK1_MAX, PCLK2_MAX}; |
| 14 | 10 | ||
| 15 | /// Only the basic setup using the HSE and HSI clocks are supported as of now. | 11 | const HSI: u32 = 16_000_000; |
| 16 | 12 | ||
| 17 | /// HSI speed | 13 | /// Clocks configutation |
| 18 | pub const HSI_FREQ: u32 = 16_000_000; | 14 | #[non_exhaustive] |
| 19 | 15 | #[derive(Default)] | |
| 20 | /// System clock mux source | 16 | pub struct Config { |
| 21 | #[derive(Clone, Copy)] | 17 | pub hse: Option<Hertz>, |
| 22 | pub enum ClockSrc { | 18 | pub bypass_hse: bool, |
| 23 | HSE(Hertz), | 19 | pub pll48: bool, |
| 24 | HSI16, | 20 | pub sys_ck: Option<Hertz>, |
| 21 | pub hclk: Option<Hertz>, | ||
| 22 | pub pclk1: Option<Hertz>, | ||
| 23 | pub pclk2: Option<Hertz>, | ||
| 24 | pub enable_debug_wfe: bool, | ||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | impl Into<Ppre> for APBPrescaler { | 27 | /// RCC peripheral |
| 28 | fn into(self) -> Ppre { | 28 | pub struct Rcc<'d> { |
| 29 | match self { | 29 | config: Config, |
| 30 | APBPrescaler::NotDivided => Ppre::DIV1, | 30 | phantom: PhantomData<&'d mut peripherals::RCC>, |
| 31 | APBPrescaler::Div2 => Ppre::DIV2, | ||
| 32 | APBPrescaler::Div4 => Ppre::DIV4, | ||
| 33 | APBPrescaler::Div8 => Ppre::DIV8, | ||
| 34 | APBPrescaler::Div16 => Ppre::DIV16, | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | 31 | } |
| 38 | 32 | ||
| 39 | impl Into<Hpre> for AHBPrescaler { | 33 | impl<'d> Rcc<'d> { |
| 40 | fn into(self) -> Hpre { | 34 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { |
| 41 | match self { | 35 | Self { |
| 42 | AHBPrescaler::NotDivided => Hpre::DIV1, | 36 | config, |
| 43 | AHBPrescaler::Div2 => Hpre::DIV2, | 37 | phantom: PhantomData, |
| 44 | AHBPrescaler::Div4 => Hpre::DIV4, | ||
| 45 | AHBPrescaler::Div8 => Hpre::DIV8, | ||
| 46 | AHBPrescaler::Div16 => Hpre::DIV16, | ||
| 47 | AHBPrescaler::Div64 => Hpre::DIV64, | ||
| 48 | AHBPrescaler::Div128 => Hpre::DIV128, | ||
| 49 | AHBPrescaler::Div256 => Hpre::DIV256, | ||
| 50 | AHBPrescaler::Div512 => Hpre::DIV512, | ||
| 51 | } | 38 | } |
| 52 | } | 39 | } |
| 53 | } | ||
| 54 | 40 | ||
| 55 | /// Clocks configutation | 41 | fn freeze(mut self) -> Clocks { |
| 56 | pub struct Config { | 42 | use super::sealed::RccPeripheral; |
| 57 | mux: ClockSrc, | 43 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw}; |
| 58 | ahb_pre: AHBPrescaler, | ||
| 59 | apb1_pre: APBPrescaler, | ||
| 60 | apb2_pre: APBPrescaler, | ||
| 61 | } | ||
| 62 | 44 | ||
| 63 | impl Default for Config { | 45 | let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI); |
| 64 | #[inline] | 46 | let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); |
| 65 | fn default() -> Config { | 47 | let sysclk_on_pll = sysclk != pllsrcclk; |
| 66 | Config { | 48 | |
| 67 | mux: ClockSrc::HSI16, | 49 | let plls = self.setup_pll( |
| 68 | ahb_pre: AHBPrescaler::NotDivided, | 50 | pllsrcclk, |
| 69 | apb1_pre: APBPrescaler::NotDivided, | 51 | self.config.hse.is_some(), |
| 70 | apb2_pre: APBPrescaler::NotDivided, | 52 | if sysclk_on_pll { Some(sysclk) } else { None }, |
| 53 | self.config.pll48, | ||
| 54 | ); | ||
| 55 | |||
| 56 | if self.config.pll48 { | ||
| 57 | assert!( | ||
| 58 | // USB specification allows +-0.25% | ||
| 59 | plls.pll48clk | ||
| 60 | .map(|freq| (48_000_000 - freq as i32).abs() <= 120_000) | ||
| 61 | .unwrap_or(false) | ||
| 62 | ); | ||
| 71 | } | 63 | } |
| 72 | } | ||
| 73 | } | ||
| 74 | 64 | ||
| 75 | impl Config { | 65 | let sysclk = if sysclk_on_pll { |
| 76 | #[inline] | 66 | plls.pllsysclk.unwrap() |
| 77 | pub fn clock_src(mut self, mux: ClockSrc) -> Self { | 67 | } else { |
| 78 | self.mux = mux; | 68 | sysclk |
| 79 | self | 69 | }; |
| 80 | } | ||
| 81 | 70 | ||
| 82 | #[inline] | 71 | let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk); |
| 83 | pub fn ahb_pre(mut self, pre: AHBPrescaler) -> Self { | 72 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { |
| 84 | self.ahb_pre = pre; | 73 | 0 => unreachable!(), |
| 85 | self | 74 | 1 => (Hpre::DIV1, 1), |
| 86 | } | 75 | 2 => (Hpre::DIV2, 2), |
| 76 | 3..=5 => (Hpre::DIV4, 4), | ||
| 77 | 6..=11 => (Hpre::DIV8, 8), | ||
| 78 | 12..=39 => (Hpre::DIV16, 16), | ||
| 79 | 40..=95 => (Hpre::DIV64, 64), | ||
| 80 | 96..=191 => (Hpre::DIV128, 128), | ||
| 81 | 192..=383 => (Hpre::DIV256, 256), | ||
| 82 | _ => (Hpre::DIV512, 512), | ||
| 83 | }; | ||
| 87 | 84 | ||
| 88 | #[inline] | 85 | // Calculate real AHB clock |
| 89 | pub fn apb1_pre(mut self, pre: APBPrescaler) -> Self { | 86 | let hclk = sysclk / hpre_div; |
| 90 | self.apb1_pre = pre; | ||
| 91 | self | ||
| 92 | } | ||
| 93 | 87 | ||
| 94 | #[inline] | 88 | let pclk1 = self |
| 95 | pub fn apb2_pre(mut self, pre: APBPrescaler) -> Self { | 89 | .config |
| 96 | self.apb2_pre = pre; | 90 | .pclk1 |
| 97 | self | 91 | .map(|p| p.0) |
| 98 | } | 92 | .unwrap_or_else(|| core::cmp::min(PCLK1_MAX, hclk)); |
| 99 | } | 93 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { |
| 94 | 0 => unreachable!(), | ||
| 95 | 1 => (0b000, 1), | ||
| 96 | 2 => (0b100, 2), | ||
| 97 | 3..=5 => (0b101, 4), | ||
| 98 | 6..=11 => (0b110, 8), | ||
| 99 | _ => (0b111, 16), | ||
| 100 | }; | ||
| 101 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 100 | 102 | ||
| 101 | /// RCC peripheral | 103 | // Calculate real APB1 clock |
| 102 | pub struct Rcc<'d> { | 104 | let pclk1 = hclk / ppre1; |
| 103 | _rb: peripherals::RCC, | 105 | assert!(pclk1 <= PCLK1_MAX); |
| 104 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 105 | } | ||
| 106 | 106 | ||
| 107 | impl<'d> Rcc<'d> { | 107 | let pclk2 = self |
| 108 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | 108 | .config |
| 109 | unborrow!(rcc); | 109 | .pclk2 |
| 110 | Self { | 110 | .map(|p| p.0) |
| 111 | _rb: rcc, | 111 | .unwrap_or_else(|| core::cmp::min(PCLK2_MAX, hclk)); |
| 112 | phantom: PhantomData, | 112 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { |
| 113 | 0 => unreachable!(), | ||
| 114 | 1 => (0b000, 1), | ||
| 115 | 2 => (0b100, 2), | ||
| 116 | 3..=5 => (0b101, 4), | ||
| 117 | 6..=11 => (0b110, 8), | ||
| 118 | _ => (0b111, 16), | ||
| 119 | }; | ||
| 120 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 121 | |||
| 122 | // Calculate real APB2 clock | ||
| 123 | let pclk2 = hclk / ppre2; | ||
| 124 | assert!(pclk2 <= PCLK2_MAX); | ||
| 125 | |||
| 126 | Self::flash_setup(sysclk); | ||
| 127 | |||
| 128 | if self.config.hse.is_some() { | ||
| 129 | // NOTE(unsafe) We own the peripheral block | ||
| 130 | unsafe { | ||
| 131 | RCC.cr().modify(|w| { | ||
| 132 | w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8)); | ||
| 133 | w.set_hseon(true); | ||
| 134 | }); | ||
| 135 | while !RCC.cr().read().hserdy() {} | ||
| 136 | } | ||
| 113 | } | 137 | } |
| 114 | } | ||
| 115 | 138 | ||
| 116 | // Safety: RCC init must have been called | 139 | if plls.use_pll { |
| 117 | pub fn clocks(&self) -> &'static Clocks { | 140 | unsafe { |
| 118 | unsafe { get_freqs() } | 141 | RCC.cr().modify(|w| w.set_pllon(true)); |
| 119 | } | ||
| 120 | } | ||
| 121 | 142 | ||
| 122 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | 143 | if hclk > 168_000_000 { |
| 123 | pub trait RccExt { | 144 | peripherals::PWR::enable(); |
| 124 | fn freeze(self, config: Config) -> Clocks; | ||
| 125 | } | ||
| 126 | 145 | ||
| 127 | impl RccExt for RCC { | 146 | PWR.cr().modify(|w| w.set_oden(true)); |
| 128 | #[inline] | 147 | while !PWR.csr().read().odrdy() {} |
| 129 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 130 | let rcc = pac::RCC; | ||
| 131 | let (sys_clk, sw) = match cfgr.mux { | ||
| 132 | ClockSrc::HSI16 => { | ||
| 133 | // Enable HSI16 | ||
| 134 | unsafe { | ||
| 135 | rcc.cr().modify(|w| w.set_hsion(true)); | ||
| 136 | while !rcc.cr().read().hsirdy() {} | ||
| 137 | } | ||
| 138 | 148 | ||
| 139 | (HSI_FREQ, Sw::HSI) | 149 | PWR.cr().modify(|w| w.set_odswen(true)); |
| 140 | } | 150 | while !PWR.csr().read().odswrdy() {} |
| 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 | } | 151 | } |
| 147 | 152 | ||
| 148 | (freq.0, Sw::HSE) | 153 | while !RCC.cr().read().pllrdy() {} |
| 149 | } | 154 | } |
| 150 | }; | 155 | } |
| 151 | 156 | ||
| 152 | unsafe { | 157 | unsafe { |
| 153 | rcc.cfgr().modify(|w| { | 158 | RCC.cfgr().modify(|w| { |
| 154 | w.set_sw(sw.into()); | 159 | w.set_ppre2(Ppre(ppre2_bits)); |
| 155 | w.set_hpre(cfgr.ahb_pre.into()); | 160 | w.set_ppre1(Ppre(ppre1_bits)); |
| 156 | w.set_ppre1(cfgr.apb1_pre.into()); | 161 | w.set_hpre(hpre_bits); |
| 157 | w.set_ppre2(cfgr.apb2_pre.into()); | 162 | }); |
| 163 | |||
| 164 | // Wait for the new prescalers to kick in | ||
| 165 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | ||
| 166 | cortex_m::asm::delay(16); | ||
| 167 | |||
| 168 | RCC.cfgr().modify(|w| { | ||
| 169 | w.set_sw(if sysclk_on_pll { | ||
| 170 | Sw::PLL | ||
| 171 | } else if self.config.hse.is_some() { | ||
| 172 | Sw::HSE | ||
| 173 | } else { | ||
| 174 | Sw::HSI | ||
| 175 | }) | ||
| 158 | }); | 176 | }); |
| 159 | } | 177 | } |
| 160 | 178 | ||
| 161 | let ahb_freq: u32 = match cfgr.ahb_pre { | 179 | if self.config.enable_debug_wfe { |
| 162 | AHBPrescaler::NotDivided => sys_clk, | 180 | unsafe { |
| 163 | pre => { | 181 | RCC.ahb1enr().modify(|w| w.set_dma1en(true)); |
| 164 | let pre: Hpre = pre.into(); | 182 | critical_section::with(|_| { |
| 165 | let pre = 1 << (pre.0 as u32 - 7); | 183 | crate::dbgmcu::Dbgmcu::enable_all(); |
| 166 | sys_clk / pre | 184 | }); |
| 167 | } | 185 | } |
| 168 | }; | 186 | } |
| 187 | |||
| 188 | Clocks { | ||
| 189 | sys: Hertz(sysclk), | ||
| 190 | apb1: Hertz(pclk1), | ||
| 191 | apb2: Hertz(pclk2), | ||
| 192 | |||
| 193 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 194 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 195 | |||
| 196 | ahb1: Hertz(hclk), | ||
| 197 | ahb2: Hertz(hclk), | ||
| 198 | ahb3: Hertz(hclk), | ||
| 199 | |||
| 200 | pll48: plls.pll48clk.map(Hertz), | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | // Safety: RCC init must have been called | ||
| 205 | pub fn clocks(&self) -> &'static Clocks { | ||
| 206 | unsafe { get_freqs() } | ||
| 207 | } | ||
| 169 | 208 | ||
| 170 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | 209 | fn setup_pll( |
| 171 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 210 | &mut self, |
| 172 | pre => { | 211 | pllsrcclk: u32, |
| 173 | let pre: Ppre = pre.into(); | 212 | use_hse: bool, |
| 174 | let pre: u8 = 1 << (pre.0 - 3); | 213 | pllsysclk: Option<u32>, |
| 175 | let freq = ahb_freq / pre as u32; | 214 | pll48clk: bool, |
| 176 | (freq, freq * 2) | 215 | ) -> PllResults { |
| 216 | use crate::pac::rcc::vals::{Pllp, Pllsrc}; | ||
| 217 | |||
| 218 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | ||
| 219 | if pllsysclk.is_none() && !pll48clk { | ||
| 220 | // NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock | ||
| 221 | unsafe { | ||
| 222 | RCC.pllcfgr() | ||
| 223 | .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); | ||
| 177 | } | 224 | } |
| 225 | |||
| 226 | return PllResults { | ||
| 227 | use_pll: false, | ||
| 228 | pllsysclk: None, | ||
| 229 | pll48clk: None, | ||
| 230 | }; | ||
| 231 | } | ||
| 232 | // Input divisor from PLL source clock, must result to frequency in | ||
| 233 | // the range from 1 to 2 MHz | ||
| 234 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 235 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 236 | |||
| 237 | // Sysclk output divisor must be one of 2, 4, 6 or 8 | ||
| 238 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); | ||
| 239 | |||
| 240 | let target_freq = if pll48clk { | ||
| 241 | 48_000_000 | ||
| 242 | } else { | ||
| 243 | sysclk * sysclk_div | ||
| 178 | }; | 244 | }; |
| 179 | 245 | ||
| 180 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | 246 | // Find the lowest pllm value that minimize the difference between |
| 181 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 247 | // target frequency and the real vco_out frequency. |
| 182 | pre => { | 248 | let pllm = (pllm_min..=pllm_max) |
| 183 | let pre: Ppre = pre.into(); | 249 | .min_by_key(|pllm| { |
| 184 | let pre: u8 = 1 << (pre.0 - 3); | 250 | let vco_in = pllsrcclk / pllm; |
| 185 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | 251 | let plln = target_freq / vco_in; |
| 186 | (freq, freq * 2) | 252 | target_freq - vco_in * plln |
| 187 | } | 253 | }) |
| 254 | .unwrap(); | ||
| 255 | |||
| 256 | let vco_in = pllsrcclk / pllm; | ||
| 257 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 258 | |||
| 259 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 260 | // and <= 432MHz, min 50, max 432 | ||
| 261 | let plln = if pll48clk { | ||
| 262 | // try the different valid pllq according to the valid | ||
| 263 | // main scaller values, and take the best | ||
| 264 | let pllq = (4..=9) | ||
| 265 | .min_by_key(|pllq| { | ||
| 266 | let plln = 48_000_000 * pllq / vco_in; | ||
| 267 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 268 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 269 | (pll48_diff, sysclk_diff) | ||
| 270 | }) | ||
| 271 | .unwrap(); | ||
| 272 | 48_000_000 * pllq / vco_in | ||
| 273 | } else { | ||
| 274 | sysclk * sysclk_div / vco_in | ||
| 188 | }; | 275 | }; |
| 189 | 276 | ||
| 190 | Clocks { | 277 | let pllp = (sysclk_div / 2) - 1; |
| 191 | sys: sys_clk.hz(), | 278 | |
| 192 | ahb1: ahb_freq.hz(), | 279 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; |
| 193 | ahb2: ahb_freq.hz(), | 280 | let real_pll48clk = vco_in * plln / pllq; |
| 194 | ahb3: ahb_freq.hz(), | 281 | |
| 195 | apb1: apb1_freq.hz(), | 282 | unsafe { |
| 196 | apb2: apb2_freq.hz(), | 283 | RCC.pllcfgr().modify(|w| { |
| 197 | apb1_tim: apb1_tim_freq.hz(), | 284 | w.set_pllm(pllm as u8); |
| 198 | apb2_tim: apb2_tim_freq.hz(), | 285 | w.set_plln(plln as u16); |
| 286 | w.set_pllp(Pllp(pllp as u8)); | ||
| 287 | w.set_pllq(pllq as u8); | ||
| 288 | w.set_pllsrc(Pllsrc(use_hse as u8)); | ||
| 289 | }); | ||
| 290 | } | ||
| 291 | |||
| 292 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 293 | |||
| 294 | PllResults { | ||
| 295 | use_pll: true, | ||
| 296 | pllsysclk: Some(real_pllsysclk), | ||
| 297 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 199 | } | 298 | } |
| 200 | } | 299 | } |
| 300 | |||
| 301 | fn flash_setup(sysclk: u32) { | ||
| 302 | use crate::pac::flash::vals::Latency; | ||
| 303 | |||
| 304 | // Be conservative with voltage ranges | ||
| 305 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 306 | |||
| 307 | critical_section::with(|_| unsafe { | ||
| 308 | FLASH | ||
| 309 | .acr() | ||
| 310 | .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 311 | }); | ||
| 312 | } | ||
| 201 | } | 313 | } |
| 202 | 314 | ||
| 203 | pub unsafe fn init(config: Config) { | 315 | pub unsafe fn init(config: Config) { |
| 204 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | 316 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); |
| 205 | let clocks = r.freeze(config); | 317 | let clocks = Rcc::new(r, config).freeze(); |
| 206 | set_freqs(clocks); | 318 | set_freqs(clocks); |
| 207 | } | 319 | } |
| 320 | |||
| 321 | struct PllResults { | ||
| 322 | use_pll: bool, | ||
| 323 | pllsysclk: Option<u32>, | ||
| 324 | pll48clk: Option<u32>, | ||
| 325 | } | ||
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 |
diff --git a/examples/stm32f4/src/bin/hello.rs b/examples/stm32f4/src/bin/hello.rs new file mode 100644 index 000000000..8ee6c1ef8 --- /dev/null +++ b/examples/stm32f4/src/bin/hello.rs | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(trait_alias)] | ||
| 4 | #![feature(min_type_alias_impl_trait)] | ||
| 5 | #![feature(impl_trait_in_bindings)] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | #![allow(incomplete_features)] | ||
| 8 | |||
| 9 | use defmt::{info, panic}; | ||
| 10 | use embassy::executor::Spawner; | ||
| 11 | use embassy::time::{Duration, Timer}; | ||
| 12 | use embassy_stm32::rcc::Config as RccConfig; | ||
| 13 | use embassy_stm32::time::Hertz; | ||
| 14 | use embassy_stm32::Config; | ||
| 15 | use embassy_stm32::Peripherals; | ||
| 16 | |||
| 17 | #[path = "../example_common.rs"] | ||
| 18 | mod example_common; | ||
| 19 | |||
| 20 | fn config() -> Config { | ||
| 21 | let mut rcc_config = RccConfig::default(); | ||
| 22 | rcc_config.sys_ck = Some(Hertz(84_000_000)); | ||
| 23 | rcc_config.enable_debug_wfe = true; | ||
| 24 | |||
| 25 | Config::default().rcc(rcc_config) | ||
| 26 | } | ||
| 27 | |||
| 28 | #[embassy::main(config = "config()")] | ||
| 29 | async fn main(_spawner: Spawner, _p: Peripherals) -> ! { | ||
| 30 | loop { | ||
| 31 | info!("Hello World!"); | ||
| 32 | Timer::after(Duration::from_secs(1)).await; | ||
| 33 | } | ||
| 34 | } | ||
diff --git a/stm32-data b/stm32-data | |||
| Subproject 62c8985228186a02a623d1acfb59b75a4865d30 | Subproject 0ad27b2fd1126c6c9d9f4602d1331f5d82f4aa2 | ||
diff --git a/stm32-metapac-gen/src/lib.rs b/stm32-metapac-gen/src/lib.rs index 83f4dcfb3..f508b3658 100644 --- a/stm32-metapac-gen/src/lib.rs +++ b/stm32-metapac-gen/src/lib.rs | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | use chiptool::generate::CommonModule; | 1 | use chiptool::generate::CommonModule; |
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use regex::Regex; | 2 | use regex::Regex; |
| 4 | use serde::Deserialize; | 3 | use serde::Deserialize; |
| 5 | use std::collections::{HashMap, HashSet}; | 4 | use std::collections::{HashMap, HashSet}; |
| @@ -10,7 +9,6 @@ use std::fs::File; | |||
| 10 | use std::io::Write; | 9 | use std::io::Write; |
| 11 | use std::path::Path; | 10 | use std::path::Path; |
| 12 | use std::path::PathBuf; | 11 | use std::path::PathBuf; |
| 13 | use std::str::FromStr; | ||
| 14 | 12 | ||
| 15 | use chiptool::util::ToSanitizedSnakeCase; | 13 | use chiptool::util::ToSanitizedSnakeCase; |
| 16 | use chiptool::{generate, ir, transform}; | 14 | use chiptool::{generate, ir, transform}; |
| @@ -54,7 +52,7 @@ pub struct Peripheral { | |||
| 54 | #[serde(default)] | 52 | #[serde(default)] |
| 55 | pub dma_channels: HashMap<String, Vec<PeripheralDmaChannel>>, | 53 | pub dma_channels: HashMap<String, Vec<PeripheralDmaChannel>>, |
| 56 | #[serde(default)] | 54 | #[serde(default)] |
| 57 | pub interrupts: HashMap<String, String> | 55 | pub interrupts: HashMap<String, String>, |
| 58 | } | 56 | } |
| 59 | 57 | ||
| 60 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | 58 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] |
| @@ -353,8 +351,8 @@ pub fn gen(options: Options) { | |||
| 353 | row.push(name.clone()); | 351 | row.push(name.clone()); |
| 354 | row.push(bi.module.clone()); | 352 | row.push(bi.module.clone()); |
| 355 | row.push(bi.block.clone()); | 353 | row.push(bi.block.clone()); |
| 356 | row.push( signal.clone() ); | 354 | row.push(signal.clone()); |
| 357 | row.push( irq_name.to_ascii_uppercase() ); | 355 | row.push(irq_name.to_ascii_uppercase()); |
| 358 | interrupt_table.push(row) | 356 | interrupt_table.push(row) |
| 359 | } | 357 | } |
| 360 | 358 | ||
