diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-09-19 04:22:57 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-09-21 23:47:56 +0200 |
| commit | 83b4c0127337c55c6a445abee6ab5eac4c993f9c (patch) | |
| tree | daa4050b6dd0bf7ecfae113c51ad2b21fe74e914 | |
| parent | e313ca4ae8bb4b7ab5dc4c348a471ccc5745b599 (diff) | |
stm32/rcc: unify h5 and h7.
26 files changed, 1195 insertions, 1529 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1a024e9a9..67f3f526b 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -59,7 +59,7 @@ 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 | atomic-polyfill = "1.0.1" | 61 | atomic-polyfill = "1.0.1" |
| 62 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2dba1f1ddee697e616aff2a4db57a6ffaf1b29b7" } | 62 | stm32-metapac = { git = "https://ci.embassy.dev/jobs/7d2e0e63527f/artifacts/generated.git" } |
| 63 | vcell = "0.1.3" | 63 | vcell = "0.1.3" |
| 64 | bxcan = "0.7.0" | 64 | bxcan = "0.7.0" |
| 65 | nb = "1.0.0" | 65 | nb = "1.0.0" |
| @@ -78,7 +78,7 @@ critical-section = { version = "1.1", features = ["std"] } | |||
| 78 | [build-dependencies] | 78 | [build-dependencies] |
| 79 | proc-macro2 = "1.0.36" | 79 | proc-macro2 = "1.0.36" |
| 80 | quote = "1.0.15" | 80 | quote = "1.0.15" |
| 81 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2dba1f1ddee697e616aff2a4db57a6ffaf1b29b7", default-features = false, features = ["metadata"]} | 81 | stm32-metapac = { git = "https://ci.embassy.dev/jobs/7d2e0e63527f/artifacts/generated.git", default-features = false, features = ["metadata"]} |
| 82 | 82 | ||
| 83 | [features] | 83 | [features] |
| 84 | default = ["rt"] | 84 | default = ["rt"] |
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs new file mode 100644 index 000000000..a4730ed49 --- /dev/null +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -0,0 +1,777 @@ | |||
| 1 | use core::ops::RangeInclusive; | ||
| 2 | |||
| 3 | use crate::pac; | ||
| 4 | use crate::pac::pwr::vals::Vos; | ||
| 5 | #[cfg(stm32h5)] | ||
| 6 | pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; | ||
| 7 | #[cfg(stm32h7)] | ||
| 8 | pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; | ||
| 9 | pub use crate::pac::rcc::vals::Ckpersel as PerClockSource; | ||
| 10 | use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre}; | ||
| 11 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 12 | use crate::rcc::{set_freqs, Clocks}; | ||
| 13 | use crate::time::Hertz; | ||
| 14 | |||
| 15 | /// HSI speed | ||
| 16 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 17 | |||
| 18 | /// CSI speed | ||
| 19 | pub const CSI_FREQ: Hertz = Hertz(4_000_000); | ||
| 20 | |||
| 21 | /// HSI48 speed | ||
| 22 | pub const HSI48_FREQ: Hertz = Hertz(48_000_000); | ||
| 23 | |||
| 24 | /// LSI speed | ||
| 25 | pub const LSI_FREQ: Hertz = Hertz(32_000); | ||
| 26 | |||
| 27 | const VCO_RANGE: RangeInclusive<u32> = 150_000_000..=420_000_000; | ||
| 28 | #[cfg(any(stm32h5, pwr_h7rm0455))] | ||
| 29 | const VCO_WIDE_RANGE: RangeInclusive<u32> = 128_000_000..=560_000_000; | ||
| 30 | #[cfg(pwr_h7rm0468)] | ||
| 31 | const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=836_000_000; | ||
| 32 | #[cfg(any(pwr_h7rm0399, pwr_h7rm0433))] | ||
| 33 | const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=960_000_000; | ||
| 34 | |||
| 35 | pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||
| 36 | |||
| 37 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 38 | pub enum VoltageScale { | ||
| 39 | Scale0, | ||
| 40 | Scale1, | ||
| 41 | Scale2, | ||
| 42 | Scale3, | ||
| 43 | } | ||
| 44 | |||
| 45 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 46 | pub enum HseMode { | ||
| 47 | /// crystal/ceramic oscillator (HSEBYP=0) | ||
| 48 | Oscillator, | ||
| 49 | /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) | ||
| 50 | Bypass, | ||
| 51 | /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) | ||
| 52 | #[cfg(any(rcc_h5, rcc_h50))] | ||
| 53 | BypassDigital, | ||
| 54 | } | ||
| 55 | |||
| 56 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 57 | pub struct Hse { | ||
| 58 | /// HSE frequency. | ||
| 59 | pub freq: Hertz, | ||
| 60 | /// HSE mode. | ||
| 61 | pub mode: HseMode, | ||
| 62 | } | ||
| 63 | |||
| 64 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 65 | pub enum Hsi { | ||
| 66 | /// 64Mhz | ||
| 67 | Mhz64, | ||
| 68 | /// 32Mhz (divided by 2) | ||
| 69 | Mhz32, | ||
| 70 | /// 16Mhz (divided by 4) | ||
| 71 | Mhz16, | ||
| 72 | /// 8Mhz (divided by 8) | ||
| 73 | Mhz8, | ||
| 74 | } | ||
| 75 | |||
| 76 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 77 | pub enum Sysclk { | ||
| 78 | /// HSI selected as sysclk | ||
| 79 | HSI, | ||
| 80 | /// HSE selected as sysclk | ||
| 81 | HSE, | ||
| 82 | /// CSI selected as sysclk | ||
| 83 | CSI, | ||
| 84 | /// PLL1_P selected as sysclk | ||
| 85 | Pll1P, | ||
| 86 | } | ||
| 87 | |||
| 88 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 89 | pub enum PllSource { | ||
| 90 | Hsi, | ||
| 91 | Csi, | ||
| 92 | Hse, | ||
| 93 | } | ||
| 94 | |||
| 95 | #[derive(Clone, Copy)] | ||
| 96 | pub struct Pll { | ||
| 97 | /// Source clock selection. | ||
| 98 | #[cfg(stm32h5)] | ||
| 99 | pub source: PllSource, | ||
| 100 | |||
| 101 | /// PLL pre-divider (DIVM). Must be between 1 and 63. | ||
| 102 | pub prediv: u8, | ||
| 103 | |||
| 104 | /// PLL multiplication factor. Must be between 4 and 512. | ||
| 105 | pub mul: u16, | ||
| 106 | |||
| 107 | /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. | ||
| 108 | /// On PLL1, it must be even (in particular, it cannot be 1.) | ||
| 109 | pub divp: Option<u16>, | ||
| 110 | /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. | ||
| 111 | pub divq: Option<u16>, | ||
| 112 | /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. | ||
| 113 | pub divr: Option<u16>, | ||
| 114 | } | ||
| 115 | |||
| 116 | fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz { | ||
| 117 | match (tim, apb) { | ||
| 118 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk, | ||
| 119 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk, | ||
| 120 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32, | ||
| 121 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32, | ||
| 122 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32, | ||
| 123 | |||
| 124 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk, | ||
| 125 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk, | ||
| 126 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk, | ||
| 127 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32, | ||
| 128 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32, | ||
| 129 | |||
| 130 | _ => unreachable!(), | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Timer prescaler | ||
| 135 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 136 | pub enum TimerPrescaler { | ||
| 137 | /// The timers kernel clock is equal to hclk if PPREx corresponds to a | ||
| 138 | /// division by 1 or 2, else it is equal to 2*pclk | ||
| 139 | DefaultX2, | ||
| 140 | |||
| 141 | /// The timers kernel clock is equal to hclk if PPREx corresponds to a | ||
| 142 | /// division by 1, 2 or 4, else it is equal to 4*pclk | ||
| 143 | DefaultX4, | ||
| 144 | } | ||
| 145 | |||
| 146 | impl From<TimerPrescaler> for Timpre { | ||
| 147 | fn from(value: TimerPrescaler) -> Self { | ||
| 148 | match value { | ||
| 149 | TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, | ||
| 150 | TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Configuration of the core clocks | ||
| 156 | #[non_exhaustive] | ||
| 157 | pub struct Config { | ||
| 158 | pub hsi: Option<Hsi>, | ||
| 159 | pub hse: Option<Hse>, | ||
| 160 | pub csi: bool, | ||
| 161 | pub hsi48: bool, | ||
| 162 | pub sys: Sysclk, | ||
| 163 | |||
| 164 | #[cfg(stm32h7)] | ||
| 165 | pub pll_src: PllSource, | ||
| 166 | |||
| 167 | pub pll1: Option<Pll>, | ||
| 168 | pub pll2: Option<Pll>, | ||
| 169 | #[cfg(any(rcc_h5, stm32h7))] | ||
| 170 | pub pll3: Option<Pll>, | ||
| 171 | |||
| 172 | pub d1c_pre: AHBPrescaler, | ||
| 173 | pub ahb_pre: AHBPrescaler, | ||
| 174 | pub apb1_pre: APBPrescaler, | ||
| 175 | pub apb2_pre: APBPrescaler, | ||
| 176 | pub apb3_pre: APBPrescaler, | ||
| 177 | #[cfg(stm32h7)] | ||
| 178 | pub apb4_pre: APBPrescaler, | ||
| 179 | |||
| 180 | pub per_clock_source: PerClockSource, | ||
| 181 | pub adc_clock_source: AdcClockSource, | ||
| 182 | pub timer_prescaler: TimerPrescaler, | ||
| 183 | pub voltage_scale: VoltageScale, | ||
| 184 | } | ||
| 185 | |||
| 186 | impl Default for Config { | ||
| 187 | fn default() -> Self { | ||
| 188 | Self { | ||
| 189 | hsi: Some(Hsi::Mhz64), | ||
| 190 | hse: None, | ||
| 191 | csi: false, | ||
| 192 | hsi48: false, | ||
| 193 | sys: Sysclk::HSI, | ||
| 194 | #[cfg(stm32h7)] | ||
| 195 | pll_src: PllSource::Hsi, | ||
| 196 | pll1: None, | ||
| 197 | pll2: None, | ||
| 198 | #[cfg(any(rcc_h5, stm32h7))] | ||
| 199 | pll3: None, | ||
| 200 | |||
| 201 | d1c_pre: AHBPrescaler::DIV1, | ||
| 202 | ahb_pre: AHBPrescaler::DIV1, | ||
| 203 | apb1_pre: APBPrescaler::DIV1, | ||
| 204 | apb2_pre: APBPrescaler::DIV1, | ||
| 205 | apb3_pre: APBPrescaler::DIV1, | ||
| 206 | #[cfg(stm32h7)] | ||
| 207 | apb4_pre: APBPrescaler::DIV1, | ||
| 208 | |||
| 209 | per_clock_source: PerClockSource::HSI, | ||
| 210 | adc_clock_source: AdcClockSource::from_bits(0), // PLL2_P on H7, HCLK on H5 | ||
| 211 | timer_prescaler: TimerPrescaler::DefaultX2, | ||
| 212 | voltage_scale: VoltageScale::Scale0, | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | pub(crate) unsafe fn init(config: Config) { | ||
| 218 | #[cfg(stm32h7)] | ||
| 219 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | ||
| 220 | #[cfg(stm32h5)] | ||
| 221 | RCC.apb3enr().modify(|w| w.set_sbsen(true)); | ||
| 222 | |||
| 223 | // NB. The lower bytes of CR3 can only be written once after | ||
| 224 | // POR, and must be written with a valid combination. Refer to | ||
| 225 | // RM0433 Rev 7 6.8.4. This is partially enforced by dropping | ||
| 226 | // `self` at the end of this method, but of course we cannot | ||
| 227 | // know what happened between the previous POR and here. | ||
| 228 | #[cfg(pwr_h7rm0433)] | ||
| 229 | PWR.cr3().modify(|w| { | ||
| 230 | w.set_scuen(true); | ||
| 231 | w.set_ldoen(true); | ||
| 232 | w.set_bypass(false); | ||
| 233 | }); | ||
| 234 | |||
| 235 | #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] | ||
| 236 | PWR.cr3().modify(|w| { | ||
| 237 | // hardcode "Direct SPMS" for now, this is what works on nucleos with the | ||
| 238 | // default solderbridge configuration. | ||
| 239 | w.set_sden(true); | ||
| 240 | w.set_ldoen(false); | ||
| 241 | }); | ||
| 242 | |||
| 243 | // Validate the supply configuration. If you are stuck here, it is | ||
| 244 | // because the voltages on your board do not match those specified | ||
| 245 | // in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset | ||
| 246 | // VOS = Scale 3, so check that the voltage on the VCAP pins = | ||
| 247 | // 1.0V. | ||
| 248 | #[cfg(any(pwr_h7rm0433, pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] | ||
| 249 | while !PWR.csr1().read().actvosrdy() {} | ||
| 250 | |||
| 251 | // Configure voltage scale. | ||
| 252 | #[cfg(any(pwr_h5, pwr_h50))] | ||
| 253 | { | ||
| 254 | PWR.voscr().modify(|w| { | ||
| 255 | w.set_vos(match config.voltage_scale { | ||
| 256 | VoltageScale::Scale0 => Vos::SCALE0, | ||
| 257 | VoltageScale::Scale1 => Vos::SCALE1, | ||
| 258 | VoltageScale::Scale2 => Vos::SCALE2, | ||
| 259 | VoltageScale::Scale3 => Vos::SCALE3, | ||
| 260 | }) | ||
| 261 | }); | ||
| 262 | while !PWR.vossr().read().vosrdy() {} | ||
| 263 | } | ||
| 264 | |||
| 265 | #[cfg(syscfg_h7)] | ||
| 266 | { | ||
| 267 | // in chips without the overdrive bit, we can go from any scale to any scale directly. | ||
| 268 | PWR.d3cr().modify(|w| { | ||
| 269 | w.set_vos(match config.voltage_scale { | ||
| 270 | VoltageScale::Scale0 => Vos::SCALE0, | ||
| 271 | VoltageScale::Scale1 => Vos::SCALE1, | ||
| 272 | VoltageScale::Scale2 => Vos::SCALE2, | ||
| 273 | VoltageScale::Scale3 => Vos::SCALE3, | ||
| 274 | }) | ||
| 275 | }); | ||
| 276 | while !PWR.d3cr().read().vosrdy() {} | ||
| 277 | } | ||
| 278 | |||
| 279 | #[cfg(syscfg_h7od)] | ||
| 280 | { | ||
| 281 | match config.voltage_scale { | ||
| 282 | VoltageScale::Scale0 => { | ||
| 283 | // to go to scale0, we must go to Scale1 first... | ||
| 284 | PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1)); | ||
| 285 | while !PWR.d3cr().read().vosrdy() {} | ||
| 286 | |||
| 287 | // Then enable overdrive. | ||
| 288 | critical_section::with(|_| pac::SYSCFG.pwrcr().modify(|w| w.set_oden(1))); | ||
| 289 | while !PWR.d3cr().read().vosrdy() {} | ||
| 290 | } | ||
| 291 | _ => { | ||
| 292 | // for all other scales, we can go directly. | ||
| 293 | PWR.d3cr().modify(|w| { | ||
| 294 | w.set_vos(match config.voltage_scale { | ||
| 295 | VoltageScale::Scale0 => unreachable!(), | ||
| 296 | VoltageScale::Scale1 => Vos::SCALE1, | ||
| 297 | VoltageScale::Scale2 => Vos::SCALE2, | ||
| 298 | VoltageScale::Scale3 => Vos::SCALE3, | ||
| 299 | }) | ||
| 300 | }); | ||
| 301 | while !PWR.d3cr().read().vosrdy() {} | ||
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | // Configure HSI | ||
| 307 | let hsi = match config.hsi { | ||
| 308 | None => { | ||
| 309 | RCC.cr().modify(|w| w.set_hsion(false)); | ||
| 310 | None | ||
| 311 | } | ||
| 312 | Some(hsi) => { | ||
| 313 | let (freq, hsidiv) = match hsi { | ||
| 314 | Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1), | ||
| 315 | Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2), | ||
| 316 | Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4), | ||
| 317 | Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8), | ||
| 318 | }; | ||
| 319 | RCC.cr().modify(|w| { | ||
| 320 | w.set_hsidiv(hsidiv); | ||
| 321 | w.set_hsion(true); | ||
| 322 | }); | ||
| 323 | while !RCC.cr().read().hsirdy() {} | ||
| 324 | Some(freq) | ||
| 325 | } | ||
| 326 | }; | ||
| 327 | |||
| 328 | // Configure HSE | ||
| 329 | let hse = match config.hse { | ||
| 330 | None => { | ||
| 331 | RCC.cr().modify(|w| w.set_hseon(false)); | ||
| 332 | None | ||
| 333 | } | ||
| 334 | Some(hse) => { | ||
| 335 | RCC.cr().modify(|w| { | ||
| 336 | w.set_hsebyp(hse.mode != HseMode::Oscillator); | ||
| 337 | #[cfg(any(rcc_h5, rcc_h50))] | ||
| 338 | w.set_hseext(match hse.mode { | ||
| 339 | HseMode::Oscillator | HseMode::Bypass => pac::rcc::vals::Hseext::ANALOG, | ||
| 340 | HseMode::BypassDigital => pac::rcc::vals::Hseext::DIGITAL, | ||
| 341 | }); | ||
| 342 | }); | ||
| 343 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 344 | while !RCC.cr().read().hserdy() {} | ||
| 345 | Some(hse.freq) | ||
| 346 | } | ||
| 347 | }; | ||
| 348 | |||
| 349 | // Configure HSI48. | ||
| 350 | RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); | ||
| 351 | let _hsi48 = match config.hsi48 { | ||
| 352 | false => None, | ||
| 353 | true => { | ||
| 354 | while !RCC.cr().read().hsi48rdy() {} | ||
| 355 | Some(CSI_FREQ) | ||
| 356 | } | ||
| 357 | }; | ||
| 358 | |||
| 359 | // Configure CSI. | ||
| 360 | RCC.cr().modify(|w| w.set_csion(config.csi)); | ||
| 361 | let csi = match config.csi { | ||
| 362 | false => None, | ||
| 363 | true => { | ||
| 364 | while !RCC.cr().read().csirdy() {} | ||
| 365 | Some(CSI_FREQ) | ||
| 366 | } | ||
| 367 | }; | ||
| 368 | |||
| 369 | // Configure PLLs. | ||
| 370 | let pll_input = PllInput { | ||
| 371 | csi, | ||
| 372 | hse, | ||
| 373 | hsi, | ||
| 374 | #[cfg(stm32h7)] | ||
| 375 | source: config.pll_src, | ||
| 376 | }; | ||
| 377 | let pll1 = init_pll(0, config.pll1, &pll_input); | ||
| 378 | let pll2 = init_pll(1, config.pll2, &pll_input); | ||
| 379 | #[cfg(any(rcc_h5, stm32h7))] | ||
| 380 | let _pll3 = init_pll(2, config.pll3, &pll_input); | ||
| 381 | |||
| 382 | // Configure sysclk | ||
| 383 | let (sys, sw) = match config.sys { | ||
| 384 | Sysclk::HSI => (unwrap!(hsi), Sw::HSI), | ||
| 385 | Sysclk::HSE => (unwrap!(hse), Sw::HSE), | ||
| 386 | Sysclk::CSI => (unwrap!(csi), Sw::CSI), | ||
| 387 | Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1), | ||
| 388 | }; | ||
| 389 | |||
| 390 | // Check limits. | ||
| 391 | #[cfg(stm32h5)] | ||
| 392 | let (hclk_max, pclk_max) = match config.voltage_scale { | ||
| 393 | VoltageScale::Scale0 => (Hertz(250_000_000), Hertz(250_000_000)), | ||
| 394 | VoltageScale::Scale1 => (Hertz(200_000_000), Hertz(200_000_000)), | ||
| 395 | VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)), | ||
| 396 | VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)), | ||
| 397 | }; | ||
| 398 | #[cfg(stm32h7)] | ||
| 399 | let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale { | ||
| 400 | VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)), | ||
| 401 | VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)), | ||
| 402 | VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)), | ||
| 403 | VoltageScale::Scale3 => (Hertz(200_000_000), Hertz(100_000_000), Hertz(50_000_000)), | ||
| 404 | }; | ||
| 405 | |||
| 406 | #[cfg(stm32h7)] | ||
| 407 | let hclk = { | ||
| 408 | let d1cpre_clk = sys / config.d1c_pre; | ||
| 409 | assert!(d1cpre_clk <= d1cpre_clk_max); | ||
| 410 | sys / config.ahb_pre | ||
| 411 | }; | ||
| 412 | #[cfg(stm32h5)] | ||
| 413 | let hclk = sys / config.ahb_pre; | ||
| 414 | assert!(hclk <= hclk_max); | ||
| 415 | |||
| 416 | let apb1 = hclk / config.apb1_pre; | ||
| 417 | let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler); | ||
| 418 | assert!(apb1 <= pclk_max); | ||
| 419 | let apb2 = hclk / config.apb2_pre; | ||
| 420 | let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler); | ||
| 421 | assert!(apb2 <= pclk_max); | ||
| 422 | let apb3 = hclk / config.apb3_pre; | ||
| 423 | assert!(apb3 <= pclk_max); | ||
| 424 | #[cfg(stm32h7)] | ||
| 425 | let apb4 = hclk / config.apb4_pre; | ||
| 426 | #[cfg(stm32h7)] | ||
| 427 | assert!(apb4 <= pclk_max); | ||
| 428 | |||
| 429 | let _per_ck = match config.per_clock_source { | ||
| 430 | Ckpersel::HSI => hsi, | ||
| 431 | Ckpersel::CSI => csi, | ||
| 432 | Ckpersel::HSE => hse, | ||
| 433 | _ => unreachable!(), | ||
| 434 | }; | ||
| 435 | |||
| 436 | #[cfg(stm32h7)] | ||
| 437 | let adc = match config.adc_clock_source { | ||
| 438 | AdcClockSource::PLL2_P => pll2.p, | ||
| 439 | AdcClockSource::PLL3_R => _pll3.r, | ||
| 440 | AdcClockSource::PER => _per_ck, | ||
| 441 | _ => unreachable!(), | ||
| 442 | }; | ||
| 443 | #[cfg(stm32h5)] | ||
| 444 | let adc = match config.adc_clock_source { | ||
| 445 | AdcClockSource::HCLK => Some(hclk), | ||
| 446 | AdcClockSource::SYSCLK => Some(sys), | ||
| 447 | AdcClockSource::PLL2_R => pll2.r, | ||
| 448 | AdcClockSource::HSE => hse, | ||
| 449 | AdcClockSource::HSI_KER => hsi, | ||
| 450 | AdcClockSource::CSI_KER => csi, | ||
| 451 | _ => unreachable!(), | ||
| 452 | }; | ||
| 453 | |||
| 454 | flash_setup(hclk, config.voltage_scale); | ||
| 455 | |||
| 456 | #[cfg(stm32h7)] | ||
| 457 | { | ||
| 458 | RCC.d1cfgr().modify(|w| { | ||
| 459 | w.set_d1cpre(config.d1c_pre); | ||
| 460 | w.set_d1ppre(config.apb3_pre); | ||
| 461 | w.set_hpre(config.ahb_pre); | ||
| 462 | }); | ||
| 463 | // Ensure core prescaler value is valid before future lower core voltage | ||
| 464 | while RCC.d1cfgr().read().d1cpre() != config.d1c_pre {} | ||
| 465 | |||
| 466 | RCC.d2cfgr().modify(|w| { | ||
| 467 | w.set_d2ppre1(config.apb1_pre); | ||
| 468 | w.set_d2ppre2(config.apb2_pre); | ||
| 469 | }); | ||
| 470 | RCC.d3cfgr().modify(|w| { | ||
| 471 | w.set_d3ppre(config.apb4_pre); | ||
| 472 | }); | ||
| 473 | |||
| 474 | RCC.d1ccipr().modify(|w| { | ||
| 475 | w.set_ckpersel(config.per_clock_source); | ||
| 476 | }); | ||
| 477 | RCC.d3ccipr().modify(|w| { | ||
| 478 | w.set_adcsel(config.adc_clock_source); | ||
| 479 | }); | ||
| 480 | } | ||
| 481 | #[cfg(stm32h5)] | ||
| 482 | { | ||
| 483 | // Set hpre | ||
| 484 | RCC.cfgr2().modify(|w| w.set_hpre(config.ahb_pre)); | ||
| 485 | while RCC.cfgr2().read().hpre() != config.ahb_pre {} | ||
| 486 | |||
| 487 | // set ppre | ||
| 488 | RCC.cfgr2().modify(|w| { | ||
| 489 | w.set_ppre1(config.apb1_pre); | ||
| 490 | w.set_ppre2(config.apb2_pre); | ||
| 491 | w.set_ppre3(config.apb3_pre); | ||
| 492 | }); | ||
| 493 | |||
| 494 | RCC.ccipr5().modify(|w| { | ||
| 495 | w.set_ckpersel(config.per_clock_source); | ||
| 496 | w.set_adcdacsel(config.adc_clock_source) | ||
| 497 | }); | ||
| 498 | } | ||
| 499 | |||
| 500 | RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into())); | ||
| 501 | |||
| 502 | RCC.cfgr().modify(|w| w.set_sw(sw)); | ||
| 503 | while RCC.cfgr().read().sws() != sw {} | ||
| 504 | |||
| 505 | // IO compensation cell - Requires CSI clock and SYSCFG | ||
| 506 | #[cfg(stm32h7)] // TODO h5 | ||
| 507 | if csi.is_some() { | ||
| 508 | // Enable the compensation cell, using back-bias voltage code | ||
| 509 | // provide by the cell. | ||
| 510 | critical_section::with(|_| { | ||
| 511 | pac::SYSCFG.cccsr().modify(|w| { | ||
| 512 | w.set_en(true); | ||
| 513 | w.set_cs(false); | ||
| 514 | w.set_hslv(false); | ||
| 515 | }) | ||
| 516 | }); | ||
| 517 | while !pac::SYSCFG.cccsr().read().ready() {} | ||
| 518 | } | ||
| 519 | |||
| 520 | set_freqs(Clocks { | ||
| 521 | sys, | ||
| 522 | ahb1: hclk, | ||
| 523 | ahb2: hclk, | ||
| 524 | ahb3: hclk, | ||
| 525 | ahb4: hclk, | ||
| 526 | apb1, | ||
| 527 | apb2, | ||
| 528 | apb3, | ||
| 529 | #[cfg(stm32h7)] | ||
| 530 | apb4, | ||
| 531 | apb1_tim, | ||
| 532 | apb2_tim, | ||
| 533 | adc: adc, | ||
| 534 | }); | ||
| 535 | } | ||
| 536 | |||
| 537 | struct PllInput { | ||
| 538 | hsi: Option<Hertz>, | ||
| 539 | hse: Option<Hertz>, | ||
| 540 | csi: Option<Hertz>, | ||
| 541 | #[cfg(stm32h7)] | ||
| 542 | source: PllSource, | ||
| 543 | } | ||
| 544 | |||
| 545 | struct PllOutput { | ||
| 546 | p: Option<Hertz>, | ||
| 547 | #[allow(dead_code)] | ||
| 548 | q: Option<Hertz>, | ||
| 549 | #[allow(dead_code)] | ||
| 550 | r: Option<Hertz>, | ||
| 551 | } | ||
| 552 | |||
| 553 | fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput { | ||
| 554 | let Some(config) = config else { | ||
| 555 | // Stop PLL | ||
| 556 | RCC.cr().modify(|w| w.set_pllon(num, false)); | ||
| 557 | while RCC.cr().read().pllrdy(num) {} | ||
| 558 | |||
| 559 | // "To save power when PLL1 is not used, the value of PLL1M must be set to 0."" | ||
| 560 | #[cfg(stm32h7)] | ||
| 561 | RCC.pllckselr().write(|w| w.set_divm(num, 0)); | ||
| 562 | #[cfg(stm32h5)] | ||
| 563 | RCC.pllcfgr(num).write(|w| w.set_divm(0)); | ||
| 564 | |||
| 565 | return PllOutput { | ||
| 566 | p: None, | ||
| 567 | q: None, | ||
| 568 | r: None, | ||
| 569 | }; | ||
| 570 | }; | ||
| 571 | |||
| 572 | assert!(1 <= config.prediv && config.prediv <= 63); | ||
| 573 | assert!(4 <= config.mul && config.mul <= 512); | ||
| 574 | |||
| 575 | #[cfg(stm32h5)] | ||
| 576 | let source = config.source; | ||
| 577 | #[cfg(stm32h7)] | ||
| 578 | let source = input.source; | ||
| 579 | |||
| 580 | let (in_clk, src) = match source { | ||
| 581 | PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI), | ||
| 582 | PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE), | ||
| 583 | PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI), | ||
| 584 | }; | ||
| 585 | |||
| 586 | let ref_clk = in_clk / config.prediv as u32; | ||
| 587 | |||
| 588 | let ref_range = match ref_clk.0 { | ||
| 589 | ..=1_999_999 => Pllrge::RANGE1, | ||
| 590 | ..=3_999_999 => Pllrge::RANGE2, | ||
| 591 | ..=7_999_999 => Pllrge::RANGE4, | ||
| 592 | ..=16_000_000 => Pllrge::RANGE8, | ||
| 593 | x => panic!("pll ref_clk out of range: {} mhz", x), | ||
| 594 | }; | ||
| 595 | |||
| 596 | // The smaller range (150 to 420 MHz) must | ||
| 597 | // be chosen when the reference clock frequency is lower than 2 MHz. | ||
| 598 | let wide_allowed = ref_range != Pllrge::RANGE1; | ||
| 599 | |||
| 600 | let vco_clk = ref_clk * config.mul; | ||
| 601 | let vco_range = if VCO_RANGE.contains(&vco_clk.0) { | ||
| 602 | Pllvcosel::MEDIUMVCO | ||
| 603 | } else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk.0) { | ||
| 604 | Pllvcosel::WIDEVCO | ||
| 605 | } else { | ||
| 606 | panic!("pll vco_clk out of range: {} mhz", vco_clk.0) | ||
| 607 | }; | ||
| 608 | |||
| 609 | let p = config.divp.map(|div| { | ||
| 610 | assert!(1 <= div && div <= 128); | ||
| 611 | if num == 0 { | ||
| 612 | // on PLL1, DIVP must be even. | ||
| 613 | assert!(div % 2 == 0); | ||
| 614 | } | ||
| 615 | |||
| 616 | vco_clk / div | ||
| 617 | }); | ||
| 618 | let q = config.divq.map(|div| { | ||
| 619 | assert!(1 <= div && div <= 128); | ||
| 620 | vco_clk / div | ||
| 621 | }); | ||
| 622 | let r = config.divr.map(|div| { | ||
| 623 | assert!(1 <= div && div <= 128); | ||
| 624 | vco_clk / div | ||
| 625 | }); | ||
| 626 | |||
| 627 | #[cfg(stm32h5)] | ||
| 628 | RCC.pllcfgr(num).write(|w| { | ||
| 629 | w.set_pllsrc(src); | ||
| 630 | w.set_divm(config.prediv); | ||
| 631 | w.set_pllvcosel(vco_range); | ||
| 632 | w.set_pllrge(ref_range); | ||
| 633 | w.set_pllfracen(false); | ||
| 634 | w.set_pllpen(p.is_some()); | ||
| 635 | w.set_pllqen(q.is_some()); | ||
| 636 | w.set_pllren(r.is_some()); | ||
| 637 | }); | ||
| 638 | |||
| 639 | #[cfg(stm32h7)] | ||
| 640 | { | ||
| 641 | RCC.pllckselr().modify(|w| { | ||
| 642 | w.set_divm(num, config.prediv); | ||
| 643 | w.set_pllsrc(src); | ||
| 644 | }); | ||
| 645 | RCC.pllcfgr().modify(|w| { | ||
| 646 | w.set_pllvcosel(num, vco_range); | ||
| 647 | w.set_pllrge(num, ref_range); | ||
| 648 | w.set_pllfracen(num, false); | ||
| 649 | w.set_divpen(num, p.is_some()); | ||
| 650 | w.set_divqen(num, q.is_some()); | ||
| 651 | w.set_divren(num, r.is_some()); | ||
| 652 | }); | ||
| 653 | } | ||
| 654 | |||
| 655 | RCC.plldivr(num).write(|w| { | ||
| 656 | w.set_plln(config.mul - 1); | ||
| 657 | w.set_pllp((config.divp.unwrap_or(1) - 1) as u8); | ||
| 658 | w.set_pllq((config.divq.unwrap_or(1) - 1) as u8); | ||
| 659 | w.set_pllr((config.divr.unwrap_or(1) - 1) as u8); | ||
| 660 | }); | ||
| 661 | |||
| 662 | RCC.cr().modify(|w| w.set_pllon(num, true)); | ||
| 663 | while !RCC.cr().read().pllrdy(num) {} | ||
| 664 | |||
| 665 | PllOutput { p, q, r } | ||
| 666 | } | ||
| 667 | |||
| 668 | fn flash_setup(clk: Hertz, vos: VoltageScale) { | ||
| 669 | // RM0481 Rev 1, table 37 | ||
| 670 | // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0 | ||
| 671 | // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz | ||
| 672 | // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz | ||
| 673 | // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz | ||
| 674 | // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz | ||
| 675 | // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz | ||
| 676 | // 5 2 170 to 200 MHz 210 to 250 MHz | ||
| 677 | #[cfg(stm32h5)] | ||
| 678 | let (latency, wrhighfreq) = match (vos, clk.0) { | ||
| 679 | (VoltageScale::Scale0, ..=42_000_000) => (0, 0), | ||
| 680 | (VoltageScale::Scale0, ..=84_000_000) => (1, 0), | ||
| 681 | (VoltageScale::Scale0, ..=126_000_000) => (2, 1), | ||
| 682 | (VoltageScale::Scale0, ..=168_000_000) => (3, 1), | ||
| 683 | (VoltageScale::Scale0, ..=210_000_000) => (4, 2), | ||
| 684 | (VoltageScale::Scale0, ..=250_000_000) => (5, 2), | ||
| 685 | |||
| 686 | (VoltageScale::Scale1, ..=34_000_000) => (0, 0), | ||
| 687 | (VoltageScale::Scale1, ..=68_000_000) => (1, 0), | ||
| 688 | (VoltageScale::Scale1, ..=102_000_000) => (2, 1), | ||
| 689 | (VoltageScale::Scale1, ..=136_000_000) => (3, 1), | ||
| 690 | (VoltageScale::Scale1, ..=170_000_000) => (4, 2), | ||
| 691 | (VoltageScale::Scale1, ..=200_000_000) => (5, 2), | ||
| 692 | |||
| 693 | (VoltageScale::Scale2, ..=30_000_000) => (0, 0), | ||
| 694 | (VoltageScale::Scale2, ..=60_000_000) => (1, 0), | ||
| 695 | (VoltageScale::Scale2, ..=90_000_000) => (2, 1), | ||
| 696 | (VoltageScale::Scale2, ..=120_000_000) => (3, 1), | ||
| 697 | (VoltageScale::Scale2, ..=150_000_000) => (4, 2), | ||
| 698 | |||
| 699 | (VoltageScale::Scale3, ..=20_000_000) => (0, 0), | ||
| 700 | (VoltageScale::Scale3, ..=40_000_000) => (1, 0), | ||
| 701 | (VoltageScale::Scale3, ..=60_000_000) => (2, 1), | ||
| 702 | (VoltageScale::Scale3, ..=80_000_000) => (3, 1), | ||
| 703 | (VoltageScale::Scale3, ..=100_000_000) => (4, 2), | ||
| 704 | |||
| 705 | _ => unreachable!(), | ||
| 706 | }; | ||
| 707 | |||
| 708 | #[cfg(flash_h7)] | ||
| 709 | let (latency, wrhighfreq) = match (vos, clk.0) { | ||
| 710 | // VOS 0 range VCORE 1.26V - 1.40V | ||
| 711 | (VoltageScale::Scale0, ..=70_000_000) => (0, 0), | ||
| 712 | (VoltageScale::Scale0, ..=140_000_000) => (1, 1), | ||
| 713 | (VoltageScale::Scale0, ..=185_000_000) => (2, 1), | ||
| 714 | (VoltageScale::Scale0, ..=210_000_000) => (2, 2), | ||
| 715 | (VoltageScale::Scale0, ..=225_000_000) => (3, 2), | ||
| 716 | (VoltageScale::Scale0, ..=240_000_000) => (4, 2), | ||
| 717 | // VOS 1 range VCORE 1.15V - 1.26V | ||
| 718 | (VoltageScale::Scale1, ..=70_000_000) => (0, 0), | ||
| 719 | (VoltageScale::Scale1, ..=140_000_000) => (1, 1), | ||
| 720 | (VoltageScale::Scale1, ..=185_000_000) => (2, 1), | ||
| 721 | (VoltageScale::Scale1, ..=210_000_000) => (2, 2), | ||
| 722 | (VoltageScale::Scale1, ..=225_000_000) => (3, 2), | ||
| 723 | // VOS 2 range VCORE 1.05V - 1.15V | ||
| 724 | (VoltageScale::Scale2, ..=55_000_000) => (0, 0), | ||
| 725 | (VoltageScale::Scale2, ..=110_000_000) => (1, 1), | ||
| 726 | (VoltageScale::Scale2, ..=165_000_000) => (2, 1), | ||
| 727 | (VoltageScale::Scale2, ..=224_000_000) => (3, 2), | ||
| 728 | // VOS 3 range VCORE 0.95V - 1.05V | ||
| 729 | (VoltageScale::Scale3, ..=45_000_000) => (0, 0), | ||
| 730 | (VoltageScale::Scale3, ..=90_000_000) => (1, 1), | ||
| 731 | (VoltageScale::Scale3, ..=135_000_000) => (2, 1), | ||
| 732 | (VoltageScale::Scale3, ..=180_000_000) => (3, 2), | ||
| 733 | (VoltageScale::Scale3, ..=224_000_000) => (4, 2), | ||
| 734 | _ => unreachable!(), | ||
| 735 | }; | ||
| 736 | |||
| 737 | // See RM0455 Rev 10 Table 16. FLASH recommended number of wait | ||
| 738 | // states and programming delay | ||
| 739 | #[cfg(flash_h7ab)] | ||
| 740 | let (latency, wrhighfreq) = match (vos, clk.0) { | ||
| 741 | // VOS 0 range VCORE 1.25V - 1.35V | ||
| 742 | (VoltageScale::Scale0, ..=42_000_000) => (0, 0), | ||
| 743 | (VoltageScale::Scale0, ..=84_000_000) => (1, 0), | ||
| 744 | (VoltageScale::Scale0, ..=126_000_000) => (2, 1), | ||
| 745 | (VoltageScale::Scale0, ..=168_000_000) => (3, 1), | ||
| 746 | (VoltageScale::Scale0, ..=210_000_000) => (4, 2), | ||
| 747 | (VoltageScale::Scale0, ..=252_000_000) => (5, 2), | ||
| 748 | (VoltageScale::Scale0, ..=280_000_000) => (6, 3), | ||
| 749 | // VOS 1 range VCORE 1.15V - 1.25V | ||
| 750 | (VoltageScale::Scale1, ..=38_000_000) => (0, 0), | ||
| 751 | (VoltageScale::Scale1, ..=76_000_000) => (1, 0), | ||
| 752 | (VoltageScale::Scale1, ..=114_000_000) => (2, 1), | ||
| 753 | (VoltageScale::Scale1, ..=152_000_000) => (3, 1), | ||
| 754 | (VoltageScale::Scale1, ..=190_000_000) => (4, 2), | ||
| 755 | (VoltageScale::Scale1, ..=225_000_000) => (5, 2), | ||
| 756 | // VOS 2 range VCORE 1.05V - 1.15V | ||
| 757 | (VoltageScale::Scale2, ..=34) => (0, 0), | ||
| 758 | (VoltageScale::Scale2, ..=68) => (1, 0), | ||
| 759 | (VoltageScale::Scale2, ..=102) => (2, 1), | ||
| 760 | (VoltageScale::Scale2, ..=136) => (3, 1), | ||
| 761 | (VoltageScale::Scale2, ..=160) => (4, 2), | ||
| 762 | // VOS 3 range VCORE 0.95V - 1.05V | ||
| 763 | (VoltageScale::Scale3, ..=22) => (0, 0), | ||
| 764 | (VoltageScale::Scale3, ..=44) => (1, 0), | ||
| 765 | (VoltageScale::Scale3, ..=66) => (2, 1), | ||
| 766 | (VoltageScale::Scale3, ..=88) => (3, 1), | ||
| 767 | _ => unreachable!(), | ||
| 768 | }; | ||
| 769 | |||
| 770 | debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); | ||
| 771 | |||
| 772 | FLASH.acr().write(|w| { | ||
| 773 | w.set_wrhighfreq(wrhighfreq); | ||
| 774 | w.set_latency(latency); | ||
| 775 | }); | ||
| 776 | while FLASH.acr().read().latency() != latency {} | ||
| 777 | } | ||
diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs deleted file mode 100644 index 15f28c5dc..000000000 --- a/embassy-stm32/src/rcc/h5.rs +++ /dev/null | |||
| @@ -1,511 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use stm32_metapac::rcc::vals::Timpre; | ||
| 4 | |||
| 5 | use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; | ||
| 6 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 7 | use crate::rcc::{set_freqs, Clocks}; | ||
| 8 | use crate::time::Hertz; | ||
| 9 | use crate::{peripherals, Peripheral}; | ||
| 10 | |||
| 11 | /// HSI speed | ||
| 12 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 13 | |||
| 14 | /// CSI speed | ||
| 15 | pub const CSI_FREQ: Hertz = Hertz(4_000_000); | ||
| 16 | |||
| 17 | /// HSI48 speed | ||
| 18 | pub const HSI48_FREQ: Hertz = Hertz(48_000_000); | ||
| 19 | |||
| 20 | /// LSI speed | ||
| 21 | pub const LSI_FREQ: Hertz = Hertz(32_000); | ||
| 22 | |||
| 23 | const VCO_MIN: u32 = 150_000_000; | ||
| 24 | const VCO_MAX: u32 = 420_000_000; | ||
| 25 | const VCO_WIDE_MIN: u32 = 128_000_000; | ||
| 26 | const VCO_WIDE_MAX: u32 = 560_000_000; | ||
| 27 | |||
| 28 | pub use super::bus::{AHBPrescaler, APBPrescaler}; | ||
| 29 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | ||
| 30 | |||
| 31 | pub enum HseMode { | ||
| 32 | /// crystal/ceramic oscillator (HSEBYP=0) | ||
| 33 | Oscillator, | ||
| 34 | /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) | ||
| 35 | BypassAnalog, | ||
| 36 | /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) | ||
| 37 | BypassDigital, | ||
| 38 | } | ||
| 39 | |||
| 40 | pub struct Hse { | ||
| 41 | /// HSE frequency. | ||
| 42 | pub freq: Hertz, | ||
| 43 | /// HSE mode. | ||
| 44 | pub mode: HseMode, | ||
| 45 | } | ||
| 46 | |||
| 47 | pub enum Hsi { | ||
| 48 | /// 64Mhz | ||
| 49 | Mhz64, | ||
| 50 | /// 32Mhz (divided by 2) | ||
| 51 | Mhz32, | ||
| 52 | /// 16Mhz (divided by 4) | ||
| 53 | Mhz16, | ||
| 54 | /// 8Mhz (divided by 8) | ||
| 55 | Mhz8, | ||
| 56 | } | ||
| 57 | |||
| 58 | pub enum Sysclk { | ||
| 59 | /// HSI selected as sysclk | ||
| 60 | HSI, | ||
| 61 | /// HSE selected as sysclk | ||
| 62 | HSE, | ||
| 63 | /// CSI selected as sysclk | ||
| 64 | CSI, | ||
| 65 | /// PLL1_P selected as sysclk | ||
| 66 | Pll1P, | ||
| 67 | } | ||
| 68 | |||
| 69 | pub enum PllSource { | ||
| 70 | Hsi, | ||
| 71 | Csi, | ||
| 72 | Hse, | ||
| 73 | } | ||
| 74 | |||
| 75 | pub struct Pll { | ||
| 76 | /// Source clock selection. | ||
| 77 | pub source: PllSource, | ||
| 78 | |||
| 79 | /// PLL pre-divider (DIVM). Must be between 1 and 63. | ||
| 80 | pub prediv: u8, | ||
| 81 | |||
| 82 | /// PLL multiplication factor. Must be between 4 and 512. | ||
| 83 | pub mul: u16, | ||
| 84 | |||
| 85 | /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. | ||
| 86 | /// On PLL1, it must be even (in particular, it cannot be 1.) | ||
| 87 | pub divp: Option<u16>, | ||
| 88 | /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. | ||
| 89 | pub divq: Option<u16>, | ||
| 90 | /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. | ||
| 91 | pub divr: Option<u16>, | ||
| 92 | } | ||
| 93 | |||
| 94 | fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz { | ||
| 95 | match (tim, apb) { | ||
| 96 | // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a | ||
| 97 | // division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 | ||
| 98 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk, | ||
| 99 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk, | ||
| 100 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32, | ||
| 101 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32, | ||
| 102 | (TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32, | ||
| 103 | // The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2 | ||
| 104 | // corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2 | ||
| 105 | // this makes NO SENSE and is different than in the H7. Mistake in the RM?? | ||
| 106 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk * 2u32, | ||
| 107 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk, | ||
| 108 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk / 2u32, | ||
| 109 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32, | ||
| 110 | (TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32, | ||
| 111 | |||
| 112 | _ => unreachable!(), | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | /// APB prescaler | ||
| 117 | #[derive(Clone, Copy)] | ||
| 118 | pub enum TimerPrescaler { | ||
| 119 | DefaultX2, | ||
| 120 | DefaultX4, | ||
| 121 | } | ||
| 122 | |||
| 123 | impl From<TimerPrescaler> for Timpre { | ||
| 124 | fn from(value: TimerPrescaler) -> Self { | ||
| 125 | match value { | ||
| 126 | TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, | ||
| 127 | TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Configuration of the core clocks | ||
| 133 | #[non_exhaustive] | ||
| 134 | pub struct Config { | ||
| 135 | pub hsi: Option<Hsi>, | ||
| 136 | pub hse: Option<Hse>, | ||
| 137 | pub csi: bool, | ||
| 138 | pub hsi48: bool, | ||
| 139 | pub sys: Sysclk, | ||
| 140 | |||
| 141 | pub pll1: Option<Pll>, | ||
| 142 | pub pll2: Option<Pll>, | ||
| 143 | #[cfg(rcc_h5)] | ||
| 144 | pub pll3: Option<Pll>, | ||
| 145 | |||
| 146 | pub ahb_pre: AHBPrescaler, | ||
| 147 | pub apb1_pre: APBPrescaler, | ||
| 148 | pub apb2_pre: APBPrescaler, | ||
| 149 | pub apb3_pre: APBPrescaler, | ||
| 150 | pub timer_prescaler: TimerPrescaler, | ||
| 151 | |||
| 152 | pub voltage_scale: VoltageScale, | ||
| 153 | } | ||
| 154 | |||
| 155 | impl Default for Config { | ||
| 156 | fn default() -> Self { | ||
| 157 | Self { | ||
| 158 | hsi: Some(Hsi::Mhz64), | ||
| 159 | hse: None, | ||
| 160 | csi: false, | ||
| 161 | hsi48: false, | ||
| 162 | sys: Sysclk::HSI, | ||
| 163 | pll1: None, | ||
| 164 | pll2: None, | ||
| 165 | #[cfg(rcc_h5)] | ||
| 166 | pll3: None, | ||
| 167 | |||
| 168 | ahb_pre: AHBPrescaler::DIV1, | ||
| 169 | apb1_pre: APBPrescaler::DIV1, | ||
| 170 | apb2_pre: APBPrescaler::DIV1, | ||
| 171 | apb3_pre: APBPrescaler::DIV1, | ||
| 172 | timer_prescaler: TimerPrescaler::DefaultX2, | ||
| 173 | |||
| 174 | voltage_scale: VoltageScale::SCALE3, | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | pub(crate) mod sealed { | ||
| 180 | pub trait McoInstance { | ||
| 181 | type Source; | ||
| 182 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 187 | |||
| 188 | pin_trait!(McoPin, McoInstance); | ||
| 189 | |||
| 190 | macro_rules! impl_peri { | ||
| 191 | ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { | ||
| 192 | impl sealed::McoInstance for peripherals::$peri { | ||
| 193 | type Source = $source; | ||
| 194 | |||
| 195 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { | ||
| 196 | RCC.cfgr().modify(|w| { | ||
| 197 | w.$set_source(source); | ||
| 198 | w.$set_prescaler(prescaler); | ||
| 199 | }); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | impl McoInstance for peripherals::$peri {} | ||
| 204 | }; | ||
| 205 | } | ||
| 206 | |||
| 207 | impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); | ||
| 208 | impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); | ||
| 209 | |||
| 210 | pub struct Mco<'d, T: McoInstance> { | ||
| 211 | phantom: PhantomData<&'d mut T>, | ||
| 212 | } | ||
| 213 | |||
| 214 | impl<'d, T: McoInstance> Mco<'d, T> { | ||
| 215 | pub fn new( | ||
| 216 | _peri: impl Peripheral<P = T> + 'd, | ||
| 217 | _pin: impl Peripheral<P = impl McoPin<T>> + 'd, | ||
| 218 | _source: T::Source, | ||
| 219 | ) -> Self { | ||
| 220 | todo!(); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | pub(crate) unsafe fn init(config: Config) { | ||
| 225 | let max_clk = match config.voltage_scale { | ||
| 226 | VoltageScale::SCALE0 => Hertz(250_000_000), | ||
| 227 | VoltageScale::SCALE1 => Hertz(200_000_000), | ||
| 228 | VoltageScale::SCALE2 => Hertz(150_000_000), | ||
| 229 | VoltageScale::SCALE3 => Hertz(100_000_000), | ||
| 230 | }; | ||
| 231 | |||
| 232 | // Configure voltage scale. | ||
| 233 | PWR.voscr().modify(|w| w.set_vos(config.voltage_scale)); | ||
| 234 | while !PWR.vossr().read().vosrdy() {} | ||
| 235 | |||
| 236 | // Configure HSI | ||
| 237 | let hsi = match config.hsi { | ||
| 238 | None => { | ||
| 239 | RCC.cr().modify(|w| w.set_hsion(false)); | ||
| 240 | None | ||
| 241 | } | ||
| 242 | Some(hsi) => { | ||
| 243 | let (freq, hsidiv) = match hsi { | ||
| 244 | Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1), | ||
| 245 | Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2), | ||
| 246 | Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4), | ||
| 247 | Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8), | ||
| 248 | }; | ||
| 249 | RCC.cr().modify(|w| { | ||
| 250 | w.set_hsidiv(hsidiv); | ||
| 251 | w.set_hsion(true); | ||
| 252 | }); | ||
| 253 | while !RCC.cr().read().hsirdy() {} | ||
| 254 | Some(freq) | ||
| 255 | } | ||
| 256 | }; | ||
| 257 | |||
| 258 | // Configure HSE | ||
| 259 | let hse = match config.hse { | ||
| 260 | None => { | ||
| 261 | RCC.cr().modify(|w| w.set_hseon(false)); | ||
| 262 | None | ||
| 263 | } | ||
| 264 | Some(hse) => { | ||
| 265 | let (byp, ext) = match hse.mode { | ||
| 266 | HseMode::Oscillator => (false, Hseext::ANALOG), | ||
| 267 | HseMode::BypassAnalog => (true, Hseext::ANALOG), | ||
| 268 | HseMode::BypassDigital => (true, Hseext::DIGITAL), | ||
| 269 | }; | ||
| 270 | |||
| 271 | RCC.cr().modify(|w| { | ||
| 272 | w.set_hsebyp(byp); | ||
| 273 | w.set_hseext(ext); | ||
| 274 | }); | ||
| 275 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 276 | while !RCC.cr().read().hserdy() {} | ||
| 277 | Some(hse.freq) | ||
| 278 | } | ||
| 279 | }; | ||
| 280 | |||
| 281 | // Configure HSI48. | ||
| 282 | RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); | ||
| 283 | let _hsi48 = match config.hsi48 { | ||
| 284 | false => None, | ||
| 285 | true => { | ||
| 286 | while !RCC.cr().read().hsi48rdy() {} | ||
| 287 | Some(CSI_FREQ) | ||
| 288 | } | ||
| 289 | }; | ||
| 290 | |||
| 291 | // Configure CSI. | ||
| 292 | RCC.cr().modify(|w| w.set_csion(config.csi)); | ||
| 293 | let csi = match config.csi { | ||
| 294 | false => None, | ||
| 295 | true => { | ||
| 296 | while !RCC.cr().read().csirdy() {} | ||
| 297 | Some(CSI_FREQ) | ||
| 298 | } | ||
| 299 | }; | ||
| 300 | |||
| 301 | // Configure PLLs. | ||
| 302 | let pll_input = PllInput { csi, hse, hsi }; | ||
| 303 | let pll1 = init_pll(0, config.pll1, &pll_input); | ||
| 304 | let _pll2 = init_pll(1, config.pll2, &pll_input); | ||
| 305 | #[cfg(rcc_h5)] | ||
| 306 | let _pll3 = init_pll(2, config.pll3, &pll_input); | ||
| 307 | |||
| 308 | // Configure sysclk | ||
| 309 | let (sys, sw) = match config.sys { | ||
| 310 | Sysclk::HSI => (unwrap!(hsi), Sw::HSI), | ||
| 311 | Sysclk::HSE => (unwrap!(hse), Sw::HSE), | ||
| 312 | Sysclk::CSI => (unwrap!(csi), Sw::CSI), | ||
| 313 | Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1), | ||
| 314 | }; | ||
| 315 | assert!(sys <= max_clk); | ||
| 316 | |||
| 317 | let hclk = sys / config.ahb_pre; | ||
| 318 | |||
| 319 | let apb1 = hclk / config.apb1_pre; | ||
| 320 | let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler); | ||
| 321 | let apb2 = hclk / config.apb2_pre; | ||
| 322 | let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler); | ||
| 323 | let apb3 = hclk / config.apb3_pre; | ||
| 324 | |||
| 325 | flash_setup(hclk, config.voltage_scale); | ||
| 326 | |||
| 327 | // Set hpre | ||
| 328 | let hpre = config.ahb_pre.into(); | ||
| 329 | RCC.cfgr2().modify(|w| w.set_hpre(hpre)); | ||
| 330 | while RCC.cfgr2().read().hpre() != hpre {} | ||
| 331 | |||
| 332 | // set ppre | ||
| 333 | RCC.cfgr2().modify(|w| { | ||
| 334 | w.set_ppre1(config.apb1_pre.into()); | ||
| 335 | w.set_ppre2(config.apb2_pre.into()); | ||
| 336 | w.set_ppre3(config.apb3_pre.into()); | ||
| 337 | }); | ||
| 338 | |||
| 339 | RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into())); | ||
| 340 | |||
| 341 | RCC.cfgr().modify(|w| w.set_sw(sw)); | ||
| 342 | while RCC.cfgr().read().sws() != sw {} | ||
| 343 | |||
| 344 | set_freqs(Clocks { | ||
| 345 | sys, | ||
| 346 | ahb1: hclk, | ||
| 347 | ahb2: hclk, | ||
| 348 | ahb3: hclk, | ||
| 349 | ahb4: hclk, | ||
| 350 | apb1, | ||
| 351 | apb2, | ||
| 352 | apb3, | ||
| 353 | apb1_tim, | ||
| 354 | apb2_tim, | ||
| 355 | adc: None, | ||
| 356 | }); | ||
| 357 | } | ||
| 358 | |||
| 359 | struct PllInput { | ||
| 360 | hsi: Option<Hertz>, | ||
| 361 | hse: Option<Hertz>, | ||
| 362 | csi: Option<Hertz>, | ||
| 363 | } | ||
| 364 | |||
| 365 | struct PllOutput { | ||
| 366 | p: Option<Hertz>, | ||
| 367 | #[allow(dead_code)] | ||
| 368 | q: Option<Hertz>, | ||
| 369 | #[allow(dead_code)] | ||
| 370 | r: Option<Hertz>, | ||
| 371 | } | ||
| 372 | |||
| 373 | fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput { | ||
| 374 | let Some(config) = config else { | ||
| 375 | // Stop PLL | ||
| 376 | RCC.cr().modify(|w| w.set_pllon(num, false)); | ||
| 377 | while RCC.cr().read().pllrdy(num) {} | ||
| 378 | |||
| 379 | // "To save power when PLL1 is not used, the value of PLL1M must be set to 0."" | ||
| 380 | RCC.pllcfgr(num).write(|w| { | ||
| 381 | w.set_divm(0); | ||
| 382 | }); | ||
| 383 | |||
| 384 | return PllOutput { | ||
| 385 | p: None, | ||
| 386 | q: None, | ||
| 387 | r: None, | ||
| 388 | }; | ||
| 389 | }; | ||
| 390 | |||
| 391 | assert!(1 <= config.prediv && config.prediv <= 63); | ||
| 392 | assert!(4 <= config.mul && config.mul <= 512); | ||
| 393 | |||
| 394 | let (in_clk, src) = match config.source { | ||
| 395 | PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI), | ||
| 396 | PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE), | ||
| 397 | PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI), | ||
| 398 | }; | ||
| 399 | |||
| 400 | let ref_clk = in_clk / config.prediv as u32; | ||
| 401 | |||
| 402 | let ref_range = match ref_clk.0 { | ||
| 403 | ..=1_999_999 => Pllrge::RANGE1, | ||
| 404 | ..=3_999_999 => Pllrge::RANGE2, | ||
| 405 | ..=7_999_999 => Pllrge::RANGE4, | ||
| 406 | ..=16_000_000 => Pllrge::RANGE8, | ||
| 407 | x => panic!("pll ref_clk out of range: {} mhz", x), | ||
| 408 | }; | ||
| 409 | |||
| 410 | // The smaller range (150 to 420 MHz) must | ||
| 411 | // be chosen when the reference clock frequency is lower than 2 MHz. | ||
| 412 | let wide_allowed = ref_range != Pllrge::RANGE1; | ||
| 413 | |||
| 414 | let vco_clk = ref_clk * config.mul; | ||
| 415 | let vco_range = match vco_clk.0 { | ||
| 416 | VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO, | ||
| 417 | VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO, | ||
| 418 | x => panic!("pll vco_clk out of range: {} mhz", x), | ||
| 419 | }; | ||
| 420 | |||
| 421 | let p = config.divp.map(|div| { | ||
| 422 | assert!(1 <= div && div <= 128); | ||
| 423 | if num == 0 { | ||
| 424 | // on PLL1, DIVP must be even. | ||
| 425 | assert!(div % 2 == 0); | ||
| 426 | } | ||
| 427 | |||
| 428 | vco_clk / div | ||
| 429 | }); | ||
| 430 | let q = config.divq.map(|div| { | ||
| 431 | assert!(1 <= div && div <= 128); | ||
| 432 | vco_clk / div | ||
| 433 | }); | ||
| 434 | let r = config.divr.map(|div| { | ||
| 435 | assert!(1 <= div && div <= 128); | ||
| 436 | vco_clk / div | ||
| 437 | }); | ||
| 438 | |||
| 439 | RCC.pllcfgr(num).write(|w| { | ||
| 440 | w.set_pllsrc(src); | ||
| 441 | w.set_divm(config.prediv); | ||
| 442 | w.set_pllvcosel(vco_range); | ||
| 443 | w.set_pllrge(ref_range); | ||
| 444 | w.set_pllfracen(false); | ||
| 445 | w.set_pllpen(p.is_some()); | ||
| 446 | w.set_pllqen(q.is_some()); | ||
| 447 | w.set_pllren(r.is_some()); | ||
| 448 | }); | ||
| 449 | RCC.plldivr(num).write(|w| { | ||
| 450 | w.set_plln(config.mul - 1); | ||
| 451 | w.set_pllp((config.divp.unwrap_or(1) - 1) as u8); | ||
| 452 | w.set_pllq((config.divq.unwrap_or(1) - 1) as u8); | ||
| 453 | w.set_pllr((config.divr.unwrap_or(1) - 1) as u8); | ||
| 454 | }); | ||
| 455 | |||
| 456 | RCC.cr().modify(|w| w.set_pllon(num, true)); | ||
| 457 | while !RCC.cr().read().pllrdy(num) {} | ||
| 458 | |||
| 459 | PllOutput { p, q, r } | ||
| 460 | } | ||
| 461 | |||
| 462 | fn flash_setup(clk: Hertz, vos: VoltageScale) { | ||
| 463 | // RM0481 Rev 1, table 37 | ||
| 464 | // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0 | ||
| 465 | // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz | ||
| 466 | // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz | ||
| 467 | // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz | ||
| 468 | // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz | ||
| 469 | // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz | ||
| 470 | // 5 2 170 to 200 MHz 210 to 250 MHz | ||
| 471 | |||
| 472 | // See RM0433 Rev 7 Table 17. FLASH recommended number of wait | ||
| 473 | // states and programming delay | ||
| 474 | let (latency, wrhighfreq) = match (vos, clk.0) { | ||
| 475 | (VoltageScale::SCALE0, ..=42_000_000) => (0, 0), | ||
| 476 | (VoltageScale::SCALE0, ..=84_000_000) => (1, 0), | ||
| 477 | (VoltageScale::SCALE0, ..=126_000_000) => (2, 1), | ||
| 478 | (VoltageScale::SCALE0, ..=168_000_000) => (3, 1), | ||
| 479 | (VoltageScale::SCALE0, ..=210_000_000) => (4, 2), | ||
| 480 | (VoltageScale::SCALE0, ..=250_000_000) => (5, 2), | ||
| 481 | |||
| 482 | (VoltageScale::SCALE1, ..=34_000_000) => (0, 0), | ||
| 483 | (VoltageScale::SCALE1, ..=68_000_000) => (1, 0), | ||
| 484 | (VoltageScale::SCALE1, ..=102_000_000) => (2, 1), | ||
| 485 | (VoltageScale::SCALE1, ..=136_000_000) => (3, 1), | ||
| 486 | (VoltageScale::SCALE1, ..=170_000_000) => (4, 2), | ||
| 487 | (VoltageScale::SCALE1, ..=200_000_000) => (5, 2), | ||
| 488 | |||
| 489 | (VoltageScale::SCALE2, ..=30_000_000) => (0, 0), | ||
| 490 | (VoltageScale::SCALE2, ..=60_000_000) => (1, 0), | ||
| 491 | (VoltageScale::SCALE2, ..=90_000_000) => (2, 1), | ||
| 492 | (VoltageScale::SCALE2, ..=120_000_000) => (3, 1), | ||
| 493 | (VoltageScale::SCALE2, ..=150_000_000) => (4, 2), | ||
| 494 | |||
| 495 | (VoltageScale::SCALE3, ..=20_000_000) => (0, 0), | ||
| 496 | (VoltageScale::SCALE3, ..=40_000_000) => (1, 0), | ||
| 497 | (VoltageScale::SCALE3, ..=60_000_000) => (2, 1), | ||
| 498 | (VoltageScale::SCALE3, ..=80_000_000) => (3, 1), | ||
| 499 | (VoltageScale::SCALE3, ..=100_000_000) => (4, 2), | ||
| 500 | |||
| 501 | _ => unreachable!(), | ||
| 502 | }; | ||
| 503 | |||
| 504 | debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); | ||
| 505 | |||
| 506 | FLASH.acr().write(|w| { | ||
| 507 | w.set_wrhighfreq(wrhighfreq); | ||
| 508 | w.set_latency(latency); | ||
| 509 | }); | ||
| 510 | while FLASH.acr().read().latency() != latency {} | ||
| 511 | } | ||
diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs deleted file mode 100644 index ea26c26c1..000000000 --- a/embassy-stm32/src/rcc/h7.rs +++ /dev/null | |||
| @@ -1,934 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy_hal_internal::into_ref; | ||
| 4 | use stm32_metapac::pwr::vals::Vos; | ||
| 5 | use stm32_metapac::rcc::vals::{Mco1, Mco2}; | ||
| 6 | |||
| 7 | pub use self::pll::PllConfig; | ||
| 8 | use crate::gpio::sealed::AFType; | ||
| 9 | use crate::gpio::Speed; | ||
| 10 | use crate::pac::rcc::vals::{Adcsel, Ckpersel, Hpre, Hsidiv, Pllsrc, Ppre, Sw, Timpre}; | ||
| 11 | use crate::pac::{PWR, RCC, SYSCFG}; | ||
| 12 | use crate::rcc::{set_freqs, Clocks}; | ||
| 13 | use crate::time::Hertz; | ||
| 14 | use crate::{peripherals, Peripheral}; | ||
| 15 | |||
| 16 | /// HSI speed | ||
| 17 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 18 | |||
| 19 | /// CSI speed | ||
| 20 | pub const CSI_FREQ: Hertz = Hertz(4_000_000); | ||
| 21 | |||
| 22 | /// HSI48 speed | ||
| 23 | pub const HSI48_FREQ: Hertz = Hertz(48_000_000); | ||
| 24 | |||
| 25 | /// LSI speed | ||
| 26 | pub const LSI_FREQ: Hertz = Hertz(32_000); | ||
| 27 | |||
| 28 | #[derive(Clone, Copy)] | ||
| 29 | pub enum VoltageScale { | ||
| 30 | Scale0, | ||
| 31 | Scale1, | ||
| 32 | Scale2, | ||
| 33 | Scale3, | ||
| 34 | } | ||
| 35 | |||
| 36 | #[derive(Clone, Copy)] | ||
| 37 | pub enum AdcClockSource { | ||
| 38 | Pll2PCk, | ||
| 39 | Pll3RCk, | ||
| 40 | PerCk, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl AdcClockSource { | ||
| 44 | pub fn adcsel(&self) -> Adcsel { | ||
| 45 | match self { | ||
| 46 | AdcClockSource::Pll2PCk => Adcsel::PLL2_P, | ||
| 47 | AdcClockSource::Pll3RCk => Adcsel::PLL3_R, | ||
| 48 | AdcClockSource::PerCk => Adcsel::PER, | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | impl Default for AdcClockSource { | ||
| 54 | fn default() -> Self { | ||
| 55 | Self::Pll2PCk | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Core clock frequencies | ||
| 60 | #[derive(Clone, Copy)] | ||
| 61 | pub struct CoreClocks { | ||
| 62 | pub hclk: Hertz, | ||
| 63 | pub pclk1: Hertz, | ||
| 64 | pub pclk2: Hertz, | ||
| 65 | pub pclk3: Hertz, | ||
| 66 | pub pclk4: Hertz, | ||
| 67 | pub ppre1: u8, | ||
| 68 | pub ppre2: u8, | ||
| 69 | pub ppre3: u8, | ||
| 70 | pub ppre4: u8, | ||
| 71 | pub csi_ck: Option<Hertz>, | ||
| 72 | pub hsi_ck: Option<Hertz>, | ||
| 73 | pub hsi48_ck: Option<Hertz>, | ||
| 74 | pub lsi_ck: Option<Hertz>, | ||
| 75 | pub per_ck: Option<Hertz>, | ||
| 76 | pub hse_ck: Option<Hertz>, | ||
| 77 | pub pll1_p_ck: Option<Hertz>, | ||
| 78 | pub pll1_q_ck: Option<Hertz>, | ||
| 79 | pub pll1_r_ck: Option<Hertz>, | ||
| 80 | pub pll2_p_ck: Option<Hertz>, | ||
| 81 | pub pll2_q_ck: Option<Hertz>, | ||
| 82 | pub pll2_r_ck: Option<Hertz>, | ||
| 83 | pub pll3_p_ck: Option<Hertz>, | ||
| 84 | pub pll3_q_ck: Option<Hertz>, | ||
| 85 | pub pll3_r_ck: Option<Hertz>, | ||
| 86 | pub timx_ker_ck: Option<Hertz>, | ||
| 87 | pub timy_ker_ck: Option<Hertz>, | ||
| 88 | pub adc_ker_ck: Option<Hertz>, | ||
| 89 | pub sys_ck: Hertz, | ||
| 90 | pub c_ck: Hertz, | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Configuration of the core clocks | ||
| 94 | #[non_exhaustive] | ||
| 95 | pub struct Config { | ||
| 96 | pub hse: Option<Hertz>, | ||
| 97 | pub bypass_hse: bool, | ||
| 98 | pub sys_ck: Option<Hertz>, | ||
| 99 | pub per_ck: Option<Hertz>, | ||
| 100 | pub hclk: Option<Hertz>, | ||
| 101 | pub pclk1: Option<Hertz>, | ||
| 102 | pub pclk2: Option<Hertz>, | ||
| 103 | pub pclk3: Option<Hertz>, | ||
| 104 | pub pclk4: Option<Hertz>, | ||
| 105 | pub pll1: PllConfig, | ||
| 106 | pub pll2: PllConfig, | ||
| 107 | pub pll3: PllConfig, | ||
| 108 | pub adc_clock_source: AdcClockSource, | ||
| 109 | pub voltage_scale: VoltageScale, | ||
| 110 | } | ||
| 111 | |||
| 112 | impl Default for Config { | ||
| 113 | fn default() -> Self { | ||
| 114 | Self { | ||
| 115 | hse: None, | ||
| 116 | bypass_hse: false, | ||
| 117 | sys_ck: None, | ||
| 118 | per_ck: None, | ||
| 119 | hclk: None, | ||
| 120 | pclk1: None, | ||
| 121 | pclk2: None, | ||
| 122 | pclk3: None, | ||
| 123 | pclk4: None, | ||
| 124 | pll1: Default::default(), | ||
| 125 | pll2: Default::default(), | ||
| 126 | pll3: Default::default(), | ||
| 127 | adc_clock_source: Default::default(), | ||
| 128 | voltage_scale: VoltageScale::Scale1, | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Setup traceclk | ||
| 134 | /// Returns a pll1_r_ck | ||
| 135 | fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) { | ||
| 136 | let pll1_r_ck = match (sys_use_pll1_p, config.pll1.r_ck) { | ||
| 137 | // pll1_p_ck selected as system clock but pll1_r_ck not | ||
| 138 | // set. The traceclk mux is synchronous with the system | ||
| 139 | // clock mux, but has pll1_r_ck as an input. In order to | ||
| 140 | // keep traceclk running, we force a pll1_r_ck. | ||
| 141 | (true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)), | ||
| 142 | |||
| 143 | // Either pll1 not selected as system clock, free choice | ||
| 144 | // of pll1_r_ck. Or pll1 is selected, assume user has set | ||
| 145 | // a suitable pll1_r_ck frequency. | ||
| 146 | _ => config.pll1.r_ck, | ||
| 147 | }; | ||
| 148 | config.pll1.r_ck = pll1_r_ck; | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Divider calculator for pclk 1 - 4 | ||
| 152 | /// | ||
| 153 | /// Returns real pclk, bits, ppre and the timer kernel clock | ||
| 154 | fn ppre_calculate( | ||
| 155 | requested_pclk: u32, | ||
| 156 | hclk: u32, | ||
| 157 | max_pclk: u32, | ||
| 158 | tim_pre: Option<Timpre>, | ||
| 159 | ) -> (u32, u8, u8, Option<u32>) { | ||
| 160 | let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk { | ||
| 161 | 0 => panic!(), | ||
| 162 | 1 => (0b000, 1), | ||
| 163 | 2 => (0b100, 2), | ||
| 164 | 3..=5 => (0b101, 4), | ||
| 165 | 6..=11 => (0b110, 8), | ||
| 166 | _ => (0b111, 16), | ||
| 167 | }; | ||
| 168 | let real_pclk = hclk / u32::from(ppre); | ||
| 169 | assert!(real_pclk <= max_pclk); | ||
| 170 | |||
| 171 | let tim_ker_clk = if let Some(tim_pre) = tim_pre { | ||
| 172 | let clk = match (bits, tim_pre) { | ||
| 173 | (0b101, Timpre::DEFAULTX2) => hclk / 2, | ||
| 174 | (0b110, Timpre::DEFAULTX4) => hclk / 2, | ||
| 175 | (0b110, Timpre::DEFAULTX2) => hclk / 4, | ||
| 176 | (0b111, Timpre::DEFAULTX4) => hclk / 4, | ||
| 177 | (0b111, Timpre::DEFAULTX2) => hclk / 8, | ||
| 178 | _ => hclk, | ||
| 179 | }; | ||
| 180 | Some(clk) | ||
| 181 | } else { | ||
| 182 | None | ||
| 183 | }; | ||
| 184 | (real_pclk, bits, ppre, tim_ker_clk) | ||
| 185 | } | ||
| 186 | |||
| 187 | /// Setup sys_ck | ||
| 188 | /// Returns sys_ck frequency, and a pll1_p_ck | ||
| 189 | fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) { | ||
| 190 | // Compare available with wanted clocks | ||
| 191 | let sys_ck = config.sys_ck.unwrap_or(srcclk); | ||
| 192 | |||
| 193 | if sys_ck != srcclk { | ||
| 194 | // The requested system clock is not the immediately available | ||
| 195 | // HSE/HSI clock. Perhaps there are other ways of obtaining | ||
| 196 | // the requested system clock (such as `HSIDIV`) but we will | ||
| 197 | // ignore those for now. | ||
| 198 | // | ||
| 199 | // Therefore we must use pll1_p_ck | ||
| 200 | let pll1_p_ck = match config.pll1.p_ck { | ||
| 201 | Some(p_ck) => { | ||
| 202 | assert!( | ||
| 203 | p_ck == sys_ck, | ||
| 204 | "Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck" | ||
| 205 | ); | ||
| 206 | Some(p_ck) | ||
| 207 | } | ||
| 208 | None => Some(sys_ck), | ||
| 209 | }; | ||
| 210 | config.pll1.p_ck = pll1_p_ck; | ||
| 211 | |||
| 212 | (sys_ck, true) | ||
| 213 | } else { | ||
| 214 | // sys_ck is derived directly from a source clock | ||
| 215 | // (HSE/HSI). pll1_p_ck can be as requested | ||
| 216 | (sys_ck, false) | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { | ||
| 221 | use crate::pac::FLASH; | ||
| 222 | |||
| 223 | // ACLK in MHz, round down and subtract 1 from integers. eg. | ||
| 224 | // 61_999_999 -> 61MHz | ||
| 225 | // 62_000_000 -> 61MHz | ||
| 226 | // 62_000_001 -> 62MHz | ||
| 227 | let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000; | ||
| 228 | |||
| 229 | // See RM0433 Rev 7 Table 17. FLASH recommended number of wait | ||
| 230 | // states and programming delay | ||
| 231 | #[cfg(flash_h7)] | ||
| 232 | let (wait_states, progr_delay) = match vos { | ||
| 233 | // VOS 0 range VCORE 1.26V - 1.40V | ||
| 234 | VoltageScale::Scale0 => match rcc_aclk_mhz { | ||
| 235 | 0..=69 => (0, 0), | ||
| 236 | 70..=139 => (1, 1), | ||
| 237 | 140..=184 => (2, 1), | ||
| 238 | 185..=209 => (2, 2), | ||
| 239 | 210..=224 => (3, 2), | ||
| 240 | 225..=239 => (4, 2), | ||
| 241 | _ => (7, 3), | ||
| 242 | }, | ||
| 243 | // VOS 1 range VCORE 1.15V - 1.26V | ||
| 244 | VoltageScale::Scale1 => match rcc_aclk_mhz { | ||
| 245 | 0..=69 => (0, 0), | ||
| 246 | 70..=139 => (1, 1), | ||
| 247 | 140..=184 => (2, 1), | ||
| 248 | 185..=209 => (2, 2), | ||
| 249 | 210..=224 => (3, 2), | ||
| 250 | _ => (7, 3), | ||
| 251 | }, | ||
| 252 | // VOS 2 range VCORE 1.05V - 1.15V | ||
| 253 | VoltageScale::Scale2 => match rcc_aclk_mhz { | ||
| 254 | 0..=54 => (0, 0), | ||
| 255 | 55..=109 => (1, 1), | ||
| 256 | 110..=164 => (2, 1), | ||
| 257 | 165..=224 => (3, 2), | ||
| 258 | _ => (7, 3), | ||
| 259 | }, | ||
| 260 | // VOS 3 range VCORE 0.95V - 1.05V | ||
| 261 | VoltageScale::Scale3 => match rcc_aclk_mhz { | ||
| 262 | 0..=44 => (0, 0), | ||
| 263 | 45..=89 => (1, 1), | ||
| 264 | 90..=134 => (2, 1), | ||
| 265 | 135..=179 => (3, 2), | ||
| 266 | 180..=224 => (4, 2), | ||
| 267 | _ => (7, 3), | ||
| 268 | }, | ||
| 269 | }; | ||
| 270 | |||
| 271 | // See RM0455 Rev 10 Table 16. FLASH recommended number of wait | ||
| 272 | // states and programming delay | ||
| 273 | #[cfg(flash_h7ab)] | ||
| 274 | let (wait_states, progr_delay) = match vos { | ||
| 275 | // VOS 0 range VCORE 1.25V - 1.35V | ||
| 276 | VoltageScale::Scale0 => match rcc_aclk_mhz { | ||
| 277 | 0..=42 => (0, 0), | ||
| 278 | 43..=84 => (1, 0), | ||
| 279 | 85..=126 => (2, 1), | ||
| 280 | 127..=168 => (3, 1), | ||
| 281 | 169..=210 => (4, 2), | ||
| 282 | 211..=252 => (5, 2), | ||
| 283 | 253..=280 => (6, 3), | ||
| 284 | _ => (7, 3), | ||
| 285 | }, | ||
| 286 | // VOS 1 range VCORE 1.15V - 1.25V | ||
| 287 | VoltageScale::Scale1 => match rcc_aclk_mhz { | ||
| 288 | 0..=38 => (0, 0), | ||
| 289 | 39..=76 => (1, 0), | ||
| 290 | 77..=114 => (2, 1), | ||
| 291 | 115..=152 => (3, 1), | ||
| 292 | 153..=190 => (4, 2), | ||
| 293 | 191..=225 => (5, 2), | ||
| 294 | _ => (7, 3), | ||
| 295 | }, | ||
| 296 | // VOS 2 range VCORE 1.05V - 1.15V | ||
| 297 | VoltageScale::Scale2 => match rcc_aclk_mhz { | ||
| 298 | 0..=34 => (0, 0), | ||
| 299 | 35..=68 => (1, 0), | ||
| 300 | 69..=102 => (2, 1), | ||
| 301 | 103..=136 => (3, 1), | ||
| 302 | 137..=160 => (4, 2), | ||
| 303 | _ => (7, 3), | ||
| 304 | }, | ||
| 305 | // VOS 3 range VCORE 0.95V - 1.05V | ||
| 306 | VoltageScale::Scale3 => match rcc_aclk_mhz { | ||
| 307 | 0..=22 => (0, 0), | ||
| 308 | 23..=44 => (1, 0), | ||
| 309 | 45..=66 => (2, 1), | ||
| 310 | 67..=88 => (3, 1), | ||
| 311 | _ => (7, 3), | ||
| 312 | }, | ||
| 313 | }; | ||
| 314 | |||
| 315 | FLASH.acr().write(|w| { | ||
| 316 | w.set_wrhighfreq(progr_delay); | ||
| 317 | w.set_latency(wait_states) | ||
| 318 | }); | ||
| 319 | while FLASH.acr().read().latency() != wait_states {} | ||
| 320 | } | ||
| 321 | |||
| 322 | pub enum McoClock { | ||
| 323 | Disabled, | ||
| 324 | Bypassed, | ||
| 325 | Divided(u8), | ||
| 326 | } | ||
| 327 | |||
| 328 | impl McoClock { | ||
| 329 | fn into_raw(&self) -> u8 { | ||
| 330 | match self { | ||
| 331 | McoClock::Disabled => 0, | ||
| 332 | McoClock::Bypassed => 1, | ||
| 333 | McoClock::Divided(divisor) => { | ||
| 334 | if *divisor > 15 { | ||
| 335 | panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.") | ||
| 336 | } | ||
| 337 | *divisor | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | #[derive(Copy, Clone)] | ||
| 344 | pub enum Mco1Source { | ||
| 345 | Hsi, | ||
| 346 | Lse, | ||
| 347 | Hse, | ||
| 348 | Pll1Q, | ||
| 349 | Hsi48, | ||
| 350 | } | ||
| 351 | |||
| 352 | impl Default for Mco1Source { | ||
| 353 | fn default() -> Self { | ||
| 354 | Self::Hsi | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | pub trait McoSource { | ||
| 359 | type Raw; | ||
| 360 | |||
| 361 | fn into_raw(&self) -> Self::Raw; | ||
| 362 | } | ||
| 363 | |||
| 364 | impl McoSource for Mco1Source { | ||
| 365 | type Raw = Mco1; | ||
| 366 | fn into_raw(&self) -> Self::Raw { | ||
| 367 | match self { | ||
| 368 | Mco1Source::Hsi => Mco1::HSI, | ||
| 369 | Mco1Source::Lse => Mco1::LSE, | ||
| 370 | Mco1Source::Hse => Mco1::HSE, | ||
| 371 | Mco1Source::Pll1Q => Mco1::PLL1_Q, | ||
| 372 | Mco1Source::Hsi48 => Mco1::HSI48, | ||
| 373 | } | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | #[derive(Copy, Clone)] | ||
| 378 | pub enum Mco2Source { | ||
| 379 | SysClk, | ||
| 380 | Pll2Q, | ||
| 381 | Hse, | ||
| 382 | Pll1Q, | ||
| 383 | Csi, | ||
| 384 | Lsi, | ||
| 385 | } | ||
| 386 | |||
| 387 | impl Default for Mco2Source { | ||
| 388 | fn default() -> Self { | ||
| 389 | Self::SysClk | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | impl McoSource for Mco2Source { | ||
| 394 | type Raw = Mco2; | ||
| 395 | fn into_raw(&self) -> Self::Raw { | ||
| 396 | match self { | ||
| 397 | Mco2Source::SysClk => Mco2::SYSCLK, | ||
| 398 | Mco2Source::Pll2Q => Mco2::PLL2_P, | ||
| 399 | Mco2Source::Hse => Mco2::HSE, | ||
| 400 | Mco2Source::Pll1Q => Mco2::PLL1_P, | ||
| 401 | Mco2Source::Csi => Mco2::CSI, | ||
| 402 | Mco2Source::Lsi => Mco2::LSI, | ||
| 403 | } | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | pub(crate) mod sealed { | ||
| 408 | pub trait McoInstance { | ||
| 409 | type Source; | ||
| 410 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 415 | |||
| 416 | pin_trait!(McoPin, McoInstance); | ||
| 417 | |||
| 418 | macro_rules! impl_peri { | ||
| 419 | ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { | ||
| 420 | impl sealed::McoInstance for peripherals::$peri { | ||
| 421 | type Source = $source; | ||
| 422 | |||
| 423 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { | ||
| 424 | RCC.cfgr().modify(|w| { | ||
| 425 | w.$set_source(source); | ||
| 426 | w.$set_prescaler(prescaler); | ||
| 427 | }); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | impl McoInstance for peripherals::$peri {} | ||
| 432 | }; | ||
| 433 | } | ||
| 434 | |||
| 435 | impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); | ||
| 436 | impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); | ||
| 437 | |||
| 438 | pub struct Mco<'d, T: McoInstance> { | ||
| 439 | phantom: PhantomData<&'d mut T>, | ||
| 440 | } | ||
| 441 | |||
| 442 | impl<'d, T: McoInstance> Mco<'d, T> { | ||
| 443 | pub fn new( | ||
| 444 | _peri: impl Peripheral<P = T> + 'd, | ||
| 445 | pin: impl Peripheral<P = impl McoPin<T>> + 'd, | ||
| 446 | source: impl McoSource<Raw = T::Source>, | ||
| 447 | prescaler: McoClock, | ||
| 448 | ) -> Self { | ||
| 449 | into_ref!(pin); | ||
| 450 | |||
| 451 | critical_section::with(|_| unsafe { | ||
| 452 | T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); | ||
| 453 | pin.set_as_af(pin.af_num(), AFType::OutputPushPull); | ||
| 454 | pin.set_speed(Speed::VeryHigh); | ||
| 455 | }); | ||
| 456 | |||
| 457 | Self { phantom: PhantomData } | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | pub(crate) unsafe fn init(mut config: Config) { | ||
| 462 | // NB. The lower bytes of CR3 can only be written once after | ||
| 463 | // POR, and must be written with a valid combination. Refer to | ||
| 464 | // RM0433 Rev 7 6.8.4. This is partially enforced by dropping | ||
| 465 | // `self` at the end of this method, but of course we cannot | ||
| 466 | // know what happened between the previous POR and here. | ||
| 467 | #[cfg(pwr_h7rm0433)] | ||
| 468 | PWR.cr3().modify(|w| { | ||
| 469 | w.set_scuen(true); | ||
| 470 | w.set_ldoen(true); | ||
| 471 | w.set_bypass(false); | ||
| 472 | }); | ||
| 473 | |||
| 474 | #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] | ||
| 475 | PWR.cr3().modify(|w| { | ||
| 476 | // hardcode "Direct SPMS" for now, this is what works on nucleos with the | ||
| 477 | // default solderbridge configuration. | ||
| 478 | w.set_sden(true); | ||
| 479 | w.set_ldoen(false); | ||
| 480 | }); | ||
| 481 | |||
| 482 | // Validate the supply configuration. If you are stuck here, it is | ||
| 483 | // because the voltages on your board do not match those specified | ||
| 484 | // in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset | ||
| 485 | // VOS = Scale 3, so check that the voltage on the VCAP pins = | ||
| 486 | // 1.0V. | ||
| 487 | info!("a"); | ||
| 488 | while !PWR.csr1().read().actvosrdy() {} | ||
| 489 | info!("b"); | ||
| 490 | |||
| 491 | #[cfg(syscfg_h7)] | ||
| 492 | { | ||
| 493 | // in chips without the overdrive bit, we can go from any scale to any scale directly. | ||
| 494 | PWR.d3cr().modify(|w| { | ||
| 495 | w.set_vos(match config.voltage_scale { | ||
| 496 | VoltageScale::Scale0 => Vos::SCALE0, | ||
| 497 | VoltageScale::Scale1 => Vos::SCALE1, | ||
| 498 | VoltageScale::Scale2 => Vos::SCALE2, | ||
| 499 | VoltageScale::Scale3 => Vos::SCALE3, | ||
| 500 | }) | ||
| 501 | }); | ||
| 502 | while !PWR.d3cr().read().vosrdy() {} | ||
| 503 | } | ||
| 504 | |||
| 505 | #[cfg(syscfg_h7od)] | ||
| 506 | { | ||
| 507 | match config.voltage_scale { | ||
| 508 | VoltageScale::Scale0 => { | ||
| 509 | // to go to scale0, we must go to Scale1 first... | ||
| 510 | PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1)); | ||
| 511 | while !PWR.d3cr().read().vosrdy() {} | ||
| 512 | |||
| 513 | // Then enable overdrive. | ||
| 514 | critical_section::with(|_| { | ||
| 515 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | ||
| 516 | SYSCFG.pwrcr().modify(|w| w.set_oden(1)); | ||
| 517 | }); | ||
| 518 | while !PWR.d3cr().read().vosrdy() {} | ||
| 519 | } | ||
| 520 | _ => { | ||
| 521 | // for all other scales, we can go directly. | ||
| 522 | PWR.d3cr().modify(|w| { | ||
| 523 | w.set_vos(match config.voltage_scale { | ||
| 524 | VoltageScale::Scale0 => unreachable!(), | ||
| 525 | VoltageScale::Scale1 => Vos::SCALE1, | ||
| 526 | VoltageScale::Scale2 => Vos::SCALE2, | ||
| 527 | VoltageScale::Scale3 => Vos::SCALE3, | ||
| 528 | }) | ||
| 529 | }); | ||
| 530 | while !PWR.d3cr().read().vosrdy() {} | ||
| 531 | } | ||
| 532 | } | ||
| 533 | } | ||
| 534 | |||
| 535 | // Freeze the core clocks, returning a Core Clocks Distribution | ||
| 536 | // and Reset (CCDR) structure. The actual frequency of the clocks | ||
| 537 | // configured is returned in the `clocks` member of the CCDR | ||
| 538 | // structure. | ||
| 539 | // | ||
| 540 | // Note that `freeze` will never result in a clock _faster_ than | ||
| 541 | // that specified. It may result in a clock that is a factor of [1, | ||
| 542 | // 2) slower. | ||
| 543 | // | ||
| 544 | // `syscfg` is required to enable the I/O compensation cell. | ||
| 545 | // | ||
| 546 | // # Panics | ||
| 547 | // | ||
| 548 | // If a clock specification cannot be achieved within the | ||
| 549 | // hardware specification then this function will panic. This | ||
| 550 | // function may also panic if a clock specification can be | ||
| 551 | // achieved, but the mechanism for doing so is not yet | ||
| 552 | // implemented here. | ||
| 553 | |||
| 554 | let srcclk = config.hse.unwrap_or(HSI_FREQ); // Available clocks | ||
| 555 | let (sys_ck, sys_use_pll1_p) = sys_ck_setup(&mut config, srcclk); | ||
| 556 | |||
| 557 | // Configure traceclk from PLL if needed | ||
| 558 | traceclk_setup(&mut config, sys_use_pll1_p); | ||
| 559 | |||
| 560 | let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0); | ||
| 561 | let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1); | ||
| 562 | let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2); | ||
| 563 | |||
| 564 | let sys_ck = if sys_use_pll1_p { | ||
| 565 | Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup | ||
| 566 | } else { | ||
| 567 | sys_ck | ||
| 568 | }; | ||
| 569 | |||
| 570 | // This routine does not support HSIDIV != 1. To | ||
| 571 | // do so it would need to ensure all PLLxON bits are clear | ||
| 572 | // before changing the value of HSIDIV | ||
| 573 | let cr = RCC.cr().read(); | ||
| 574 | assert!(cr.hsion()); | ||
| 575 | assert!(cr.hsidiv() == Hsidiv::DIV1); | ||
| 576 | |||
| 577 | RCC.csr().modify(|w| w.set_lsion(true)); | ||
| 578 | while !RCC.csr().read().lsirdy() {} | ||
| 579 | |||
| 580 | // per_ck from HSI by default | ||
| 581 | let (per_ck, ckpersel) = match (config.per_ck == config.hse, config.per_ck) { | ||
| 582 | (true, Some(hse)) => (hse, Ckpersel::HSE), // HSE | ||
| 583 | (_, Some(CSI_FREQ)) => (CSI_FREQ, Ckpersel::CSI), // CSI | ||
| 584 | _ => (HSI_FREQ, Ckpersel::HSI), // HSI | ||
| 585 | }; | ||
| 586 | |||
| 587 | // D1 Core Prescaler | ||
| 588 | // Set to 1 | ||
| 589 | let d1cpre_bits = 0; | ||
| 590 | let d1cpre_div = 1; | ||
| 591 | let sys_d1cpre_ck = sys_ck.0 / d1cpre_div; | ||
| 592 | |||
| 593 | // Refer to part datasheet "General operating conditions" | ||
| 594 | // table for (rev V). We do not assert checks for earlier | ||
| 595 | // revisions which may have lower limits. | ||
| 596 | let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match config.voltage_scale { | ||
| 597 | VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000), | ||
| 598 | VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000), | ||
| 599 | VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000), | ||
| 600 | VoltageScale::Scale3 => (200_000_000, 100_000_000, 50_000_000), | ||
| 601 | }; | ||
| 602 | assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max); | ||
| 603 | |||
| 604 | let rcc_hclk = config.hclk.map(|v| v.0).unwrap_or(sys_d1cpre_ck / 2); | ||
| 605 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 606 | |||
| 607 | // Estimate divisor | ||
| 608 | let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk { | ||
| 609 | 0 => panic!(), | ||
| 610 | 1 => (Hpre::DIV1, 1), | ||
| 611 | 2 => (Hpre::DIV2, 2), | ||
| 612 | 3..=5 => (Hpre::DIV4, 4), | ||
| 613 | 6..=11 => (Hpre::DIV8, 8), | ||
| 614 | 12..=39 => (Hpre::DIV16, 16), | ||
| 615 | 40..=95 => (Hpre::DIV64, 64), | ||
| 616 | 96..=191 => (Hpre::DIV128, 128), | ||
| 617 | 192..=383 => (Hpre::DIV256, 256), | ||
| 618 | _ => (Hpre::DIV512, 512), | ||
| 619 | }; | ||
| 620 | // Calculate real AXI and AHB clock | ||
| 621 | let rcc_hclk = sys_d1cpre_ck / hpre_div; | ||
| 622 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 623 | let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7 | ||
| 624 | // Timer prescaler selection | ||
| 625 | let timpre = Timpre::DEFAULTX2; | ||
| 626 | |||
| 627 | let requested_pclk1 = config.pclk1.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 628 | let (rcc_pclk1, ppre1_bits, ppre1, rcc_timerx_ker_ck) = | ||
| 629 | ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre)); | ||
| 630 | |||
| 631 | let requested_pclk2 = config.pclk2.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 632 | let (rcc_pclk2, ppre2_bits, ppre2, rcc_timery_ker_ck) = | ||
| 633 | ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre)); | ||
| 634 | |||
| 635 | let requested_pclk3 = config.pclk3.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 636 | let (rcc_pclk3, ppre3_bits, ppre3, _) = ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None); | ||
| 637 | |||
| 638 | let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 639 | let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None); | ||
| 640 | |||
| 641 | // Start switching clocks ------------------- | ||
| 642 | |||
| 643 | // Ensure CSI is on and stable | ||
| 644 | RCC.cr().modify(|w| w.set_csion(true)); | ||
| 645 | while !RCC.cr().read().csirdy() {} | ||
| 646 | |||
| 647 | // Ensure HSI48 is on and stable | ||
| 648 | RCC.cr().modify(|w| w.set_hsi48on(true)); | ||
| 649 | while !RCC.cr().read().hsi48on() {} | ||
| 650 | |||
| 651 | // XXX: support MCO ? | ||
| 652 | |||
| 653 | let hse_ck = match config.hse { | ||
| 654 | Some(hse) => { | ||
| 655 | // Ensure HSE is on and stable | ||
| 656 | RCC.cr().modify(|w| { | ||
| 657 | w.set_hseon(true); | ||
| 658 | w.set_hsebyp(config.bypass_hse); | ||
| 659 | }); | ||
| 660 | while !RCC.cr().read().hserdy() {} | ||
| 661 | Some(hse) | ||
| 662 | } | ||
| 663 | None => None, | ||
| 664 | }; | ||
| 665 | |||
| 666 | let pllsrc = if config.hse.is_some() { Pllsrc::HSE } else { Pllsrc::HSI }; | ||
| 667 | RCC.pllckselr().modify(|w| w.set_pllsrc(pllsrc)); | ||
| 668 | |||
| 669 | let enable_pll = |pll| { | ||
| 670 | RCC.cr().modify(|w| w.set_pllon(pll, true)); | ||
| 671 | while !RCC.cr().read().pllrdy(pll) {} | ||
| 672 | }; | ||
| 673 | |||
| 674 | if pll1_p_ck.is_some() { | ||
| 675 | enable_pll(0); | ||
| 676 | } | ||
| 677 | |||
| 678 | if pll2_p_ck.is_some() { | ||
| 679 | enable_pll(1); | ||
| 680 | } | ||
| 681 | |||
| 682 | if pll3_p_ck.is_some() { | ||
| 683 | enable_pll(2); | ||
| 684 | } | ||
| 685 | |||
| 686 | // Core Prescaler / AHB Prescaler / APB3 Prescaler | ||
| 687 | RCC.d1cfgr().modify(|w| { | ||
| 688 | w.set_d1cpre(Hpre::from_bits(d1cpre_bits)); | ||
| 689 | w.set_d1ppre(Ppre::from_bits(ppre3_bits)); | ||
| 690 | w.set_hpre(hpre_bits) | ||
| 691 | }); | ||
| 692 | // Ensure core prescaler value is valid before future lower | ||
| 693 | // core voltage | ||
| 694 | while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {} | ||
| 695 | |||
| 696 | flash_setup(rcc_aclk, config.voltage_scale); | ||
| 697 | |||
| 698 | // APB1 / APB2 Prescaler | ||
| 699 | RCC.d2cfgr().modify(|w| { | ||
| 700 | w.set_d2ppre1(Ppre::from_bits(ppre1_bits)); | ||
| 701 | w.set_d2ppre2(Ppre::from_bits(ppre2_bits)); | ||
| 702 | }); | ||
| 703 | |||
| 704 | // APB4 Prescaler | ||
| 705 | RCC.d3cfgr().modify(|w| w.set_d3ppre(Ppre::from_bits(ppre4_bits))); | ||
| 706 | |||
| 707 | // Peripheral Clock (per_ck) | ||
| 708 | RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel)); | ||
| 709 | |||
| 710 | // ADC clock MUX | ||
| 711 | RCC.d3ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel())); | ||
| 712 | |||
| 713 | let adc_ker_ck = match config.adc_clock_source { | ||
| 714 | AdcClockSource::Pll2PCk => pll2_p_ck.map(Hertz), | ||
| 715 | AdcClockSource::Pll3RCk => pll3_r_ck.map(Hertz), | ||
| 716 | AdcClockSource::PerCk => Some(per_ck), | ||
| 717 | }; | ||
| 718 | |||
| 719 | // Set timer clocks prescaler setting | ||
| 720 | RCC.cfgr().modify(|w| w.set_timpre(timpre)); | ||
| 721 | |||
| 722 | // Select system clock source | ||
| 723 | let sw = match (sys_use_pll1_p, config.hse.is_some()) { | ||
| 724 | (true, _) => Sw::PLL1, | ||
| 725 | (false, true) => Sw::HSE, | ||
| 726 | _ => Sw::HSI, | ||
| 727 | }; | ||
| 728 | RCC.cfgr().modify(|w| w.set_sw(sw)); | ||
| 729 | while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} | ||
| 730 | |||
| 731 | // IO compensation cell - Requires CSI clock and SYSCFG | ||
| 732 | assert!(RCC.cr().read().csirdy()); | ||
| 733 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | ||
| 734 | |||
| 735 | // Enable the compensation cell, using back-bias voltage code | ||
| 736 | // provide by the cell. | ||
| 737 | critical_section::with(|_| { | ||
| 738 | SYSCFG.cccsr().modify(|w| { | ||
| 739 | w.set_en(true); | ||
| 740 | w.set_cs(false); | ||
| 741 | w.set_hslv(false); | ||
| 742 | }) | ||
| 743 | }); | ||
| 744 | while !SYSCFG.cccsr().read().ready() {} | ||
| 745 | |||
| 746 | let core_clocks = CoreClocks { | ||
| 747 | hclk: Hertz(rcc_hclk), | ||
| 748 | pclk1: Hertz(rcc_pclk1), | ||
| 749 | pclk2: Hertz(rcc_pclk2), | ||
| 750 | pclk3: Hertz(rcc_pclk3), | ||
| 751 | pclk4: Hertz(rcc_pclk4), | ||
| 752 | ppre1, | ||
| 753 | ppre2, | ||
| 754 | ppre3, | ||
| 755 | ppre4, | ||
| 756 | csi_ck: Some(CSI_FREQ), | ||
| 757 | hsi_ck: Some(HSI_FREQ), | ||
| 758 | hsi48_ck: Some(HSI48_FREQ), | ||
| 759 | lsi_ck: Some(LSI_FREQ), | ||
| 760 | per_ck: Some(per_ck), | ||
| 761 | hse_ck, | ||
| 762 | pll1_p_ck: pll1_p_ck.map(Hertz), | ||
| 763 | pll1_q_ck: pll1_q_ck.map(Hertz), | ||
| 764 | pll1_r_ck: pll1_r_ck.map(Hertz), | ||
| 765 | pll2_p_ck: pll2_p_ck.map(Hertz), | ||
| 766 | pll2_q_ck: pll2_q_ck.map(Hertz), | ||
| 767 | pll2_r_ck: pll2_r_ck.map(Hertz), | ||
| 768 | pll3_p_ck: pll3_p_ck.map(Hertz), | ||
| 769 | pll3_q_ck: pll3_q_ck.map(Hertz), | ||
| 770 | pll3_r_ck: pll3_r_ck.map(Hertz), | ||
| 771 | timx_ker_ck: rcc_timerx_ker_ck.map(Hertz), | ||
| 772 | timy_ker_ck: rcc_timery_ker_ck.map(Hertz), | ||
| 773 | adc_ker_ck, | ||
| 774 | sys_ck, | ||
| 775 | c_ck: Hertz(sys_d1cpre_ck), | ||
| 776 | }; | ||
| 777 | |||
| 778 | set_freqs(Clocks { | ||
| 779 | sys: core_clocks.c_ck, | ||
| 780 | ahb1: core_clocks.hclk, | ||
| 781 | ahb2: core_clocks.hclk, | ||
| 782 | ahb3: core_clocks.hclk, | ||
| 783 | ahb4: core_clocks.hclk, | ||
| 784 | apb1: core_clocks.pclk1, | ||
| 785 | apb2: core_clocks.pclk2, | ||
| 786 | apb4: core_clocks.pclk4, | ||
| 787 | apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1), | ||
| 788 | apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2), | ||
| 789 | adc: core_clocks.adc_ker_ck, | ||
| 790 | }); | ||
| 791 | } | ||
| 792 | |||
| 793 | mod pll { | ||
| 794 | use super::{Hertz, RCC}; | ||
| 795 | |||
| 796 | const VCO_MIN: u32 = 150_000_000; | ||
| 797 | const VCO_MAX: u32 = 420_000_000; | ||
| 798 | |||
| 799 | #[derive(Default)] | ||
| 800 | pub struct PllConfig { | ||
| 801 | pub p_ck: Option<Hertz>, | ||
| 802 | pub q_ck: Option<Hertz>, | ||
| 803 | pub r_ck: Option<Hertz>, | ||
| 804 | } | ||
| 805 | |||
| 806 | pub(super) struct PllConfigResults { | ||
| 807 | pub ref_x_ck: u32, | ||
| 808 | pub pll_x_m: u32, | ||
| 809 | pub pll_x_p: u32, | ||
| 810 | pub vco_ck_target: u32, | ||
| 811 | } | ||
| 812 | |||
| 813 | fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) { | ||
| 814 | let pll_x_p = if plln == 0 { | ||
| 815 | if output > VCO_MAX / 2 { | ||
| 816 | 1 | ||
| 817 | } else { | ||
| 818 | ((VCO_MAX / output) | 1) - 1 // Must be even or unity | ||
| 819 | } | ||
| 820 | } else { | ||
| 821 | // Specific to PLL2/3, will subtract 1 later | ||
| 822 | if output > VCO_MAX / 2 { | ||
| 823 | 1 | ||
| 824 | } else { | ||
| 825 | VCO_MAX / output | ||
| 826 | } | ||
| 827 | }; | ||
| 828 | |||
| 829 | let vco_ck = output * pll_x_p; | ||
| 830 | |||
| 831 | assert!(pll_x_p < 128); | ||
| 832 | assert!(vco_ck >= VCO_MIN); | ||
| 833 | assert!(vco_ck <= VCO_MAX); | ||
| 834 | |||
| 835 | (vco_ck, pll_x_p) | ||
| 836 | } | ||
| 837 | |||
| 838 | /// # Safety | ||
| 839 | /// | ||
| 840 | /// Must have exclusive access to the RCC register block | ||
| 841 | fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults { | ||
| 842 | use crate::pac::rcc::vals::{Pllrge, Pllvcosel}; | ||
| 843 | |||
| 844 | let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln); | ||
| 845 | |||
| 846 | // Input divisor, resulting in a reference clock in the range | ||
| 847 | // 1 to 2 MHz. Choose the highest reference clock (lowest m) | ||
| 848 | let pll_x_m = (pll_src + 1_999_999) / 2_000_000; | ||
| 849 | assert!(pll_x_m < 64); | ||
| 850 | |||
| 851 | // Calculate resulting reference clock | ||
| 852 | let ref_x_ck = pll_src / pll_x_m; | ||
| 853 | assert!((1_000_000..=2_000_000).contains(&ref_x_ck)); | ||
| 854 | |||
| 855 | RCC.pllcfgr().modify(|w| { | ||
| 856 | w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO); | ||
| 857 | w.set_pllrge(plln, Pllrge::RANGE1); | ||
| 858 | }); | ||
| 859 | PllConfigResults { | ||
| 860 | ref_x_ck, | ||
| 861 | pll_x_m, | ||
| 862 | pll_x_p, | ||
| 863 | vco_ck_target, | ||
| 864 | } | ||
| 865 | } | ||
| 866 | |||
| 867 | /// # Safety | ||
| 868 | /// | ||
| 869 | /// Must have exclusive access to the RCC register block | ||
| 870 | pub(super) fn pll_setup(pll_src: u32, config: &PllConfig, plln: usize) -> (Option<u32>, Option<u32>, Option<u32>) { | ||
| 871 | use crate::pac::rcc::vals::Divp; | ||
| 872 | |||
| 873 | match config.p_ck { | ||
| 874 | Some(requested_output) => { | ||
| 875 | let config_results = vco_setup(pll_src, requested_output.0, plln); | ||
| 876 | let PllConfigResults { | ||
| 877 | ref_x_ck, | ||
| 878 | pll_x_m, | ||
| 879 | pll_x_p, | ||
| 880 | vco_ck_target, | ||
| 881 | } = config_results; | ||
| 882 | |||
| 883 | RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8)); | ||
| 884 | |||
| 885 | // Feedback divider. Integer only | ||
| 886 | let pll_x_n = vco_ck_target / ref_x_ck; | ||
| 887 | assert!(pll_x_n >= 4); | ||
| 888 | assert!(pll_x_n <= 512); | ||
| 889 | RCC.plldivr(plln).modify(|w| w.set_divn1((pll_x_n - 1) as u16)); | ||
| 890 | |||
| 891 | // No FRACN | ||
| 892 | RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false)); | ||
| 893 | let vco_ck = ref_x_ck * pll_x_n; | ||
| 894 | |||
| 895 | RCC.plldivr(plln) | ||
| 896 | .modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8))); | ||
| 897 | RCC.pllcfgr().modify(|w| w.set_divpen(plln, true)); | ||
| 898 | |||
| 899 | // Calulate additional output dividers | ||
| 900 | let q_ck = match config.q_ck { | ||
| 901 | Some(Hertz(ck)) if ck > 0 => { | ||
| 902 | let div = (vco_ck + ck - 1) / ck; | ||
| 903 | RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8)); | ||
| 904 | RCC.pllcfgr().modify(|w| w.set_divqen(plln, true)); | ||
| 905 | Some(vco_ck / div) | ||
| 906 | } | ||
| 907 | _ => None, | ||
| 908 | }; | ||
| 909 | let r_ck = match config.r_ck { | ||
| 910 | Some(Hertz(ck)) if ck > 0 => { | ||
| 911 | let div = (vco_ck + ck - 1) / ck; | ||
| 912 | RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8)); | ||
| 913 | RCC.pllcfgr().modify(|w| w.set_divren(plln, true)); | ||
| 914 | Some(vco_ck / div) | ||
| 915 | } | ||
| 916 | _ => None, | ||
| 917 | }; | ||
| 918 | |||
| 919 | (Some(vco_ck / pll_x_p), q_ck, r_ck) | ||
| 920 | } | ||
| 921 | None => { | ||
| 922 | assert!( | ||
| 923 | config.q_ck.is_none(), | ||
| 924 | "Must set PLL P clock for Q clock to take effect!" | ||
| 925 | ); | ||
| 926 | assert!( | ||
| 927 | config.r_ck.is_none(), | ||
| 928 | "Must set PLL P clock for R clock to take effect!" | ||
| 929 | ); | ||
| 930 | (None, None, None) | ||
| 931 | } | ||
| 932 | } | ||
| 933 | } | ||
| 934 | } | ||
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs new file mode 100644 index 000000000..2453ed821 --- /dev/null +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy_hal_internal::into_ref; | ||
| 4 | |||
| 5 | use crate::gpio::sealed::AFType; | ||
| 6 | use crate::gpio::Speed; | ||
| 7 | pub use crate::pac::rcc::vals::{Mco1 as Mco1Source, Mco2 as Mco2Source}; | ||
| 8 | use crate::pac::RCC; | ||
| 9 | use crate::{peripherals, Peripheral}; | ||
| 10 | |||
| 11 | pub(crate) mod sealed { | ||
| 12 | pub trait McoInstance { | ||
| 13 | type Source; | ||
| 14 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 19 | |||
| 20 | pin_trait!(McoPin, McoInstance); | ||
| 21 | |||
| 22 | macro_rules! impl_peri { | ||
| 23 | ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { | ||
| 24 | impl sealed::McoInstance for peripherals::$peri { | ||
| 25 | type Source = $source; | ||
| 26 | |||
| 27 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { | ||
| 28 | RCC.cfgr().modify(|w| { | ||
| 29 | w.$set_source(source); | ||
| 30 | w.$set_prescaler(prescaler); | ||
| 31 | }); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | impl McoInstance for peripherals::$peri {} | ||
| 36 | }; | ||
| 37 | } | ||
| 38 | |||
| 39 | impl_peri!(MCO1, Mco1Source, set_mco1, set_mco1pre); | ||
| 40 | impl_peri!(MCO2, Mco2Source, set_mco2, set_mco2pre); | ||
| 41 | |||
| 42 | pub struct Mco<'d, T: McoInstance> { | ||
| 43 | phantom: PhantomData<&'d mut T>, | ||
| 44 | } | ||
| 45 | |||
| 46 | impl<'d, T: McoInstance> Mco<'d, T> { | ||
| 47 | /// Create a new MCO instance. | ||
| 48 | /// | ||
| 49 | /// `prescaler` must be between 1 and 15. | ||
| 50 | pub fn new( | ||
| 51 | _peri: impl Peripheral<P = T> + 'd, | ||
| 52 | pin: impl Peripheral<P = impl McoPin<T>> + 'd, | ||
| 53 | source: T::Source, | ||
| 54 | prescaler: u8, | ||
| 55 | ) -> Self { | ||
| 56 | into_ref!(pin); | ||
| 57 | |||
| 58 | assert!( | ||
| 59 | 1 <= prescaler && prescaler <= 15, | ||
| 60 | "Mco prescaler must be between 1 and 15. Refer to the reference manual for more information." | ||
| 61 | ); | ||
| 62 | |||
| 63 | critical_section::with(|_| unsafe { | ||
| 64 | T::apply_clock_settings(source, prescaler); | ||
| 65 | pin.set_as_af(pin.af_num(), AFType::OutputPushPull); | ||
| 66 | pin.set_speed(Speed::VeryHigh); | ||
| 67 | }); | ||
| 68 | |||
| 69 | Self { phantom: PhantomData } | ||
| 70 | } | ||
| 71 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index ff9b9bac8..0d6b0e308 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -1,12 +1,17 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | pub(crate) mod bd; | ||
| 4 | pub mod bus; | ||
| 5 | use core::mem::MaybeUninit; | 3 | use core::mem::MaybeUninit; |
| 6 | 4 | ||
| 7 | pub use crate::rcc::bd::RtcClockSource; | 5 | pub use crate::rcc::bd::RtcClockSource; |
| 8 | use crate::time::Hertz; | 6 | use crate::time::Hertz; |
| 9 | 7 | ||
| 8 | pub(crate) mod bd; | ||
| 9 | mod bus; | ||
| 10 | #[cfg(any(stm32h5, stm32h7))] | ||
| 11 | mod mco; | ||
| 12 | #[cfg(any(stm32h5, stm32h7))] | ||
| 13 | pub use mco::*; | ||
| 14 | |||
| 10 | #[cfg_attr(rcc_f0, path = "f0.rs")] | 15 | #[cfg_attr(rcc_f0, path = "f0.rs")] |
| 11 | #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] | 16 | #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] |
| 12 | #[cfg_attr(rcc_f2, path = "f2.rs")] | 17 | #[cfg_attr(rcc_f2, path = "f2.rs")] |
| @@ -16,7 +21,7 @@ use crate::time::Hertz; | |||
| 16 | #[cfg_attr(rcc_c0, path = "c0.rs")] | 21 | #[cfg_attr(rcc_c0, path = "c0.rs")] |
| 17 | #[cfg_attr(rcc_g0, path = "g0.rs")] | 22 | #[cfg_attr(rcc_g0, path = "g0.rs")] |
| 18 | #[cfg_attr(rcc_g4, path = "g4.rs")] | 23 | #[cfg_attr(rcc_g4, path = "g4.rs")] |
| 19 | #[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")] | 24 | #[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab), path = "h.rs")] |
| 20 | #[cfg_attr(rcc_l0, path = "l0.rs")] | 25 | #[cfg_attr(rcc_l0, path = "l0.rs")] |
| 21 | #[cfg_attr(rcc_l1, path = "l1.rs")] | 26 | #[cfg_attr(rcc_l1, path = "l1.rs")] |
| 22 | #[cfg_attr(rcc_l4, path = "l4.rs")] | 27 | #[cfg_attr(rcc_l4, path = "l4.rs")] |
| @@ -25,7 +30,6 @@ use crate::time::Hertz; | |||
| 25 | #[cfg_attr(rcc_wb, path = "wb.rs")] | 30 | #[cfg_attr(rcc_wb, path = "wb.rs")] |
| 26 | #[cfg_attr(rcc_wba, path = "wba.rs")] | 31 | #[cfg_attr(rcc_wba, path = "wba.rs")] |
| 27 | #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] | 32 | #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] |
| 28 | #[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] | ||
| 29 | mod _version; | 33 | mod _version; |
| 30 | pub use _version::*; | 34 | pub use _version::*; |
| 31 | #[cfg(feature = "low-power")] | 35 | #[cfg(feature = "low-power")] |
| @@ -53,7 +57,7 @@ pub struct Clocks { | |||
| 53 | pub apb2: Hertz, | 57 | pub apb2: Hertz, |
| 54 | #[cfg(not(any(rcc_c0, rcc_g0)))] | 58 | #[cfg(not(any(rcc_c0, rcc_g0)))] |
| 55 | pub apb2_tim: Hertz, | 59 | pub apb2_tim: Hertz, |
| 56 | #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))] | 60 | #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5))] |
| 57 | pub apb3: Hertz, | 61 | pub apb3: Hertz, |
| 58 | #[cfg(any(rcc_h7, rcc_h7ab))] | 62 | #[cfg(any(rcc_h7, rcc_h7ab))] |
| 59 | pub apb4: Hertz, | 63 | pub apb4: Hertz, |
diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 5f1e62d0a..16bf5d949 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs | |||
| @@ -100,6 +100,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 100 | let r = socket.connect(remote_endpoint).await; | 100 | let r = socket.connect(remote_endpoint).await; |
| 101 | if let Err(e) = r { | 101 | if let Err(e) = r { |
| 102 | info!("connect error: {:?}", e); | 102 | info!("connect error: {:?}", e); |
| 103 | Timer::after(Duration::from_secs(1)).await; | ||
| 103 | continue; | 104 | continue; |
| 104 | } | 105 | } |
| 105 | info!("connected!"); | 106 | info!("connected!"); |
| @@ -108,7 +109,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 108 | let r = socket.write_all(&buf).await; | 109 | let r = socket.write_all(&buf).await; |
| 109 | if let Err(e) = r { | 110 | if let Err(e) = r { |
| 110 | info!("write error: {:?}", e); | 111 | info!("write error: {:?}", e); |
| 111 | continue; | 112 | break; |
| 112 | } | 113 | } |
| 113 | Timer::after(Duration::from_secs(1)).await; | 114 | Timer::after(Duration::from_secs(1)).await; |
| 114 | } | 115 | } |
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 01c38106e..93c97c8ee 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -101,6 +101,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 101 | let r = socket.connect(remote_endpoint).await; | 101 | let r = socket.connect(remote_endpoint).await; |
| 102 | if let Err(e) = r { | 102 | if let Err(e) = r { |
| 103 | info!("connect error: {:?}", e); | 103 | info!("connect error: {:?}", e); |
| 104 | Timer::after(Duration::from_secs(1)).await; | ||
| 104 | continue; | 105 | continue; |
| 105 | } | 106 | } |
| 106 | info!("connected!"); | 107 | info!("connected!"); |
| @@ -109,7 +110,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 109 | let r = socket.write_all(&buf).await; | 110 | let r = socket.write_all(&buf).await; |
| 110 | if let Err(e) = r { | 111 | if let Err(e) = r { |
| 111 | info!("write error: {:?}", e); | 112 | info!("write error: {:?}", e); |
| 112 | continue; | 113 | break; |
| 113 | } | 114 | } |
| 114 | Timer::after(Duration::from_secs(1)).await; | 115 | Timer::after(Duration::from_secs(1)).await; |
| 115 | } | 116 | } |
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 41ef2acaa..4e92d0647 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs | |||
| @@ -53,7 +53,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 53 | config.rcc.apb2_pre = APBPrescaler::DIV1; | 53 | config.rcc.apb2_pre = APBPrescaler::DIV1; |
| 54 | config.rcc.apb3_pre = APBPrescaler::DIV1; | 54 | config.rcc.apb3_pre = APBPrescaler::DIV1; |
| 55 | config.rcc.sys = Sysclk::Pll1P; | 55 | config.rcc.sys = Sysclk::Pll1P; |
| 56 | config.rcc.voltage_scale = VoltageScale::SCALE0; | 56 | config.rcc.voltage_scale = VoltageScale::Scale0; |
| 57 | let p = embassy_stm32::init(config); | 57 | let p = embassy_stm32::init(config); |
| 58 | info!("Hello World!"); | 58 | info!("Hello World!"); |
| 59 | 59 | ||
| @@ -128,7 +128,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 128 | let r = socket.write_all(b"Hello\n").await; | 128 | let r = socket.write_all(b"Hello\n").await; |
| 129 | if let Err(e) = r { | 129 | if let Err(e) = r { |
| 130 | info!("write error: {:?}", e); | 130 | info!("write error: {:?}", e); |
| 131 | continue; | 131 | break; |
| 132 | } | 132 | } |
| 133 | Timer::after(Duration::from_secs(1)).await; | 133 | Timer::after(Duration::from_secs(1)).await; |
| 134 | } | 134 | } |
diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 63c694aff..cbe540a06 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs | |||
| @@ -40,7 +40,7 @@ async fn main(_spawner: Spawner) { | |||
| 40 | config.rcc.apb2_pre = APBPrescaler::DIV2; | 40 | config.rcc.apb2_pre = APBPrescaler::DIV2; |
| 41 | config.rcc.apb3_pre = APBPrescaler::DIV4; | 41 | config.rcc.apb3_pre = APBPrescaler::DIV4; |
| 42 | config.rcc.sys = Sysclk::Pll1P; | 42 | config.rcc.sys = Sysclk::Pll1P; |
| 43 | config.rcc.voltage_scale = VoltageScale::SCALE0; | 43 | config.rcc.voltage_scale = VoltageScale::Scale0; |
| 44 | let p = embassy_stm32::init(config); | 44 | let p = embassy_stm32::init(config); |
| 45 | 45 | ||
| 46 | info!("Hello World!"); | 46 | info!("Hello World!"); |
diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index 0e1e28c72..77922d4bc 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs | |||
| @@ -5,8 +5,6 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::adc::{Adc, SampleTime}; | 7 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 8 | use embassy_stm32::rcc::AdcClockSource; | ||
| 9 | use embassy_stm32::time::mhz; | ||
| 10 | use embassy_stm32::Config; | 8 | use embassy_stm32::Config; |
| 11 | use embassy_time::{Delay, Duration, Timer}; | 9 | use embassy_time::{Delay, Duration, Timer}; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -14,10 +12,34 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 14 | #[embassy_executor::main] | 12 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) { | 13 | async fn main(_spawner: Spawner) { |
| 16 | let mut config = Config::default(); | 14 | let mut config = Config::default(); |
| 17 | config.rcc.sys_ck = Some(mhz(400)); | 15 | { |
| 18 | config.rcc.hclk = Some(mhz(200)); | 16 | use embassy_stm32::rcc::*; |
| 19 | config.rcc.per_ck = Some(mhz(64)); | 17 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 20 | config.rcc.adc_clock_source = AdcClockSource::PerCk; | 18 | config.rcc.csi = true; |
| 19 | config.rcc.pll_src = PllSource::Hsi; | ||
| 20 | config.rcc.pll1 = Some(Pll { | ||
| 21 | prediv: 4, | ||
| 22 | mul: 50, | ||
| 23 | divp: Some(2), | ||
| 24 | divq: Some(8), // SPI1 cksel defaults to pll1_q | ||
| 25 | divr: None, | ||
| 26 | }); | ||
| 27 | config.rcc.pll2 = Some(Pll { | ||
| 28 | prediv: 4, | ||
| 29 | mul: 50, | ||
| 30 | divp: Some(8), // 100mhz | ||
| 31 | divq: None, | ||
| 32 | divr: None, | ||
| 33 | }); | ||
| 34 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 35 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 36 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 37 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 38 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 39 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 40 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 41 | config.rcc.adc_clock_source = AdcClockSource::PLL2_P; | ||
| 42 | } | ||
| 21 | let mut p = embassy_stm32::init(config); | 43 | let mut p = embassy_stm32::init(config); |
| 22 | 44 | ||
| 23 | info!("Hello World!"); | 45 | info!("Hello World!"); |
diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 6f75a0630..de8ddc292 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs | |||
| @@ -6,8 +6,8 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::dcmi::{self, *}; | 6 | use embassy_stm32::dcmi::{self, *}; |
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | 7 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 8 | use embassy_stm32::i2c::I2c; | 8 | use embassy_stm32::i2c::I2c; |
| 9 | use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; | 9 | use embassy_stm32::rcc::{Mco, Mco1Source}; |
| 10 | use embassy_stm32::time::{khz, mhz}; | 10 | use embassy_stm32::time::khz; |
| 11 | use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; | 11 | use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; |
| 12 | use embassy_time::{Duration, Timer}; | 12 | use embassy_time::{Duration, Timer}; |
| 13 | use ov7725::*; | 13 | use ov7725::*; |
| @@ -26,17 +26,30 @@ bind_interrupts!(struct Irqs { | |||
| 26 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 27 | async fn main(_spawner: Spawner) { | 27 | async fn main(_spawner: Spawner) { |
| 28 | let mut config = Config::default(); | 28 | let mut config = Config::default(); |
| 29 | config.rcc.sys_ck = Some(mhz(400)); | 29 | { |
| 30 | config.rcc.hclk = Some(mhz(400)); | 30 | use embassy_stm32::rcc::*; |
| 31 | config.rcc.pll1.q_ck = Some(mhz(100)); | 31 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 32 | config.rcc.pclk1 = Some(mhz(100)); | 32 | config.rcc.csi = true; |
| 33 | config.rcc.pclk2 = Some(mhz(100)); | 33 | config.rcc.pll_src = PllSource::Hsi; |
| 34 | config.rcc.pclk3 = Some(mhz(100)); | 34 | config.rcc.pll1 = Some(Pll { |
| 35 | config.rcc.pclk4 = Some(mhz(100)); | 35 | prediv: 4, |
| 36 | mul: 50, | ||
| 37 | divp: Some(2), | ||
| 38 | divq: Some(8), // 100mhz | ||
| 39 | divr: None, | ||
| 40 | }); | ||
| 41 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 42 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 43 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 44 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 45 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 46 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 47 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 48 | } | ||
| 36 | let p = embassy_stm32::init(config); | 49 | let p = embassy_stm32::init(config); |
| 37 | 50 | ||
| 38 | defmt::info!("Hello World!"); | 51 | defmt::info!("Hello World!"); |
| 39 | let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); | 52 | let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 3); |
| 40 | 53 | ||
| 41 | let mut led = Output::new(p.PE3, Level::High, Speed::Low); | 54 | let mut led = Output::new(p.PE3, Level::High, Speed::Low); |
| 42 | let cam_i2c = I2c::new( | 55 | let cam_i2c = I2c::new( |
diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index ee078286b..93df7a319 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs | |||
| @@ -6,7 +6,6 @@ use cortex_m_rt::entry; | |||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; | 7 | use embassy_stm32::dac::{DacCh1, DacChannel, Value}; |
| 8 | use embassy_stm32::dma::NoDma; | 8 | use embassy_stm32::dma::NoDma; |
| 9 | use embassy_stm32::time::mhz; | ||
| 10 | use embassy_stm32::Config; | 9 | use embassy_stm32::Config; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| @@ -15,9 +14,34 @@ fn main() -> ! { | |||
| 15 | info!("Hello World, dude!"); | 14 | info!("Hello World, dude!"); |
| 16 | 15 | ||
| 17 | let mut config = Config::default(); | 16 | let mut config = Config::default(); |
| 18 | config.rcc.sys_ck = Some(mhz(400)); | 17 | { |
| 19 | config.rcc.hclk = Some(mhz(200)); | 18 | use embassy_stm32::rcc::*; |
| 20 | config.rcc.pll1.q_ck = Some(mhz(100)); | 19 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 20 | config.rcc.csi = true; | ||
| 21 | config.rcc.pll_src = PllSource::Hsi; | ||
| 22 | config.rcc.pll1 = Some(Pll { | ||
| 23 | prediv: 4, | ||
| 24 | mul: 50, | ||
| 25 | divp: Some(2), | ||
| 26 | divq: Some(8), // SPI1 cksel defaults to pll1_q | ||
| 27 | divr: None, | ||
| 28 | }); | ||
| 29 | config.rcc.pll2 = Some(Pll { | ||
| 30 | prediv: 4, | ||
| 31 | mul: 50, | ||
| 32 | divp: Some(8), // 100mhz | ||
| 33 | divq: None, | ||
| 34 | divr: None, | ||
| 35 | }); | ||
| 36 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 37 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 38 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 39 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 40 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 41 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 42 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 43 | config.rcc.adc_clock_source = AdcClockSource::PLL2_P; | ||
| 44 | } | ||
| 21 | let p = embassy_stm32::init(config); | 45 | let p = embassy_stm32::init(config); |
| 22 | 46 | ||
| 23 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); | 47 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); |
diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index a9cb5d1ed..8c921abca 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs | |||
| @@ -8,7 +8,7 @@ use embassy_stm32::dac::{DacChannel, ValueArray}; | |||
| 8 | use embassy_stm32::pac::timer::vals::{Mms, Opm}; | 8 | use embassy_stm32::pac::timer::vals::{Mms, Opm}; |
| 9 | use embassy_stm32::peripherals::{TIM6, TIM7}; | 9 | use embassy_stm32::peripherals::{TIM6, TIM7}; |
| 10 | use embassy_stm32::rcc::low_level::RccPeripheral; | 10 | use embassy_stm32::rcc::low_level::RccPeripheral; |
| 11 | use embassy_stm32::time::{mhz, Hertz}; | 11 | use embassy_stm32::time::Hertz; |
| 12 | use embassy_stm32::timer::low_level::Basic16bitInstance; | 12 | use embassy_stm32::timer::low_level::Basic16bitInstance; |
| 13 | use micromath::F32Ext; | 13 | use micromath::F32Ext; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -22,9 +22,34 @@ pub type Dac2Type = | |||
| 22 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 23 | async fn main(spawner: Spawner) { | 23 | async fn main(spawner: Spawner) { |
| 24 | let mut config = embassy_stm32::Config::default(); | 24 | let mut config = embassy_stm32::Config::default(); |
| 25 | config.rcc.sys_ck = Some(mhz(400)); | 25 | { |
| 26 | config.rcc.hclk = Some(mhz(100)); | 26 | use embassy_stm32::rcc::*; |
| 27 | config.rcc.pll1.q_ck = Some(mhz(100)); | 27 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 28 | config.rcc.csi = true; | ||
| 29 | config.rcc.pll_src = PllSource::Hsi; | ||
| 30 | config.rcc.pll1 = Some(Pll { | ||
| 31 | prediv: 4, | ||
| 32 | mul: 50, | ||
| 33 | divp: Some(2), | ||
| 34 | divq: Some(8), // SPI1 cksel defaults to pll1_q | ||
| 35 | divr: None, | ||
| 36 | }); | ||
| 37 | config.rcc.pll2 = Some(Pll { | ||
| 38 | prediv: 4, | ||
| 39 | mul: 50, | ||
| 40 | divp: Some(8), // 100mhz | ||
| 41 | divq: None, | ||
| 42 | divr: None, | ||
| 43 | }); | ||
| 44 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 45 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 46 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 47 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 48 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 49 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 50 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 51 | config.rcc.adc_clock_source = AdcClockSource::PLL2_P; | ||
| 52 | } | ||
| 28 | 53 | ||
| 29 | // Initialize the board and obtain a Peripherals instance | 54 | // Initialize the board and obtain a Peripherals instance |
| 30 | let p: embassy_stm32::Peripherals = embassy_stm32::init(config); | 55 | let p: embassy_stm32::Peripherals = embassy_stm32::init(config); |
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index e691c6d06..1b5d71ed3 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs | |||
| @@ -10,7 +10,6 @@ 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; | ||
| 14 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 13 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 15 | use embassy_time::{Duration, Timer}; | 14 | use embassy_time::{Duration, Timer}; |
| 16 | use embedded_io_async::Write; | 15 | use embedded_io_async::Write; |
| @@ -33,9 +32,27 @@ async fn net_task(stack: &'static Stack<Device>) -> ! { | |||
| 33 | #[embassy_executor::main] | 32 | #[embassy_executor::main] |
| 34 | async fn main(spawner: Spawner) -> ! { | 33 | async fn main(spawner: Spawner) -> ! { |
| 35 | let mut config = Config::default(); | 34 | let mut config = Config::default(); |
| 36 | config.rcc.sys_ck = Some(mhz(400)); | 35 | { |
| 37 | config.rcc.hclk = Some(mhz(200)); | 36 | use embassy_stm32::rcc::*; |
| 38 | config.rcc.pll1.q_ck = Some(mhz(100)); | 37 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 38 | config.rcc.csi = true; | ||
| 39 | config.rcc.hsi48 = true; // needed for RNG | ||
| 40 | config.rcc.pll_src = PllSource::Hsi; | ||
| 41 | config.rcc.pll1 = Some(Pll { | ||
| 42 | prediv: 4, | ||
| 43 | mul: 50, | ||
| 44 | divp: Some(2), | ||
| 45 | divq: None, | ||
| 46 | divr: None, | ||
| 47 | }); | ||
| 48 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 49 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 50 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 51 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 52 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 53 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 54 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 55 | } | ||
| 39 | let p = embassy_stm32::init(config); | 56 | let p = embassy_stm32::init(config); |
| 40 | info!("Hello World!"); | 57 | info!("Hello World!"); |
| 41 | 58 | ||
| @@ -102,6 +119,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 102 | let r = socket.connect(remote_endpoint).await; | 119 | let r = socket.connect(remote_endpoint).await; |
| 103 | if let Err(e) = r { | 120 | if let Err(e) = r { |
| 104 | info!("connect error: {:?}", e); | 121 | info!("connect error: {:?}", e); |
| 122 | Timer::after(Duration::from_secs(1)).await; | ||
| 105 | continue; | 123 | continue; |
| 106 | } | 124 | } |
| 107 | info!("connected!"); | 125 | info!("connected!"); |
| @@ -109,7 +127,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 109 | let r = socket.write_all(b"Hello\n").await; | 127 | let r = socket.write_all(b"Hello\n").await; |
| 110 | if let Err(e) = r { | 128 | if let Err(e) = r { |
| 111 | info!("write error: {:?}", e); | 129 | info!("write error: {:?}", e); |
| 112 | continue; | 130 | break; |
| 113 | } | 131 | } |
| 114 | Timer::after(Duration::from_secs(1)).await; | 132 | Timer::after(Duration::from_secs(1)).await; |
| 115 | } | 133 | } |
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index ebef54c3c..3abd31c73 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs | |||
| @@ -10,7 +10,6 @@ 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; | ||
| 14 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 13 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 15 | use embassy_time::{Duration, Timer}; | 14 | use embassy_time::{Duration, Timer}; |
| 16 | use embedded_io_async::Write; | 15 | use embedded_io_async::Write; |
| @@ -34,9 +33,27 @@ async fn net_task(stack: &'static Stack<Device>) -> ! { | |||
| 34 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 35 | async fn main(spawner: Spawner) -> ! { | 34 | async fn main(spawner: Spawner) -> ! { |
| 36 | let mut config = Config::default(); | 35 | let mut config = Config::default(); |
| 37 | config.rcc.sys_ck = Some(mhz(400)); | 36 | { |
| 38 | config.rcc.hclk = Some(mhz(200)); | 37 | use embassy_stm32::rcc::*; |
| 39 | config.rcc.pll1.q_ck = Some(mhz(100)); | 38 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 39 | config.rcc.csi = true; | ||
| 40 | config.rcc.hsi48 = true; // needed for RNG | ||
| 41 | config.rcc.pll_src = PllSource::Hsi; | ||
| 42 | config.rcc.pll1 = Some(Pll { | ||
| 43 | prediv: 4, | ||
| 44 | mul: 50, | ||
| 45 | divp: Some(2), | ||
| 46 | divq: None, | ||
| 47 | divr: None, | ||
| 48 | }); | ||
| 49 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 50 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 51 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 52 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 53 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 54 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 55 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 56 | } | ||
| 40 | let p = embassy_stm32::init(config); | 57 | let p = embassy_stm32::init(config); |
| 41 | info!("Hello World!"); | 58 | info!("Hello World!"); |
| 42 | 59 | ||
| @@ -108,7 +125,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 108 | let r = connection.write_all(b"Hello\n").await; | 125 | let r = connection.write_all(b"Hello\n").await; |
| 109 | if let Err(e) = r { | 126 | if let Err(e) = r { |
| 110 | info!("write error: {:?}", e); | 127 | info!("write error: {:?}", e); |
| 111 | continue; | 128 | break; |
| 112 | } | 129 | } |
| 113 | Timer::after(Duration::from_secs(1)).await; | 130 | Timer::after(Duration::from_secs(1)).await; |
| 114 | } | 131 | } |
diff --git a/examples/stm32h7/src/bin/fmc.rs b/examples/stm32h7/src/bin/fmc.rs index 85c690fe6..de0b351df 100644 --- a/examples/stm32h7/src/bin/fmc.rs +++ b/examples/stm32h7/src/bin/fmc.rs | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::fmc::Fmc; | 7 | use embassy_stm32::fmc::Fmc; |
| 8 | use embassy_stm32::time::mhz; | ||
| 9 | use embassy_stm32::Config; | 8 | use embassy_stm32::Config; |
| 10 | use embassy_time::{Delay, Duration, Timer}; | 9 | use embassy_time::{Delay, Duration, Timer}; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -13,9 +12,26 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 13 | #[embassy_executor::main] | 12 | #[embassy_executor::main] |
| 14 | async fn main(_spawner: Spawner) { | 13 | async fn main(_spawner: Spawner) { |
| 15 | let mut config = Config::default(); | 14 | let mut config = Config::default(); |
| 16 | config.rcc.sys_ck = Some(mhz(400)); | 15 | { |
| 17 | config.rcc.hclk = Some(mhz(200)); | 16 | use embassy_stm32::rcc::*; |
| 18 | config.rcc.pll1.q_ck = Some(mhz(100)); | 17 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 18 | config.rcc.csi = true; | ||
| 19 | config.rcc.pll_src = PllSource::Hsi; | ||
| 20 | config.rcc.pll1 = Some(Pll { | ||
| 21 | prediv: 4, | ||
| 22 | mul: 50, | ||
| 23 | divp: Some(2), | ||
| 24 | divq: Some(8), // 100mhz | ||
| 25 | divr: None, | ||
| 26 | }); | ||
| 27 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 28 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 29 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 30 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 31 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 32 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 33 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 34 | } | ||
| 19 | let p = embassy_stm32::init(config); | 35 | let p = embassy_stm32::init(config); |
| 20 | 36 | ||
| 21 | info!("Hello World!"); | 37 | info!("Hello World!"); |
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 45b0872b5..a1e955c39 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs | |||
| @@ -6,7 +6,7 @@ use defmt::*; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::gpio::low_level::AFType; | 7 | use embassy_stm32::gpio::low_level::AFType; |
| 8 | use embassy_stm32::gpio::Speed; | 8 | use embassy_stm32::gpio::Speed; |
| 9 | use embassy_stm32::time::{khz, mhz, Hertz}; | 9 | use embassy_stm32::time::{khz, Hertz}; |
| 10 | use embassy_stm32::timer::*; | 10 | use embassy_stm32::timer::*; |
| 11 | use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; | 11 | use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; |
| 12 | use embassy_time::{Duration, Timer}; | 12 | use embassy_time::{Duration, Timer}; |
| @@ -15,13 +15,27 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 17 | let mut config = Config::default(); | 17 | let mut config = Config::default(); |
| 18 | config.rcc.sys_ck = Some(mhz(400)); | 18 | { |
| 19 | config.rcc.hclk = Some(mhz(400)); | 19 | use embassy_stm32::rcc::*; |
| 20 | config.rcc.pll1.q_ck = Some(mhz(100)); | 20 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 21 | config.rcc.pclk1 = Some(mhz(100)); | 21 | config.rcc.csi = true; |
| 22 | config.rcc.pclk2 = Some(mhz(100)); | 22 | config.rcc.hsi48 = true; // needed for RNG |
| 23 | config.rcc.pclk3 = Some(mhz(100)); | 23 | config.rcc.pll_src = PllSource::Hsi; |
| 24 | config.rcc.pclk4 = Some(mhz(100)); | 24 | config.rcc.pll1 = Some(Pll { |
| 25 | prediv: 4, | ||
| 26 | mul: 50, | ||
| 27 | divp: Some(2), | ||
| 28 | divq: Some(8), // 100 Mhz | ||
| 29 | divr: None, | ||
| 30 | }); | ||
| 31 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 32 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 33 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 34 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 35 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 36 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 37 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 38 | } | ||
| 25 | let p = embassy_stm32::init(config); | 39 | let p = embassy_stm32::init(config); |
| 26 | 40 | ||
| 27 | info!("Hello World!"); | 41 | info!("Hello World!"); |
diff --git a/examples/stm32h7/src/bin/mco.rs b/examples/stm32h7/src/bin/mco.rs index 036455d5e..9d6d805ae 100644 --- a/examples/stm32h7/src/bin/mco.rs +++ b/examples/stm32h7/src/bin/mco.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::gpio::{Level, Output, Speed}; | 7 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 8 | use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; | 8 | use embassy_stm32::rcc::{Mco, Mco1Source}; |
| 9 | use embassy_time::{Duration, Timer}; | 9 | use embassy_time::{Duration, Timer}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { | |||
| 16 | 16 | ||
| 17 | let mut led = Output::new(p.PB14, Level::High, Speed::Low); | 17 | let mut led = Output::new(p.PB14, Level::High, Speed::Low); |
| 18 | 18 | ||
| 19 | let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(8)); | 19 | let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 8); |
| 20 | 20 | ||
| 21 | loop { | 21 | loop { |
| 22 | info!("high"); | 22 | info!("high"); |
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index aa5ec1bcf..5c8e57aa2 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.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::gpio::OutputType; | 7 | use embassy_stm32::gpio::OutputType; |
| 8 | use embassy_stm32::time::{khz, mhz}; | 8 | use embassy_stm32::time::khz; |
| 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 10 | use embassy_stm32::timer::Channel; | 10 | use embassy_stm32::timer::Channel; |
| 11 | use embassy_stm32::Config; | 11 | use embassy_stm32::Config; |
| @@ -15,13 +15,26 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 17 | let mut config = Config::default(); | 17 | let mut config = Config::default(); |
| 18 | config.rcc.sys_ck = Some(mhz(400)); | 18 | { |
| 19 | config.rcc.hclk = Some(mhz(400)); | 19 | use embassy_stm32::rcc::*; |
| 20 | config.rcc.pll1.q_ck = Some(mhz(100)); | 20 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 21 | config.rcc.pclk1 = Some(mhz(100)); | 21 | config.rcc.csi = true; |
| 22 | config.rcc.pclk2 = Some(mhz(100)); | 22 | config.rcc.pll_src = PllSource::Hsi; |
| 23 | config.rcc.pclk3 = Some(mhz(100)); | 23 | config.rcc.pll1 = Some(Pll { |
| 24 | config.rcc.pclk4 = Some(mhz(100)); | 24 | prediv: 4, |
| 25 | mul: 50, | ||
| 26 | divp: Some(2), | ||
| 27 | divq: None, | ||
| 28 | divr: None, | ||
| 29 | }); | ||
| 30 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 31 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 32 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 33 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 34 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 35 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 36 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 37 | } | ||
| 25 | let p = embassy_stm32::init(config); | 38 | let p = embassy_stm32::init(config); |
| 26 | info!("Hello World!"); | 39 | info!("Hello World!"); |
| 27 | 40 | ||
diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index 7c8c50eca..af1d6ebb8 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.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::rng::Rng; | 7 | use embassy_stm32::rng::Rng; |
| 8 | use embassy_stm32::{bind_interrupts, peripherals, rng}; | 8 | use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | 11 | bind_interrupts!(struct Irqs { |
| @@ -14,7 +14,9 @@ bind_interrupts!(struct Irqs { | |||
| 14 | 14 | ||
| 15 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 17 | let p = embassy_stm32::init(Default::default()); | 17 | let mut config = Config::default(); |
| 18 | config.rcc.hsi48 = true; // needed for RNG. | ||
| 19 | let p = embassy_stm32::init(config); | ||
| 18 | info!("Hello World!"); | 20 | info!("Hello World!"); |
| 19 | 21 | ||
| 20 | let mut rng = Rng::new(p.RNG, Irqs); | 22 | let mut rng = Rng::new(p.RNG, Irqs); |
diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index ce91b6b1c..752aefdf7 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs | |||
| @@ -16,7 +16,26 @@ 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 | use embassy_stm32::rcc::*; | ||
| 21 | config.rcc.hsi = Some(Hsi::Mhz64); | ||
| 22 | config.rcc.csi = true; | ||
| 23 | config.rcc.pll_src = PllSource::Hsi; | ||
| 24 | config.rcc.pll1 = Some(Pll { | ||
| 25 | prediv: 4, | ||
| 26 | mul: 50, | ||
| 27 | divp: Some(2), | ||
| 28 | divq: Some(4), // default clock chosen by SDMMCSEL. 200 Mhz | ||
| 29 | divr: None, | ||
| 30 | }); | ||
| 31 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 32 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 33 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 34 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 35 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 36 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 37 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 38 | } | ||
| 20 | let p = embassy_stm32::init(config); | 39 | let p = embassy_stm32::init(config); |
| 21 | info!("Hello World!"); | 40 | info!("Hello World!"); |
| 22 | 41 | ||
diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 28bba2b8d..9fe46f031 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs | |||
| @@ -38,9 +38,26 @@ fn main() -> ! { | |||
| 38 | info!("Hello World!"); | 38 | info!("Hello World!"); |
| 39 | 39 | ||
| 40 | let mut config = Config::default(); | 40 | let mut config = Config::default(); |
| 41 | config.rcc.sys_ck = Some(mhz(400)); | 41 | { |
| 42 | config.rcc.hclk = Some(mhz(200)); | 42 | use embassy_stm32::rcc::*; |
| 43 | config.rcc.pll1.q_ck = Some(mhz(100)); | 43 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 44 | config.rcc.csi = true; | ||
| 45 | config.rcc.pll_src = PllSource::Hsi; | ||
| 46 | config.rcc.pll1 = Some(Pll { | ||
| 47 | prediv: 4, | ||
| 48 | mul: 50, | ||
| 49 | divp: Some(2), | ||
| 50 | divq: Some(4), // used by SPI3. 100Mhz. | ||
| 51 | divr: None, | ||
| 52 | }); | ||
| 53 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 54 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 55 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 56 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 57 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 58 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 59 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 60 | } | ||
| 44 | let p = embassy_stm32::init(config); | 61 | let p = embassy_stm32::init(config); |
| 45 | 62 | ||
| 46 | let mut spi_config = spi::Config::default(); | 63 | let mut spi_config = spi::Config::default(); |
diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index f6e30cfa5..88d65d5be 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs | |||
| @@ -34,9 +34,26 @@ fn main() -> ! { | |||
| 34 | info!("Hello World!"); | 34 | info!("Hello World!"); |
| 35 | 35 | ||
| 36 | let mut config = Config::default(); | 36 | let mut config = Config::default(); |
| 37 | config.rcc.sys_ck = Some(mhz(400)); | 37 | { |
| 38 | config.rcc.hclk = Some(mhz(200)); | 38 | use embassy_stm32::rcc::*; |
| 39 | config.rcc.pll1.q_ck = Some(mhz(100)); | 39 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 40 | config.rcc.csi = true; | ||
| 41 | config.rcc.pll_src = PllSource::Hsi; | ||
| 42 | config.rcc.pll1 = Some(Pll { | ||
| 43 | prediv: 4, | ||
| 44 | mul: 50, | ||
| 45 | divp: Some(2), | ||
| 46 | divq: Some(4), // used by SPI3. 100Mhz. | ||
| 47 | divr: None, | ||
| 48 | }); | ||
| 49 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 50 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 51 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 52 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 53 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 54 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 55 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 56 | } | ||
| 40 | let p = embassy_stm32::init(config); | 57 | let p = embassy_stm32::init(config); |
| 41 | 58 | ||
| 42 | let mut spi_config = spi::Config::default(); | 59 | let mut spi_config = spi::Config::default(); |
diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 97291f60c..14de43568 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs | |||
| @@ -4,7 +4,6 @@ | |||
| 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; | ||
| 8 | use embassy_stm32::usb_otg::{Driver, Instance}; | 7 | use embassy_stm32::usb_otg::{Driver, Instance}; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | 8 | use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; |
| 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | 9 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; |
| @@ -22,9 +21,27 @@ async fn main(_spawner: Spawner) { | |||
| 22 | info!("Hello World!"); | 21 | info!("Hello World!"); |
| 23 | 22 | ||
| 24 | let mut config = Config::default(); | 23 | let mut config = Config::default(); |
| 25 | config.rcc.sys_ck = Some(mhz(400)); | 24 | { |
| 26 | config.rcc.hclk = Some(mhz(200)); | 25 | use embassy_stm32::rcc::*; |
| 27 | config.rcc.pll1.q_ck = Some(mhz(100)); | 26 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 27 | config.rcc.csi = true; | ||
| 28 | config.rcc.hsi48 = true; // needed for USB | ||
| 29 | config.rcc.pll_src = PllSource::Hsi; | ||
| 30 | config.rcc.pll1 = Some(Pll { | ||
| 31 | prediv: 4, | ||
| 32 | mul: 50, | ||
| 33 | divp: Some(2), | ||
| 34 | divq: None, | ||
| 35 | divr: None, | ||
| 36 | }); | ||
| 37 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 38 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 39 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 40 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 41 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 42 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 43 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 44 | } | ||
| 28 | let p = embassy_stm32::init(config); | 45 | let p = embassy_stm32::init(config); |
| 29 | 46 | ||
| 30 | // Create the driver, from the HAL. | 47 | // Create the driver, from the HAL. |
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index ca5cb43ac..3a1b5c3ec 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -31,9 +31,32 @@ pub fn config() -> Config { | |||
| 31 | 31 | ||
| 32 | #[cfg(feature = "stm32h755zi")] | 32 | #[cfg(feature = "stm32h755zi")] |
| 33 | { | 33 | { |
| 34 | config.rcc.sys_ck = Some(Hertz(400_000_000)); | 34 | use embassy_stm32::rcc::*; |
| 35 | config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); | 35 | config.rcc.hsi = Some(Hsi::Mhz64); |
| 36 | config.rcc.adc_clock_source = embassy_stm32::rcc::AdcClockSource::PerCk; | 36 | config.rcc.csi = true; |
| 37 | config.rcc.pll_src = PllSource::Hsi; | ||
| 38 | config.rcc.pll1 = Some(Pll { | ||
| 39 | prediv: 4, | ||
| 40 | mul: 50, | ||
| 41 | divp: Some(2), | ||
| 42 | divq: Some(8), // SPI1 cksel defaults to pll1_q | ||
| 43 | divr: None, | ||
| 44 | }); | ||
| 45 | config.rcc.pll2 = Some(Pll { | ||
| 46 | prediv: 4, | ||
| 47 | mul: 50, | ||
| 48 | divp: Some(8), // 100mhz | ||
| 49 | divq: None, | ||
| 50 | divr: None, | ||
| 51 | }); | ||
| 52 | config.rcc.sys = Sysclk::Pll1P; // 400 Mhz | ||
| 53 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 54 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 55 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 56 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 57 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 58 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 59 | config.rcc.adc_clock_source = AdcClockSource::PLL2_P; | ||
| 37 | } | 60 | } |
| 38 | 61 | ||
| 39 | #[cfg(feature = "stm32u585ai")] | 62 | #[cfg(feature = "stm32u585ai")] |
