diff options
| -rw-r--r-- | embassy-stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/f4.rs | 400 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/f4f7.rs | 379 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/f7.rs | 305 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h.rs | 9 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/l4l5.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 3 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/eth.rs | 22 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/hello.rs | 4 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/sdmmc.rs | 23 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/usb_ethernet.rs | 24 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/usb_serial.rs | 24 | ||||
| -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 | 40 |
17 files changed, 559 insertions, 753 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ab7b92219..3b9220bc7 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -58,7 +58,7 @@ rand_core = "0.6.3" | |||
| 58 | sdio-host = "0.5.0" | 58 | sdio-host = "0.5.0" |
| 59 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } | 59 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } |
| 60 | critical-section = "1.1" | 60 | critical-section = "1.1" |
| 61 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6f7449303bf8af60a63704d35df9af46006c6148" } | 61 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526" } |
| 62 | vcell = "0.1.3" | 62 | vcell = "0.1.3" |
| 63 | bxcan = "0.7.0" | 63 | bxcan = "0.7.0" |
| 64 | nb = "1.0.0" | 64 | nb = "1.0.0" |
| @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } | |||
| 76 | [build-dependencies] | 76 | [build-dependencies] |
| 77 | proc-macro2 = "1.0.36" | 77 | proc-macro2 = "1.0.36" |
| 78 | quote = "1.0.15" | 78 | quote = "1.0.15" |
| 79 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6f7449303bf8af60a63704d35df9af46006c6148", default-features = false, features = ["metadata"]} | 79 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526", default-features = false, features = ["metadata"]} |
| 80 | 80 | ||
| 81 | 81 | ||
| 82 | [features] | 82 | [features] |
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs deleted file mode 100644 index eb51dc89a..000000000 --- a/embassy-stm32/src/rcc/f4.rs +++ /dev/null | |||
| @@ -1,400 +0,0 @@ | |||
| 1 | use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllq, Pllr, Ppre, Sw}; | ||
| 2 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 3 | use crate::rcc::{set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | |||
| 6 | /// HSI speed | ||
| 7 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | ||
| 8 | |||
| 9 | /// Clocks configuration | ||
| 10 | #[non_exhaustive] | ||
| 11 | #[derive(Default)] | ||
| 12 | pub struct Config { | ||
| 13 | pub hse: Option<Hertz>, | ||
| 14 | pub bypass_hse: bool, | ||
| 15 | pub hclk: Option<Hertz>, | ||
| 16 | pub sys_ck: Option<Hertz>, | ||
| 17 | pub pclk1: Option<Hertz>, | ||
| 18 | pub pclk2: Option<Hertz>, | ||
| 19 | |||
| 20 | #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] | ||
| 21 | pub plli2s: Option<Hertz>, | ||
| 22 | |||
| 23 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 24 | pub pllsai: Option<Hertz>, | ||
| 25 | |||
| 26 | pub pll48: bool, | ||
| 27 | pub ls: super::LsConfig, | ||
| 28 | } | ||
| 29 | |||
| 30 | #[cfg(stm32f410)] | ||
| 31 | fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> { | ||
| 32 | None | ||
| 33 | } | ||
| 34 | |||
| 35 | // Not currently implemented, but will be in the future | ||
| 36 | #[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] | ||
| 37 | fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> { | ||
| 38 | None | ||
| 39 | } | ||
| 40 | |||
| 41 | #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423)))] | ||
| 42 | fn calculate_sai_i2s_pll_values(vco_in: u32, max_div: u32, target: Option<u32>) -> Option<(u32, u32, u32)> { | ||
| 43 | let min_div = 2; | ||
| 44 | let target = match target { | ||
| 45 | Some(target) => target, | ||
| 46 | None => return None, | ||
| 47 | }; | ||
| 48 | |||
| 49 | // We loop through the possible divider values to find the best configuration. Looping | ||
| 50 | // through all possible "N" values would result in more iterations. | ||
| 51 | let (n, outdiv, output, _error) = (min_div..=max_div) | ||
| 52 | .filter_map(|outdiv| { | ||
| 53 | let target_vco_out = match target.checked_mul(outdiv) { | ||
| 54 | Some(x) => x, | ||
| 55 | None => return None, | ||
| 56 | }; | ||
| 57 | let n = (target_vco_out + (vco_in >> 1)) / vco_in; | ||
| 58 | let vco_out = vco_in * n; | ||
| 59 | if !(100_000_000..=432_000_000).contains(&vco_out) { | ||
| 60 | return None; | ||
| 61 | } | ||
| 62 | let output = vco_out / outdiv; | ||
| 63 | let error = (output as i32 - target as i32).unsigned_abs(); | ||
| 64 | Some((n, outdiv, output, error)) | ||
| 65 | }) | ||
| 66 | .min_by_key(|(_, _, _, error)| *error)?; | ||
| 67 | |||
| 68 | Some((n, outdiv, output)) | ||
| 69 | } | ||
| 70 | |||
| 71 | #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] | ||
| 72 | fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> { | ||
| 73 | let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 7, plli2s)?; | ||
| 74 | |||
| 75 | RCC.plli2scfgr().modify(|w| { | ||
| 76 | w.set_plli2sn(n as u16); | ||
| 77 | w.set_plli2sr(outdiv as u8); | ||
| 78 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 79 | w.set_plli2sq(outdiv as u8); //set sai divider same as i2s | ||
| 80 | }); | ||
| 81 | |||
| 82 | Some(output) | ||
| 83 | } | ||
| 84 | |||
| 85 | #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))] | ||
| 86 | fn setup_sai_pll(_vco_in: u32, _pllsai: Option<u32>) -> Option<u32> { | ||
| 87 | None | ||
| 88 | } | ||
| 89 | |||
| 90 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 91 | fn setup_sai_pll(vco_in: u32, pllsai: Option<u32>) -> Option<u32> { | ||
| 92 | let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 15, pllsai)?; | ||
| 93 | |||
| 94 | RCC.pllsaicfgr().modify(|w| { | ||
| 95 | w.set_pllsain(n as u16); | ||
| 96 | w.set_pllsaiq(outdiv as u8); | ||
| 97 | }); | ||
| 98 | |||
| 99 | Some(output) | ||
| 100 | } | ||
| 101 | |||
| 102 | fn setup_pll( | ||
| 103 | pllsrcclk: u32, | ||
| 104 | use_hse: bool, | ||
| 105 | pllsysclk: Option<u32>, | ||
| 106 | plli2s: Option<u32>, | ||
| 107 | pllsai: Option<u32>, | ||
| 108 | pll48clk: bool, | ||
| 109 | ) -> PllResults { | ||
| 110 | use crate::pac::rcc::vals::{Pllp, Pllsrc}; | ||
| 111 | |||
| 112 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | ||
| 113 | if pllsysclk.is_none() && !pll48clk { | ||
| 114 | RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8))); | ||
| 115 | |||
| 116 | return PllResults { | ||
| 117 | use_pll: false, | ||
| 118 | pllsysclk: None, | ||
| 119 | pll48clk: None, | ||
| 120 | plli2sclk: None, | ||
| 121 | pllsaiclk: None, | ||
| 122 | }; | ||
| 123 | } | ||
| 124 | // Input divisor from PLL source clock, must result to frequency in | ||
| 125 | // the range from 1 to 2 MHz | ||
| 126 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 127 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 128 | |||
| 129 | // Sysclk output divisor must be one of 2, 4, 6 or 8 | ||
| 130 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); | ||
| 131 | |||
| 132 | let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div }; | ||
| 133 | |||
| 134 | // Find the lowest pllm value that minimize the difference between | ||
| 135 | // target frequency and the real vco_out frequency. | ||
| 136 | let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { | ||
| 137 | let vco_in = pllsrcclk / pllm; | ||
| 138 | let plln = target_freq / vco_in; | ||
| 139 | target_freq - vco_in * plln | ||
| 140 | })); | ||
| 141 | |||
| 142 | let vco_in = pllsrcclk / pllm; | ||
| 143 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 144 | |||
| 145 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 146 | // and <= 432MHz, min 50, max 432 | ||
| 147 | let plln = if pll48clk { | ||
| 148 | // try the different valid pllq according to the valid | ||
| 149 | // main scaller values, and take the best | ||
| 150 | let pllq = unwrap!((4..=9).min_by_key(|pllq| { | ||
| 151 | let plln = 48_000_000 * pllq / vco_in; | ||
| 152 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 153 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 154 | (pll48_diff, sysclk_diff) | ||
| 155 | })); | ||
| 156 | 48_000_000 * pllq / vco_in | ||
| 157 | } else { | ||
| 158 | sysclk * sysclk_div / vco_in | ||
| 159 | }; | ||
| 160 | |||
| 161 | let pllp = (sysclk_div / 2) - 1; | ||
| 162 | |||
| 163 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; | ||
| 164 | let real_pll48clk = vco_in * plln / pllq; | ||
| 165 | |||
| 166 | RCC.pllcfgr().modify(|w| { | ||
| 167 | w.set_pllm(Pllm::from_bits(pllm as u8)); | ||
| 168 | w.set_plln(Plln::from_bits(plln as u16)); | ||
| 169 | w.set_pllp(Pllp::from_bits(pllp as u8)); | ||
| 170 | w.set_pllq(Pllq::from_bits(pllq as u8)); | ||
| 171 | w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); | ||
| 172 | w.set_pllr(Pllr::from_bits(0)); | ||
| 173 | }); | ||
| 174 | |||
| 175 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 176 | |||
| 177 | PllResults { | ||
| 178 | use_pll: true, | ||
| 179 | pllsysclk: Some(real_pllsysclk), | ||
| 180 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 181 | plli2sclk: setup_i2s_pll(vco_in, plli2s), | ||
| 182 | pllsaiclk: setup_sai_pll(vco_in, pllsai), | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | fn flash_setup(sysclk: u32) { | ||
| 187 | use crate::pac::flash::vals::Latency; | ||
| 188 | |||
| 189 | // Be conservative with voltage ranges | ||
| 190 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 191 | |||
| 192 | critical_section::with(|_| { | ||
| 193 | FLASH | ||
| 194 | .acr() | ||
| 195 | .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 196 | }); | ||
| 197 | } | ||
| 198 | |||
| 199 | pub(crate) unsafe fn init(config: Config) { | ||
| 200 | let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0); | ||
| 201 | let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | ||
| 202 | let sysclk_on_pll = sysclk != pllsrcclk; | ||
| 203 | |||
| 204 | let plls = setup_pll( | ||
| 205 | pllsrcclk, | ||
| 206 | config.hse.is_some(), | ||
| 207 | if sysclk_on_pll { Some(sysclk) } else { None }, | ||
| 208 | #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] | ||
| 209 | config.plli2s.map(|i2s| i2s.0), | ||
| 210 | #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] | ||
| 211 | None, | ||
| 212 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 213 | config.pllsai.map(|sai| sai.0), | ||
| 214 | #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))] | ||
| 215 | None, | ||
| 216 | config.pll48, | ||
| 217 | ); | ||
| 218 | |||
| 219 | if config.pll48 { | ||
| 220 | let freq = unwrap!(plls.pll48clk); | ||
| 221 | |||
| 222 | assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32); | ||
| 223 | } | ||
| 224 | |||
| 225 | let sysclk = if sysclk_on_pll { unwrap!(plls.pllsysclk) } else { sysclk }; | ||
| 226 | |||
| 227 | // AHB prescaler | ||
| 228 | let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 229 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { | ||
| 230 | 0 => unreachable!(), | ||
| 231 | 1 => (Hpre::DIV1, 1), | ||
| 232 | 2 => (Hpre::DIV2, 2), | ||
| 233 | 3..=5 => (Hpre::DIV4, 4), | ||
| 234 | 6..=11 => (Hpre::DIV8, 8), | ||
| 235 | 12..=39 => (Hpre::DIV16, 16), | ||
| 236 | 40..=95 => (Hpre::DIV64, 64), | ||
| 237 | 96..=191 => (Hpre::DIV128, 128), | ||
| 238 | 192..=383 => (Hpre::DIV256, 256), | ||
| 239 | _ => (Hpre::DIV512, 512), | ||
| 240 | }; | ||
| 241 | |||
| 242 | // Calculate real AHB clock | ||
| 243 | let hclk = sysclk / hpre_div; | ||
| 244 | |||
| 245 | let pclk1 = config | ||
| 246 | .pclk1 | ||
| 247 | .map(|p| p.0) | ||
| 248 | .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); | ||
| 249 | |||
| 250 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | ||
| 251 | 0 => unreachable!(), | ||
| 252 | 1 => (0b000, 1), | ||
| 253 | 2 => (0b100, 2), | ||
| 254 | 3..=5 => (0b101, 4), | ||
| 255 | 6..=11 => (0b110, 8), | ||
| 256 | _ => (0b111, 16), | ||
| 257 | }; | ||
| 258 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 259 | |||
| 260 | // Calculate real APB1 clock | ||
| 261 | let pclk1 = hclk / ppre1; | ||
| 262 | assert!(pclk1 <= max::PCLK1_MAX); | ||
| 263 | |||
| 264 | let pclk2 = config | ||
| 265 | .pclk2 | ||
| 266 | .map(|p| p.0) | ||
| 267 | .unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk)); | ||
| 268 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { | ||
| 269 | 0 => unreachable!(), | ||
| 270 | 1 => (0b000, 1), | ||
| 271 | 2 => (0b100, 2), | ||
| 272 | 3..=5 => (0b101, 4), | ||
| 273 | 6..=11 => (0b110, 8), | ||
| 274 | _ => (0b111, 16), | ||
| 275 | }; | ||
| 276 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 277 | |||
| 278 | // Calculate real APB2 clock | ||
| 279 | let pclk2 = hclk / ppre2; | ||
| 280 | assert!(pclk2 <= max::PCLK2_MAX); | ||
| 281 | |||
| 282 | flash_setup(sysclk); | ||
| 283 | |||
| 284 | if config.hse.is_some() { | ||
| 285 | RCC.cr().modify(|w| { | ||
| 286 | w.set_hsebyp(config.bypass_hse); | ||
| 287 | w.set_hseon(true); | ||
| 288 | }); | ||
| 289 | while !RCC.cr().read().hserdy() {} | ||
| 290 | } | ||
| 291 | |||
| 292 | if plls.use_pll { | ||
| 293 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 294 | |||
| 295 | if hclk > max::HCLK_OVERDRIVE_FREQUENCY { | ||
| 296 | PWR.cr1().modify(|w| w.set_oden(true)); | ||
| 297 | while !PWR.csr1().read().odrdy() {} | ||
| 298 | |||
| 299 | PWR.cr1().modify(|w| w.set_odswen(true)); | ||
| 300 | while !PWR.csr1().read().odswrdy() {} | ||
| 301 | } | ||
| 302 | |||
| 303 | while !RCC.cr().read().pllrdy() {} | ||
| 304 | } | ||
| 305 | |||
| 306 | #[cfg(not(stm32f410))] | ||
| 307 | if plls.plli2sclk.is_some() { | ||
| 308 | RCC.cr().modify(|w| w.set_plli2son(true)); | ||
| 309 | |||
| 310 | while !RCC.cr().read().plli2srdy() {} | ||
| 311 | } | ||
| 312 | |||
| 313 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 314 | if plls.pllsaiclk.is_some() { | ||
| 315 | RCC.cr().modify(|w| w.set_pllsaion(true)); | ||
| 316 | while !RCC.cr().read().pllsairdy() {} | ||
| 317 | } | ||
| 318 | |||
| 319 | RCC.cfgr().modify(|w| { | ||
| 320 | w.set_ppre2(Ppre::from_bits(ppre2_bits)); | ||
| 321 | w.set_ppre1(Ppre::from_bits(ppre1_bits)); | ||
| 322 | w.set_hpre(hpre_bits); | ||
| 323 | }); | ||
| 324 | |||
| 325 | // Wait for the new prescalers to kick in | ||
| 326 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | ||
| 327 | cortex_m::asm::delay(16); | ||
| 328 | |||
| 329 | RCC.cfgr().modify(|w| { | ||
| 330 | w.set_sw(if sysclk_on_pll { | ||
| 331 | Sw::PLL1_P | ||
| 332 | } else if config.hse.is_some() { | ||
| 333 | Sw::HSE | ||
| 334 | } else { | ||
| 335 | Sw::HSI | ||
| 336 | }) | ||
| 337 | }); | ||
| 338 | |||
| 339 | let rtc = config.ls.init(); | ||
| 340 | |||
| 341 | set_freqs(Clocks { | ||
| 342 | sys: Hertz(sysclk), | ||
| 343 | pclk1: Hertz(pclk1), | ||
| 344 | pclk2: Hertz(pclk2), | ||
| 345 | |||
| 346 | pclk1_tim: Hertz(pclk1 * timer_mul1), | ||
| 347 | pclk2_tim: Hertz(pclk2 * timer_mul2), | ||
| 348 | |||
| 349 | hclk1: Hertz(hclk), | ||
| 350 | hclk2: Hertz(hclk), | ||
| 351 | hclk3: Hertz(hclk), | ||
| 352 | |||
| 353 | pll1_q: plls.pll48clk.map(Hertz), | ||
| 354 | |||
| 355 | #[cfg(not(stm32f410))] | ||
| 356 | plli2s1_q: plls.plli2sclk.map(Hertz), | ||
| 357 | #[cfg(not(stm32f410))] | ||
| 358 | plli2s1_r: None, | ||
| 359 | |||
| 360 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 361 | pllsai1_q: plls.pllsaiclk.map(Hertz), | ||
| 362 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 363 | pllsai1_r: None, | ||
| 364 | |||
| 365 | rtc, | ||
| 366 | }); | ||
| 367 | } | ||
| 368 | |||
| 369 | struct PllResults { | ||
| 370 | use_pll: bool, | ||
| 371 | pllsysclk: Option<u32>, | ||
| 372 | pll48clk: Option<u32>, | ||
| 373 | #[allow(dead_code)] | ||
| 374 | plli2sclk: Option<u32>, | ||
| 375 | #[allow(dead_code)] | ||
| 376 | pllsaiclk: Option<u32>, | ||
| 377 | } | ||
| 378 | |||
| 379 | mod max { | ||
| 380 | #[cfg(stm32f401)] | ||
| 381 | pub(crate) const SYSCLK_MAX: u32 = 84_000_000; | ||
| 382 | #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))] | ||
| 383 | pub(crate) const SYSCLK_MAX: u32 = 168_000_000; | ||
| 384 | #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 385 | pub(crate) const SYSCLK_MAX: u32 = 100_000_000; | ||
| 386 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))] | ||
| 387 | pub(crate) const SYSCLK_MAX: u32 = 180_000_000; | ||
| 388 | |||
| 389 | pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 168_000_000; | ||
| 390 | |||
| 391 | pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2; | ||
| 392 | |||
| 393 | #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 394 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX; | ||
| 395 | #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))] | ||
| 396 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | ||
| 397 | |||
| 398 | pub(crate) const PLL_48_CLK: u32 = 48_000_000; | ||
| 399 | pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; | ||
| 400 | } | ||
diff --git a/embassy-stm32/src/rcc/f4f7.rs b/embassy-stm32/src/rcc/f4f7.rs new file mode 100644 index 000000000..de37eab72 --- /dev/null +++ b/embassy-stm32/src/rcc/f4f7.rs | |||
| @@ -0,0 +1,379 @@ | |||
| 1 | pub use crate::pac::rcc::vals::{ | ||
| 2 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource, | ||
| 3 | Ppre as APBPrescaler, Sw as Sysclk, | ||
| 4 | }; | ||
| 5 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 6 | use crate::rcc::{set_freqs, Clocks}; | ||
| 7 | use crate::time::Hertz; | ||
| 8 | |||
| 9 | // TODO: on some F4s, PLLM is shared between all PLLs. Enforce that. | ||
| 10 | // TODO: on some F4s, add support for plli2s_src | ||
| 11 | // | ||
| 12 | // plli2s plli2s_m plli2s_src pllsai pllsai_m | ||
| 13 | // f401 y shared | ||
| 14 | // f410 | ||
| 15 | // f411 y individual | ||
| 16 | // f412 y individual y | ||
| 17 | // f4[12]3 y individual y | ||
| 18 | // f446 y individual y individual | ||
| 19 | // f4[67]9 y shared y shared | ||
| 20 | // f4[23][79] y shared y shared | ||
| 21 | // f4[01][57] y shared | ||
| 22 | |||
| 23 | /// HSI speed | ||
| 24 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | ||
| 25 | |||
| 26 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 27 | pub enum HseMode { | ||
| 28 | /// crystal/ceramic oscillator (HSEBYP=0) | ||
| 29 | Oscillator, | ||
| 30 | /// external analog clock (low swing) (HSEBYP=1) | ||
| 31 | Bypass, | ||
| 32 | } | ||
| 33 | |||
| 34 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 35 | pub struct Hse { | ||
| 36 | /// HSE frequency. | ||
| 37 | pub freq: Hertz, | ||
| 38 | /// HSE mode. | ||
| 39 | pub mode: HseMode, | ||
| 40 | } | ||
| 41 | |||
| 42 | #[derive(Clone, Copy)] | ||
| 43 | pub struct Pll { | ||
| 44 | /// PLL pre-divider (DIVM). | ||
| 45 | pub prediv: PllPreDiv, | ||
| 46 | |||
| 47 | /// PLL multiplication factor. | ||
| 48 | pub mul: PllMul, | ||
| 49 | |||
| 50 | /// PLL P division factor. If None, PLL P output is disabled. | ||
| 51 | pub divp: Option<Pllp>, | ||
| 52 | /// PLL Q division factor. If None, PLL Q output is disabled. | ||
| 53 | pub divq: Option<Pllq>, | ||
| 54 | /// PLL R division factor. If None, PLL R output is disabled. | ||
| 55 | pub divr: Option<Pllr>, | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Configuration of the core clocks | ||
| 59 | #[non_exhaustive] | ||
| 60 | pub struct Config { | ||
| 61 | pub hsi: bool, | ||
| 62 | pub hse: Option<Hse>, | ||
| 63 | pub sys: Sysclk, | ||
| 64 | |||
| 65 | pub pll_src: PllSource, | ||
| 66 | |||
| 67 | pub pll: Option<Pll>, | ||
| 68 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] | ||
| 69 | pub plli2s: Option<Pll>, | ||
| 70 | #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] | ||
| 71 | pub pllsai: Option<Pll>, | ||
| 72 | |||
| 73 | pub ahb_pre: AHBPrescaler, | ||
| 74 | pub apb1_pre: APBPrescaler, | ||
| 75 | pub apb2_pre: APBPrescaler, | ||
| 76 | |||
| 77 | pub ls: super::LsConfig, | ||
| 78 | } | ||
| 79 | |||
| 80 | impl Default for Config { | ||
| 81 | fn default() -> Self { | ||
| 82 | Self { | ||
| 83 | hsi: true, | ||
| 84 | hse: None, | ||
| 85 | sys: Sysclk::HSI, | ||
| 86 | pll_src: PllSource::HSI, | ||
| 87 | pll: None, | ||
| 88 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] | ||
| 89 | plli2s: None, | ||
| 90 | #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] | ||
| 91 | pllsai: None, | ||
| 92 | |||
| 93 | ahb_pre: AHBPrescaler::DIV1, | ||
| 94 | apb1_pre: APBPrescaler::DIV1, | ||
| 95 | apb2_pre: APBPrescaler::DIV1, | ||
| 96 | |||
| 97 | ls: Default::default(), | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | pub(crate) unsafe fn init(config: Config) { | ||
| 103 | // always enable overdrive for now. Make it configurable in the future. | ||
| 104 | PWR.cr1().modify(|w| w.set_oden(true)); | ||
| 105 | while !PWR.csr1().read().odrdy() {} | ||
| 106 | |||
| 107 | PWR.cr1().modify(|w| w.set_odswen(true)); | ||
| 108 | while !PWR.csr1().read().odswrdy() {} | ||
| 109 | |||
| 110 | // Configure HSI | ||
| 111 | let hsi = match config.hsi { | ||
| 112 | false => { | ||
| 113 | RCC.cr().modify(|w| w.set_hsion(false)); | ||
| 114 | None | ||
| 115 | } | ||
| 116 | true => { | ||
| 117 | RCC.cr().modify(|w| w.set_hsion(true)); | ||
| 118 | while !RCC.cr().read().hsirdy() {} | ||
| 119 | Some(HSI_FREQ) | ||
| 120 | } | ||
| 121 | }; | ||
| 122 | |||
| 123 | // Configure HSE | ||
| 124 | let hse = match config.hse { | ||
| 125 | None => { | ||
| 126 | RCC.cr().modify(|w| w.set_hseon(false)); | ||
| 127 | None | ||
| 128 | } | ||
| 129 | Some(hse) => { | ||
| 130 | match hse.mode { | ||
| 131 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | ||
| 132 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | ||
| 133 | } | ||
| 134 | |||
| 135 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | ||
| 136 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 137 | while !RCC.cr().read().hserdy() {} | ||
| 138 | Some(hse.freq) | ||
| 139 | } | ||
| 140 | }; | ||
| 141 | |||
| 142 | // Configure PLLs. | ||
| 143 | let pll_input = PllInput { | ||
| 144 | hse, | ||
| 145 | hsi, | ||
| 146 | source: config.pll_src, | ||
| 147 | }; | ||
| 148 | let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); | ||
| 149 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] | ||
| 150 | let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input); | ||
| 151 | #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] | ||
| 152 | let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input); | ||
| 153 | |||
| 154 | // Configure sysclk | ||
| 155 | let sys = match config.sys { | ||
| 156 | Sysclk::HSI => unwrap!(hsi), | ||
| 157 | Sysclk::HSE => unwrap!(hse), | ||
| 158 | Sysclk::PLL1_P => unwrap!(pll.p), | ||
| 159 | _ => unreachable!(), | ||
| 160 | }; | ||
| 161 | |||
| 162 | let hclk = sys / config.ahb_pre; | ||
| 163 | let (pclk1, pclk1_tim) = calc_pclk(hclk, config.apb1_pre); | ||
| 164 | let (pclk2, pclk2_tim) = calc_pclk(hclk, config.apb2_pre); | ||
| 165 | |||
| 166 | assert!(max::SYSCLK.contains(&sys)); | ||
| 167 | assert!(max::HCLK.contains(&hclk)); | ||
| 168 | assert!(max::PCLK1.contains(&pclk1)); | ||
| 169 | assert!(max::PCLK2.contains(&pclk2)); | ||
| 170 | |||
| 171 | let rtc = config.ls.init(); | ||
| 172 | |||
| 173 | flash_setup(hclk); | ||
| 174 | |||
| 175 | RCC.cfgr().modify(|w| { | ||
| 176 | w.set_sw(config.sys); | ||
| 177 | w.set_hpre(config.ahb_pre); | ||
| 178 | w.set_ppre1(config.apb1_pre); | ||
| 179 | w.set_ppre2(config.apb2_pre); | ||
| 180 | }); | ||
| 181 | while RCC.cfgr().read().sws() != config.sys {} | ||
| 182 | |||
| 183 | set_freqs(Clocks { | ||
| 184 | sys, | ||
| 185 | hclk1: hclk, | ||
| 186 | hclk2: hclk, | ||
| 187 | hclk3: hclk, | ||
| 188 | pclk1, | ||
| 189 | pclk2, | ||
| 190 | pclk1_tim, | ||
| 191 | pclk2_tim, | ||
| 192 | rtc, | ||
| 193 | pll1_q: pll.q, | ||
| 194 | #[cfg(all(rcc_f4, not(stm32f410)))] | ||
| 195 | plli2s1_q: _plli2s.q, | ||
| 196 | #[cfg(all(rcc_f4, not(stm32f410)))] | ||
| 197 | plli2s1_r: _plli2s.r, | ||
| 198 | |||
| 199 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 200 | pllsai1_q: _pllsai.q, | ||
| 201 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | ||
| 202 | pllsai1_r: _pllsai.r, | ||
| 203 | }); | ||
| 204 | } | ||
| 205 | |||
| 206 | struct PllInput { | ||
| 207 | source: PllSource, | ||
| 208 | hsi: Option<Hertz>, | ||
| 209 | hse: Option<Hertz>, | ||
| 210 | } | ||
| 211 | |||
| 212 | #[derive(Default)] | ||
| 213 | #[allow(unused)] | ||
| 214 | struct PllOutput { | ||
| 215 | p: Option<Hertz>, | ||
| 216 | q: Option<Hertz>, | ||
| 217 | r: Option<Hertz>, | ||
| 218 | } | ||
| 219 | |||
| 220 | #[derive(PartialEq, Eq, Clone, Copy)] | ||
| 221 | enum PllInstance { | ||
| 222 | Pll, | ||
| 223 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] | ||
| 224 | Plli2s, | ||
| 225 | #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] | ||
| 226 | Pllsai, | ||
| 227 | } | ||
| 228 | |||
| 229 | fn pll_enable(instance: PllInstance, enabled: bool) { | ||
| 230 | match instance { | ||
| 231 | PllInstance::Pll => { | ||
| 232 | RCC.cr().modify(|w| w.set_pllon(enabled)); | ||
| 233 | while RCC.cr().read().pllrdy() != enabled {} | ||
| 234 | } | ||
| 235 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] | ||
| 236 | PllInstance::Plli2s => { | ||
| 237 | RCC.cr().modify(|w| w.set_plli2son(enabled)); | ||
| 238 | while RCC.cr().read().plli2srdy() != enabled {} | ||
| 239 | } | ||
| 240 | #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] | ||
| 241 | PllInstance::Pllsai => { | ||
| 242 | RCC.cr().modify(|w| w.set_pllsaion(enabled)); | ||
| 243 | while RCC.cr().read().pllsairdy() != enabled {} | ||
| 244 | } | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput { | ||
| 249 | // Disable PLL | ||
| 250 | pll_enable(instance, false); | ||
| 251 | |||
| 252 | let Some(pll) = config else { return PllOutput::default() }; | ||
| 253 | |||
| 254 | let pll_src = match input.source { | ||
| 255 | PllSource::HSE => input.hse, | ||
| 256 | PllSource::HSI => input.hsi, | ||
| 257 | }; | ||
| 258 | |||
| 259 | let pll_src = pll_src.unwrap(); | ||
| 260 | |||
| 261 | let in_freq = pll_src / pll.prediv; | ||
| 262 | assert!(max::PLL_IN.contains(&in_freq)); | ||
| 263 | let vco_freq = in_freq * pll.mul; | ||
| 264 | assert!(max::PLL_VCO.contains(&vco_freq)); | ||
| 265 | |||
| 266 | let p = pll.divp.map(|div| vco_freq / div); | ||
| 267 | let q = pll.divq.map(|div| vco_freq / div); | ||
| 268 | let r = pll.divr.map(|div| vco_freq / div); | ||
| 269 | |||
| 270 | macro_rules! write_fields { | ||
| 271 | ($w:ident) => { | ||
| 272 | $w.set_plln(pll.mul); | ||
| 273 | if let Some(divp) = pll.divp { | ||
| 274 | $w.set_pllp(divp); | ||
| 275 | } | ||
| 276 | if let Some(divq) = pll.divq { | ||
| 277 | $w.set_pllq(divq); | ||
| 278 | } | ||
| 279 | if let Some(divr) = pll.divr { | ||
| 280 | $w.set_pllr(divr); | ||
| 281 | } | ||
| 282 | }; | ||
| 283 | } | ||
| 284 | |||
| 285 | match instance { | ||
| 286 | PllInstance::Pll => RCC.pllcfgr().write(|w| { | ||
| 287 | w.set_pllm(pll.prediv); | ||
| 288 | w.set_pllsrc(input.source); | ||
| 289 | write_fields!(w); | ||
| 290 | }), | ||
| 291 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] | ||
| 292 | PllInstance::Plli2s => RCC.plli2scfgr().write(|w| { | ||
| 293 | write_fields!(w); | ||
| 294 | }), | ||
| 295 | #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] | ||
| 296 | PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| { | ||
| 297 | write_fields!(w); | ||
| 298 | }), | ||
| 299 | } | ||
| 300 | |||
| 301 | // Enable PLL | ||
| 302 | pll_enable(instance, true); | ||
| 303 | |||
| 304 | PllOutput { p, q, r } | ||
| 305 | } | ||
| 306 | |||
| 307 | fn flash_setup(clk: Hertz) { | ||
| 308 | use crate::pac::flash::vals::Latency; | ||
| 309 | |||
| 310 | // Be conservative with voltage ranges | ||
| 311 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 312 | |||
| 313 | let latency = (clk.0 - 1) / FLASH_LATENCY_STEP; | ||
| 314 | debug!("flash: latency={}", latency); | ||
| 315 | |||
| 316 | let latency = Latency::from_bits(latency as u8); | ||
| 317 | FLASH.acr().write(|w| { | ||
| 318 | w.set_latency(latency); | ||
| 319 | }); | ||
| 320 | while FLASH.acr().read().latency() != latency {} | ||
| 321 | } | ||
| 322 | |||
| 323 | fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz) | ||
| 324 | where | ||
| 325 | Hertz: core::ops::Div<D, Output = Hertz>, | ||
| 326 | { | ||
| 327 | let pclk = hclk / ppre; | ||
| 328 | let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 }; | ||
| 329 | (pclk, pclk_tim) | ||
| 330 | } | ||
| 331 | |||
| 332 | #[cfg(stm32f7)] | ||
| 333 | mod max { | ||
| 334 | use core::ops::RangeInclusive; | ||
| 335 | |||
| 336 | use crate::time::Hertz; | ||
| 337 | |||
| 338 | pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000); | ||
| 339 | pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000); | ||
| 340 | |||
| 341 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000); | ||
| 342 | pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000); | ||
| 343 | pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 4); | ||
| 344 | pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 2); | ||
| 345 | |||
| 346 | pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000); | ||
| 347 | pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000); | ||
| 348 | } | ||
| 349 | |||
| 350 | #[cfg(stm32f4)] | ||
| 351 | mod max { | ||
| 352 | use core::ops::RangeInclusive; | ||
| 353 | |||
| 354 | use crate::time::Hertz; | ||
| 355 | |||
| 356 | pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000); | ||
| 357 | pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000); | ||
| 358 | |||
| 359 | #[cfg(stm32f401)] | ||
| 360 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(84_000_000); | ||
| 361 | #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))] | ||
| 362 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(168_000_000); | ||
| 363 | #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 364 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(100_000_000); | ||
| 365 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))] | ||
| 366 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(180_000_000); | ||
| 367 | |||
| 368 | pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0); | ||
| 369 | |||
| 370 | pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(PCLK2.end().0 / 2); | ||
| 371 | |||
| 372 | #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 373 | pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0); | ||
| 374 | #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))] | ||
| 375 | pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0 / 2); | ||
| 376 | |||
| 377 | pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000); | ||
| 378 | pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000); | ||
| 379 | } | ||
diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs deleted file mode 100644 index 7c6c150d9..000000000 --- a/embassy-stm32/src/rcc/f7.rs +++ /dev/null | |||
| @@ -1,305 +0,0 @@ | |||
| 1 | use crate::pac::pwr::vals::Vos; | ||
| 2 | use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllp, Pllq, Pllsrc, Ppre, Sw}; | ||
| 3 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 4 | use crate::rcc::{set_freqs, Clocks}; | ||
| 5 | use crate::time::Hertz; | ||
| 6 | |||
| 7 | /// HSI speed | ||
| 8 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | ||
| 9 | |||
| 10 | /// Clocks configuration | ||
| 11 | #[non_exhaustive] | ||
| 12 | #[derive(Default)] | ||
| 13 | pub struct Config { | ||
| 14 | pub hse: Option<Hertz>, | ||
| 15 | pub bypass_hse: bool, | ||
| 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 | } | ||
| 24 | |||
| 25 | fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults { | ||
| 26 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | ||
| 27 | if pllsysclk.is_none() && !pll48clk { | ||
| 28 | RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8))); | ||
| 29 | |||
| 30 | return PllResults { | ||
| 31 | use_pll: false, | ||
| 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 | |||
| 73 | let pllp = (sysclk_div / 2) - 1; | ||
| 74 | |||
| 75 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; | ||
| 76 | let real_pll48clk = vco_in * plln / pllq; | ||
| 77 | |||
| 78 | RCC.pllcfgr().modify(|w| { | ||
| 79 | w.set_pllm(Pllm::from_bits(pllm as u8)); | ||
| 80 | w.set_plln(Plln::from_bits(plln as u16)); | ||
| 81 | w.set_pllp(Pllp::from_bits(pllp as u8)); | ||
| 82 | w.set_pllq(Pllq::from_bits(pllq as u8)); | ||
| 83 | w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); | ||
| 84 | }); | ||
| 85 | |||
| 86 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 87 | |||
| 88 | PllResults { | ||
| 89 | use_pll: true, | ||
| 90 | pllsysclk: Some(real_pllsysclk), | ||
| 91 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | fn flash_setup(sysclk: u32) { | ||
| 96 | use crate::pac::flash::vals::Latency; | ||
| 97 | |||
| 98 | // Be conservative with voltage ranges | ||
| 99 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 100 | |||
| 101 | critical_section::with(|_| { | ||
| 102 | FLASH | ||
| 103 | .acr() | ||
| 104 | .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 105 | }); | ||
| 106 | } | ||
| 107 | |||
| 108 | pub(crate) unsafe fn init(config: Config) { | ||
| 109 | if let Some(hse) = config.hse { | ||
| 110 | if config.bypass_hse { | ||
| 111 | assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0)); | ||
| 112 | } else { | ||
| 113 | assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0)); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0); | ||
| 118 | let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | ||
| 119 | let sysclk_on_pll = sysclk != pllsrcclk; | ||
| 120 | |||
| 121 | assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk)); | ||
| 122 | |||
| 123 | let plls = setup_pll( | ||
| 124 | pllsrcclk, | ||
| 125 | config.hse.is_some(), | ||
| 126 | if sysclk_on_pll { Some(sysclk) } else { None }, | ||
| 127 | config.pll48, | ||
| 128 | ); | ||
| 129 | |||
| 130 | if config.pll48 { | ||
| 131 | let freq = unwrap!(plls.pll48clk); | ||
| 132 | |||
| 133 | assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32); | ||
| 134 | } | ||
| 135 | |||
| 136 | let sysclk = if sysclk_on_pll { unwrap!(plls.pllsysclk) } else { sysclk }; | ||
| 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 | |||
| 153 | // Calculate real AHB clock | ||
| 154 | let hclk = sysclk / hpre_div; | ||
| 155 | |||
| 156 | assert!(hclk <= max::HCLK_MAX); | ||
| 157 | |||
| 158 | let pclk1 = config | ||
| 159 | .pclk1 | ||
| 160 | .map(|p| p.0) | ||
| 161 | .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); | ||
| 162 | |||
| 163 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | ||
| 164 | 0 => unreachable!(), | ||
| 165 | 1 => (0b000, 1), | ||
| 166 | 2 => (0b100, 2), | ||
| 167 | 3..=5 => (0b101, 4), | ||
| 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 | |||
| 191 | // Calculate real APB2 clock | ||
| 192 | let pclk2 = hclk / ppre2; | ||
| 193 | assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2)); | ||
| 194 | |||
| 195 | flash_setup(sysclk); | ||
| 196 | |||
| 197 | if config.hse.is_some() { | ||
| 198 | RCC.cr().modify(|w| { | ||
| 199 | w.set_hsebyp(config.bypass_hse); | ||
| 200 | w.set_hseon(true); | ||
| 201 | }); | ||
| 202 | while !RCC.cr().read().hserdy() {} | ||
| 203 | } | ||
| 204 | |||
| 205 | if plls.use_pll { | ||
| 206 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 207 | |||
| 208 | // setup VOSScale | ||
| 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 | |||
| 235 | while !RCC.cr().read().pllrdy() {} | ||
| 236 | } | ||
| 237 | |||
| 238 | RCC.cfgr().modify(|w| { | ||
| 239 | w.set_ppre2(Ppre::from_bits(ppre2_bits)); | ||
| 240 | w.set_ppre1(Ppre::from_bits(ppre1_bits)); | ||
| 241 | w.set_hpre(hpre_bits); | ||
| 242 | }); | ||
| 243 | |||
| 244 | // Wait for the new prescalers to kick in | ||
| 245 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | ||
| 246 | cortex_m::asm::delay(16); | ||
| 247 | |||
| 248 | RCC.cfgr().modify(|w| { | ||
| 249 | w.set_sw(if sysclk_on_pll { | ||
| 250 | Sw::PLL1_P | ||
| 251 | } else if config.hse.is_some() { | ||
| 252 | Sw::HSE | ||
| 253 | } else { | ||
| 254 | Sw::HSI | ||
| 255 | }) | ||
| 256 | }); | ||
| 257 | |||
| 258 | let rtc = config.ls.init(); | ||
| 259 | |||
| 260 | set_freqs(Clocks { | ||
| 261 | sys: Hertz(sysclk), | ||
| 262 | pclk1: Hertz(pclk1), | ||
| 263 | pclk2: Hertz(pclk2), | ||
| 264 | |||
| 265 | pclk1_tim: Hertz(pclk1 * timer_mul1), | ||
| 266 | pclk2_tim: Hertz(pclk2 * timer_mul2), | ||
| 267 | |||
| 268 | hclk1: Hertz(hclk), | ||
| 269 | hclk2: Hertz(hclk), | ||
| 270 | hclk3: Hertz(hclk), | ||
| 271 | |||
| 272 | pll1_q: plls.pll48clk.map(Hertz), | ||
| 273 | |||
| 274 | rtc, | ||
| 275 | }); | ||
| 276 | } | ||
| 277 | |||
| 278 | struct PllResults { | ||
| 279 | use_pll: bool, | ||
| 280 | pllsysclk: Option<u32>, | ||
| 281 | pll48clk: Option<u32>, | ||
| 282 | } | ||
| 283 | |||
| 284 | mod max { | ||
| 285 | pub(crate) const HSE_OSC_MIN: u32 = 4_000_000; | ||
| 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 | |||
| 293 | pub(crate) const SYSCLK_MIN: u32 = 12_500_000; | ||
| 294 | pub(crate) const SYSCLK_MAX: u32 = 216_000_000; | ||
| 295 | |||
| 296 | pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN; | ||
| 297 | pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4; | ||
| 298 | |||
| 299 | pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN; | ||
| 300 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | ||
| 301 | |||
| 302 | // USB specification allows +-0.25% | ||
| 303 | pub(crate) const PLL_48_CLK: u32 = 48_000_000; | ||
| 304 | pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; | ||
| 305 | } | ||
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 86136d438..5dbcfea90 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -58,15 +58,6 @@ pub struct Hse { | |||
| 58 | pub mode: HseMode, | 58 | pub mode: HseMode, |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | #[cfg(stm32h7)] | ||
| 62 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 63 | pub enum Lse { | ||
| 64 | /// 32.768 kHz crystal/ceramic oscillator (LSEBYP=0) | ||
| 65 | Oscillator, | ||
| 66 | /// external clock input up to 1MHz (LSEBYP=1) | ||
| 67 | Bypass(Hertz), | ||
| 68 | } | ||
| 69 | |||
| 70 | #[derive(Clone, Copy, Eq, PartialEq)] | 61 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 71 | pub enum Hsi { | 62 | pub enum Hsi { |
| 72 | /// 64Mhz | 63 | /// 64Mhz |
diff --git a/embassy-stm32/src/rcc/l4l5.rs b/embassy-stm32/src/rcc/l4l5.rs index a10169d6c..683b47c05 100644 --- a/embassy-stm32/src/rcc/l4l5.rs +++ b/embassy-stm32/src/rcc/l4l5.rs | |||
| @@ -241,6 +241,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 241 | w.set_ppre1(config.apb1_pre); | 241 | w.set_ppre1(config.apb1_pre); |
| 242 | w.set_ppre2(config.apb2_pre); | 242 | w.set_ppre2(config.apb2_pre); |
| 243 | }); | 243 | }); |
| 244 | while RCC.cfgr().read().sws() != config.mux {} | ||
| 244 | 245 | ||
| 245 | let ahb_freq = sys_clk / config.ahb_pre; | 246 | let ahb_freq = sys_clk / config.ahb_pre; |
| 246 | 247 | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d587a1988..49174b27f 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -13,8 +13,7 @@ pub use mco::*; | |||
| 13 | #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] | 13 | #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] |
| 14 | #[cfg_attr(rcc_f2, path = "f2.rs")] | 14 | #[cfg_attr(rcc_f2, path = "f2.rs")] |
| 15 | #[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")] | 15 | #[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")] |
| 16 | #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] | 16 | #[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")] |
| 17 | #[cfg_attr(rcc_f7, path = "f7.rs")] | ||
| 18 | #[cfg_attr(rcc_c0, path = "c0.rs")] | 17 | #[cfg_attr(rcc_c0, path = "c0.rs")] |
| 19 | #[cfg_attr(rcc_g0, path = "g0.rs")] | 18 | #[cfg_attr(rcc_g0, path = "g0.rs")] |
| 20 | #[cfg_attr(rcc_g4, path = "g4.rs")] | 19 | #[cfg_attr(rcc_g4, path = "g4.rs")] |
diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index ddf8596ae..1747bbf4b 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/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; |
| @@ -32,7 +32,25 @@ async fn net_task(stack: &'static Stack<Device>) -> ! { | |||
| 32 | #[embassy_executor::main] | 32 | #[embassy_executor::main] |
| 33 | async fn main(spawner: Spawner) -> ! { | 33 | async fn main(spawner: Spawner) -> ! { |
| 34 | let mut config = Config::default(); | 34 | let mut config = Config::default(); |
| 35 | config.rcc.sys_ck = Some(mhz(200)); | 35 | { |
| 36 | use embassy_stm32::rcc::*; | ||
| 37 | config.rcc.hse = Some(Hse { | ||
| 38 | freq: Hertz(8_000_000), | ||
| 39 | mode: HseMode::Bypass, | ||
| 40 | }); | ||
| 41 | config.rcc.pll_src = PllSource::HSE; | ||
| 42 | config.rcc.pll = Some(Pll { | ||
| 43 | prediv: PllPreDiv::DIV4, | ||
| 44 | mul: PllMul::MUL180, | ||
| 45 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. | ||
| 46 | divq: None, | ||
| 47 | divr: None, | ||
| 48 | }); | ||
| 49 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 50 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 51 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 52 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 53 | } | ||
| 36 | let p = embassy_stm32::init(config); | 54 | let p = embassy_stm32::init(config); |
| 37 | 55 | ||
| 38 | info!("Hello World!"); | 56 | info!("Hello World!"); |
diff --git a/examples/stm32f4/src/bin/hello.rs b/examples/stm32f4/src/bin/hello.rs index 27ee83aa5..a2a287110 100644 --- a/examples/stm32f4/src/bin/hello.rs +++ b/examples/stm32f4/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/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 6ec7d0fec..37e42384b 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/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::{DataBlock, Sdmmc}; | 7 | use embassy_stm32::sdmmc::{DataBlock, 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 | ||
| @@ -20,8 +20,25 @@ bind_interrupts!(struct Irqs { | |||
| 20 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 22 | let mut config = Config::default(); | 22 | let mut config = Config::default(); |
| 23 | config.rcc.sys_ck = Some(mhz(48)); | 23 | { |
| 24 | config.rcc.pll48 = true; | 24 | use embassy_stm32::rcc::*; |
| 25 | config.rcc.hse = Some(Hse { | ||
| 26 | freq: Hertz(8_000_000), | ||
| 27 | mode: HseMode::Bypass, | ||
| 28 | }); | ||
| 29 | config.rcc.pll_src = PllSource::HSE; | ||
| 30 | config.rcc.pll = Some(Pll { | ||
| 31 | prediv: PllPreDiv::DIV4, | ||
| 32 | mul: PllMul::MUL168, | ||
| 33 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. | ||
| 34 | divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. | ||
| 35 | divr: None, | ||
| 36 | }); | ||
| 37 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 38 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 39 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 40 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 41 | } | ||
| 25 | let p = embassy_stm32::init(config); | 42 | let p = embassy_stm32::init(config); |
| 26 | info!("Hello World!"); | 43 | info!("Hello World!"); |
| 27 | 44 | ||
diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 763e3a9e7..7c0644aeb 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs | |||
| @@ -7,7 +7,7 @@ use embassy_executor::Spawner; | |||
| 7 | use embassy_net::tcp::TcpSocket; | 7 | use embassy_net::tcp::TcpSocket; |
| 8 | use embassy_net::{Stack, StackResources}; | 8 | use embassy_net::{Stack, StackResources}; |
| 9 | use embassy_stm32::rng::{self, Rng}; | 9 | use embassy_stm32::rng::{self, Rng}; |
| 10 | use embassy_stm32::time::mhz; | 10 | use embassy_stm32::time::Hertz; |
| 11 | use embassy_stm32::usb_otg::Driver; | 11 | use embassy_stm32::usb_otg::Driver; |
| 12 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | 12 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; |
| 13 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; | 13 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; |
| @@ -46,9 +46,25 @@ async fn main(spawner: Spawner) { | |||
| 46 | info!("Hello World!"); | 46 | info!("Hello World!"); |
| 47 | 47 | ||
| 48 | let mut config = Config::default(); | 48 | let mut config = Config::default(); |
| 49 | config.rcc.pll48 = true; | 49 | { |
| 50 | config.rcc.sys_ck = Some(mhz(48)); | 50 | use embassy_stm32::rcc::*; |
| 51 | 51 | config.rcc.hse = Some(Hse { | |
| 52 | freq: Hertz(8_000_000), | ||
| 53 | mode: HseMode::Bypass, | ||
| 54 | }); | ||
| 55 | config.rcc.pll_src = PllSource::HSE; | ||
| 56 | config.rcc.pll = Some(Pll { | ||
| 57 | prediv: PllPreDiv::DIV4, | ||
| 58 | mul: PllMul::MUL168, | ||
| 59 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. | ||
| 60 | divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. | ||
| 61 | divr: None, | ||
| 62 | }); | ||
| 63 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 64 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 65 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 66 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 67 | } | ||
| 52 | let p = embassy_stm32::init(config); | 68 | let p = embassy_stm32::init(config); |
| 53 | 69 | ||
| 54 | // Create the driver, from the HAL. | 70 | // Create the driver, from the HAL. |
diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 4ff6452ef..004ff038d 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/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,9 +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.pll48 = true; | 25 | { |
| 26 | config.rcc.sys_ck = Some(mhz(48)); | 26 | use embassy_stm32::rcc::*; |
| 27 | 27 | config.rcc.hse = Some(Hse { | |
| 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::MUL168, | ||
| 35 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. | ||
| 36 | divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 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 | } | ||
| 28 | let p = embassy_stm32::init(config); | 44 | let p = embassy_stm32::init(config); |
| 29 | 45 | ||
| 30 | // Create the driver, from the HAL. | 46 | // Create the driver, from the HAL. |
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..8dde71fb3 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -224,16 +224,44 @@ pub fn config() -> Config { | |||
| 224 | 224 | ||
| 225 | #[cfg(feature = "stm32f429zi")] | 225 | #[cfg(feature = "stm32f429zi")] |
| 226 | { | 226 | { |
| 227 | // TODO: stm32f429zi can do up to 180mhz, but that makes tests fail. | 227 | use embassy_stm32::rcc::*; |
| 228 | // perhaps we have some bug w.r.t overdrive. | 228 | config.rcc.hse = Some(Hse { |
| 229 | config.rcc.sys_ck = Some(Hertz(168_000_000)); | 229 | freq: Hertz(8_000_000), |
| 230 | config.rcc.pclk1 = Some(Hertz(42_000_000)); | 230 | mode: HseMode::Bypass, |
| 231 | config.rcc.pclk2 = Some(Hertz(84_000_000)); | 231 | }); |
| 232 | config.rcc.pll_src = PllSource::HSE; | ||
| 233 | config.rcc.pll = Some(Pll { | ||
| 234 | prediv: PllPreDiv::DIV4, | ||
| 235 | mul: PllMul::MUL180, | ||
| 236 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. | ||
| 237 | divq: None, | ||
| 238 | divr: None, | ||
| 239 | }); | ||
| 240 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 241 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 242 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 243 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 232 | } | 244 | } |
| 233 | 245 | ||
| 234 | #[cfg(feature = "stm32f767zi")] | 246 | #[cfg(feature = "stm32f767zi")] |
| 235 | { | 247 | { |
| 236 | config.rcc.sys_ck = Some(Hertz(200_000_000)); | 248 | use embassy_stm32::rcc::*; |
| 249 | config.rcc.hse = Some(Hse { | ||
| 250 | freq: Hertz(8_000_000), | ||
| 251 | mode: HseMode::Bypass, | ||
| 252 | }); | ||
| 253 | config.rcc.pll_src = PllSource::HSE; | ||
| 254 | config.rcc.pll = Some(Pll { | ||
| 255 | prediv: PllPreDiv::DIV4, | ||
| 256 | mul: PllMul::MUL216, | ||
| 257 | divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz. | ||
| 258 | divq: None, | ||
| 259 | divr: None, | ||
| 260 | }); | ||
| 261 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 262 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 263 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 264 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 237 | } | 265 | } |
| 238 | 266 | ||
| 239 | #[cfg(feature = "stm32h563zi")] | 267 | #[cfg(feature = "stm32h563zi")] |
