diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-03-04 00:26:21 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-03-04 00:26:21 +0100 |
| commit | 02a0a15976ca815924a3780f80a7f28b90717a2b (patch) | |
| tree | 66fa6a524c6ce25b0f883639c40dc1a64c24a047 | |
| parent | 20760ff4f7dcbca8b1654957462df4551883932d (diff) | |
| parent | ae266f3bf528c334c4712cd37305d2bcb71c0936 (diff) | |
Merge pull request #2656 from embassy-rs/rcc-gc0
stm32/rcc: port g0, c0 to new api. Add c0 HSIKER/HSISYS support.
| -rw-r--r-- | embassy-stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/c0.rs | 224 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/g0.rs | 482 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/g4.rs | 218 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/hf_timer.rs | 15 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc.rs | 2 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/can.rs | 2 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/pll.rs | 29 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/usb_serial.rs | 2 | ||||
| -rw-r--r-- | tests/stm32/src/common.rs | 26 |
10 files changed, 520 insertions, 484 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4bbd43c47..b326c26fd 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -70,7 +70,7 @@ rand_core = "0.6.3" | |||
| 70 | sdio-host = "0.5.0" | 70 | sdio-host = "0.5.0" |
| 71 | critical-section = "1.1" | 71 | critical-section = "1.1" |
| 72 | #stm32-metapac = { version = "15" } | 72 | #stm32-metapac = { version = "15" } |
| 73 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e853cf944b150898312984d092d63926970c340d" } | 73 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085" } |
| 74 | vcell = "0.1.3" | 74 | vcell = "0.1.3" |
| 75 | bxcan = "0.7.0" | 75 | bxcan = "0.7.0" |
| 76 | nb = "1.0.0" | 76 | nb = "1.0.0" |
| @@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] } | |||
| 94 | proc-macro2 = "1.0.36" | 94 | proc-macro2 = "1.0.36" |
| 95 | quote = "1.0.15" | 95 | quote = "1.0.15" |
| 96 | #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} | 96 | #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} |
| 97 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e853cf944b150898312984d092d63926970c340d", default-features = false, features = ["metadata"]} | 97 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085", default-features = false, features = ["metadata"]} |
| 98 | 98 | ||
| 99 | 99 | ||
| 100 | [features] | 100 | [features] |
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 1946c5a15..7ca737bf0 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs | |||
| @@ -1,25 +1,56 @@ | |||
| 1 | use crate::pac::flash::vals::Latency; | 1 | use crate::pac::flash::vals::Latency; |
| 2 | use crate::pac::rcc::vals::Sw; | 2 | pub use crate::pac::rcc::vals::{ |
| 3 | pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Ppre as APBPrescaler}; | 3 | Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Hsikerdiv as HsiKerDiv, Ppre as APBPrescaler, Sw as Sysclk, |
| 4 | }; | ||
| 4 | use crate::pac::{FLASH, RCC}; | 5 | use crate::pac::{FLASH, RCC}; |
| 5 | use crate::time::Hertz; | 6 | use crate::time::Hertz; |
| 6 | 7 | ||
| 7 | /// HSI speed | 8 | /// HSI speed |
| 8 | pub const HSI_FREQ: Hertz = Hertz(48_000_000); | 9 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 9 | 10 | ||
| 10 | /// System clock mux source | 11 | /// HSE Mode |
| 11 | #[derive(Clone, Copy)] | 12 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 12 | pub enum Sysclk { | 13 | pub enum HseMode { |
| 13 | HSE(Hertz), | 14 | /// crystal/ceramic oscillator (HSEBYP=0) |
| 14 | HSI(HSIPrescaler), | 15 | Oscillator, |
| 15 | LSI, | 16 | /// external analog clock (low swing) (HSEBYP=1) |
| 17 | Bypass, | ||
| 18 | } | ||
| 19 | |||
| 20 | /// HSE Configuration | ||
| 21 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 22 | pub struct Hse { | ||
| 23 | /// HSE frequency. | ||
| 24 | pub freq: Hertz, | ||
| 25 | /// HSE mode. | ||
| 26 | pub mode: HseMode, | ||
| 27 | } | ||
| 28 | |||
| 29 | /// HSI Configuration | ||
| 30 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 31 | pub struct Hsi { | ||
| 32 | /// Division factor for HSISYS clock. Default is 4. | ||
| 33 | pub sys_div: HsiSysDiv, | ||
| 34 | /// Division factor for HSIKER clock. Default is 3. | ||
| 35 | pub ker_div: HsiKerDiv, | ||
| 16 | } | 36 | } |
| 17 | 37 | ||
| 18 | /// Clocks configutation | 38 | /// Clocks configutation |
| 39 | #[non_exhaustive] | ||
| 19 | pub struct Config { | 40 | pub struct Config { |
| 41 | /// HSI Configuration | ||
| 42 | pub hsi: Option<Hsi>, | ||
| 43 | |||
| 44 | /// HSE Configuration | ||
| 45 | pub hse: Option<Hse>, | ||
| 46 | |||
| 47 | /// System Clock Configuration | ||
| 20 | pub sys: Sysclk, | 48 | pub sys: Sysclk, |
| 49 | |||
| 21 | pub ahb_pre: AHBPrescaler, | 50 | pub ahb_pre: AHBPrescaler, |
| 22 | pub apb_pre: APBPrescaler, | 51 | pub apb1_pre: APBPrescaler, |
| 52 | |||
| 53 | /// Low-Speed Clock Configuration | ||
| 23 | pub ls: super::LsConfig, | 54 | pub ls: super::LsConfig, |
| 24 | 55 | ||
| 25 | /// Per-peripheral kernel clock selection muxes | 56 | /// Per-peripheral kernel clock selection muxes |
| @@ -30,9 +61,14 @@ impl Default for Config { | |||
| 30 | #[inline] | 61 | #[inline] |
| 31 | fn default() -> Config { | 62 | fn default() -> Config { |
| 32 | Config { | 63 | Config { |
| 33 | sys: Sysclk::HSI(HSIPrescaler::DIV1), | 64 | hsi: Some(Hsi { |
| 65 | sys_div: HsiSysDiv::DIV4, | ||
| 66 | ker_div: HsiKerDiv::DIV3, | ||
| 67 | }), | ||
| 68 | hse: None, | ||
| 69 | sys: Sysclk::HSISYS, | ||
| 34 | ahb_pre: AHBPrescaler::DIV1, | 70 | ahb_pre: AHBPrescaler::DIV1, |
| 35 | apb_pre: APBPrescaler::DIV1, | 71 | apb1_pre: APBPrescaler::DIV1, |
| 36 | ls: Default::default(), | 72 | ls: Default::default(), |
| 37 | mux: Default::default(), | 73 | mux: Default::default(), |
| 38 | } | 74 | } |
| @@ -40,111 +76,109 @@ impl Default for Config { | |||
| 40 | } | 76 | } |
| 41 | 77 | ||
| 42 | pub(crate) unsafe fn init(config: Config) { | 78 | pub(crate) unsafe fn init(config: Config) { |
| 43 | let (sys_clk, sw) = match config.sys { | 79 | // Configure HSI |
| 44 | Sysclk::HSI(div) => { | 80 | let (hsi, hsisys, hsiker) = match config.hsi { |
| 45 | // Enable HSI | 81 | None => { |
| 46 | RCC.cr().write(|w| { | 82 | RCC.cr().modify(|w| w.set_hsion(false)); |
| 47 | w.set_hsidiv(div); | 83 | (None, None, None) |
| 48 | w.set_hsion(true) | 84 | } |
| 85 | Some(hsi) => { | ||
| 86 | RCC.cr().modify(|w| { | ||
| 87 | w.set_hsidiv(hsi.sys_div); | ||
| 88 | w.set_hsikerdiv(hsi.ker_div); | ||
| 89 | w.set_hsion(true); | ||
| 49 | }); | 90 | }); |
| 50 | while !RCC.cr().read().hsirdy() {} | 91 | while !RCC.cr().read().hsirdy() {} |
| 51 | 92 | ( | |
| 52 | (HSI_FREQ / div, Sw::HSI) | 93 | Some(HSI_FREQ), |
| 94 | Some(HSI_FREQ / hsi.sys_div), | ||
| 95 | Some(HSI_FREQ / hsi.ker_div), | ||
| 96 | ) | ||
| 53 | } | 97 | } |
| 54 | Sysclk::HSE(freq) => { | 98 | }; |
| 55 | // Enable HSE | ||
| 56 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 57 | while !RCC.cr().read().hserdy() {} | ||
| 58 | 99 | ||
| 59 | (freq, Sw::HSE) | 100 | // Configure HSE |
| 101 | let hse = match config.hse { | ||
| 102 | None => { | ||
| 103 | RCC.cr().modify(|w| w.set_hseon(false)); | ||
| 104 | None | ||
| 60 | } | 105 | } |
| 61 | Sysclk::LSI => { | 106 | Some(hse) => { |
| 62 | // Enable LSI | 107 | match hse.mode { |
| 63 | RCC.csr2().write(|w| w.set_lsion(true)); | 108 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), |
| 64 | while !RCC.csr2().read().lsirdy() {} | 109 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), |
| 65 | (super::LSI_FREQ, Sw::LSI) | 110 | } |
| 111 | |||
| 112 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | ||
| 113 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 114 | while !RCC.cr().read().hserdy() {} | ||
| 115 | Some(hse.freq) | ||
| 66 | } | 116 | } |
| 67 | }; | 117 | }; |
| 68 | 118 | ||
| 69 | let rtc = config.ls.init(); | 119 | let sys = match config.sys { |
| 120 | Sysclk::HSISYS => unwrap!(hsisys), | ||
| 121 | Sysclk::HSE => unwrap!(hse), | ||
| 122 | _ => unreachable!(), | ||
| 123 | }; | ||
| 70 | 124 | ||
| 71 | // Determine the flash latency implied by the target clock speed | 125 | assert!(max::SYSCLK.contains(&sys)); |
| 72 | // RM0454 § 3.3.4: | 126 | |
| 73 | let target_flash_latency = if sys_clk <= Hertz(24_000_000) { | 127 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. |
| 74 | Latency::WS0 | 128 | let hclk = sys / config.ahb_pre; |
| 75 | } else { | 129 | assert!(max::HCLK.contains(&hclk)); |
| 76 | Latency::WS1 | 130 | |
| 131 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); | ||
| 132 | assert!(max::PCLK.contains(&pclk1)); | ||
| 133 | |||
| 134 | let latency = match hclk.0 { | ||
| 135 | ..=24_000_000 => Latency::WS0, | ||
| 136 | _ => Latency::WS1, | ||
| 77 | }; | 137 | }; |
| 78 | 138 | ||
| 79 | // Increase the number of cycles we wait for flash if the new value is higher | 139 | // Configure flash read access latency based on voltage scale and frequency |
| 80 | // There's no harm in waiting a little too much before the clock change, but we'll | ||
| 81 | // crash immediately if we don't wait enough after the clock change | ||
| 82 | let mut set_flash_latency_after = false; | ||
| 83 | FLASH.acr().modify(|w| { | 140 | FLASH.acr().modify(|w| { |
| 84 | // Is the current flash latency less than what we need at the new SYSCLK? | 141 | w.set_latency(latency); |
| 85 | if w.latency().to_bits() <= target_flash_latency.to_bits() { | ||
| 86 | // We must increase the number of wait states now | ||
| 87 | w.set_latency(target_flash_latency) | ||
| 88 | } else { | ||
| 89 | // We may decrease the number of wait states later | ||
| 90 | set_flash_latency_after = true; | ||
| 91 | } | ||
| 92 | |||
| 93 | // RM0490 § 3.3.4: | ||
| 94 | // > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register | ||
| 95 | // > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the | ||
| 96 | // > Flash memory. | ||
| 97 | // | ||
| 98 | // Enable flash prefetching if we have at least one wait state, and disable it otherwise. | ||
| 99 | w.set_prften(target_flash_latency.to_bits() > 0); | ||
| 100 | }); | 142 | }); |
| 101 | 143 | ||
| 102 | if !set_flash_latency_after { | 144 | // Spin until the effective flash latency is set. |
| 103 | // Spin until the effective flash latency is compatible with the clock change | 145 | while FLASH.acr().read().latency() != latency {} |
| 104 | while FLASH.acr().read().latency() < target_flash_latency {} | ||
| 105 | } | ||
| 106 | 146 | ||
| 107 | // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once | 147 | // Now that boost mode and flash read access latency are configured, set up SYSCLK |
| 108 | RCC.cfgr().modify(|w| { | 148 | RCC.cfgr().modify(|w| { |
| 109 | w.set_sw(sw); | 149 | w.set_sw(config.sys); |
| 110 | w.set_hpre(config.ahb_pre); | 150 | w.set_hpre(config.ahb_pre); |
| 111 | w.set_ppre(config.apb_pre); | 151 | w.set_ppre(config.apb1_pre); |
| 112 | }); | 152 | }); |
| 113 | // Spin until the SYSCLK changes have taken effect | ||
| 114 | loop { | ||
| 115 | let cfgr = RCC.cfgr().read(); | ||
| 116 | if cfgr.sw() == sw && cfgr.hpre() == config.ahb_pre && cfgr.ppre() == config.apb_pre { | ||
| 117 | break; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | 153 | ||
| 121 | // Set the flash latency to require fewer wait states | 154 | let rtc = config.ls.init(); |
| 122 | if set_flash_latency_after { | ||
| 123 | FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); | ||
| 124 | } | ||
| 125 | |||
| 126 | let ahb_freq = sys_clk / config.ahb_pre; | ||
| 127 | |||
| 128 | let (apb_freq, apb_tim_freq) = match config.apb_pre { | ||
| 129 | APBPrescaler::DIV1 => (ahb_freq, ahb_freq), | ||
| 130 | pre => { | ||
| 131 | let freq = ahb_freq / pre; | ||
| 132 | (freq, freq * 2u32) | ||
| 133 | } | ||
| 134 | }; | ||
| 135 | 155 | ||
| 136 | config.mux.init(); | 156 | config.mux.init(); |
| 137 | 157 | ||
| 138 | // without this, the ringbuffered uart test fails. | ||
| 139 | cortex_m::asm::dsb(); | ||
| 140 | |||
| 141 | set_clocks!( | 158 | set_clocks!( |
| 142 | hsi: None, | 159 | sys: Some(sys), |
| 143 | lse: None, | 160 | hclk1: Some(hclk), |
| 144 | sys: Some(sys_clk), | 161 | pclk1: Some(pclk1), |
| 145 | hclk1: Some(ahb_freq), | 162 | pclk1_tim: Some(pclk1_tim), |
| 146 | pclk1: Some(apb_freq), | 163 | hsi: hsi, |
| 147 | pclk1_tim: Some(apb_tim_freq), | 164 | hsiker: hsiker, |
| 165 | hse: hse, | ||
| 148 | rtc: rtc, | 166 | rtc: rtc, |
| 167 | |||
| 168 | // TODO | ||
| 169 | lsi: None, | ||
| 170 | lse: None, | ||
| 149 | ); | 171 | ); |
| 150 | } | 172 | } |
| 173 | |||
| 174 | mod max { | ||
| 175 | use core::ops::RangeInclusive; | ||
| 176 | |||
| 177 | use crate::time::Hertz; | ||
| 178 | |||
| 179 | pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000); | ||
| 180 | pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000); | ||
| 181 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000); | ||
| 182 | pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(8)..=Hertz(48_000_000); | ||
| 183 | pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000); | ||
| 184 | } | ||
diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 5cfe9953b..ea4422ccc 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | use crate::pac::flash::vals::Latency; | 1 | use crate::pac::flash::vals::Latency; |
| 2 | use crate::pac::rcc::vals::{self, Sw}; | 2 | pub use crate::pac::pwr::vals::Vos as VoltageRange; |
| 3 | pub use crate::pac::rcc::vals::{ | 3 | pub use crate::pac::rcc::vals::{ |
| 4 | Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler, | 4 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, |
| 5 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | ||
| 5 | }; | 6 | }; |
| 6 | use crate::pac::{FLASH, PWR, RCC}; | 7 | use crate::pac::{FLASH, PWR, RCC}; |
| 7 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| @@ -9,6 +10,7 @@ use crate::time::Hertz; | |||
| 9 | /// HSI speed | 10 | /// HSI speed |
| 10 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 11 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 11 | 12 | ||
| 13 | /// HSE Mode | ||
| 12 | #[derive(Clone, Copy, Eq, PartialEq)] | 14 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 13 | pub enum HseMode { | 15 | pub enum HseMode { |
| 14 | /// crystal/ceramic oscillator (HSEBYP=0) | 16 | /// crystal/ceramic oscillator (HSEBYP=0) |
| @@ -17,69 +19,71 @@ pub enum HseMode { | |||
| 17 | Bypass, | 19 | Bypass, |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 20 | /// System clock mux source | 22 | /// HSE Configuration |
| 21 | #[derive(Clone, Copy)] | 23 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 22 | pub enum Sysclk { | 24 | pub struct Hse { |
| 23 | HSE(Hertz, HseMode), | 25 | /// HSE frequency. |
| 24 | HSI(HSIPrescaler), | 26 | pub freq: Hertz, |
| 25 | PLL(PllConfig), | 27 | /// HSE mode. |
| 26 | LSI, | 28 | pub mode: HseMode, |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | /// The PLL configuration. | 31 | /// PLL Configuration |
| 30 | /// | 32 | /// |
| 31 | /// * `VCOCLK = source / m * n` | 33 | /// Use this struct to configure the PLL source, input frequency, multiplication factor, and output |
| 32 | /// * `PLLRCLK = VCOCLK / r` | 34 | /// dividers. Be sure to keep check the datasheet for your specific part for the appropriate |
| 33 | /// * `PLLQCLK = VCOCLK / q` | 35 | /// frequency ranges for each of these settings. |
| 34 | /// * `PLLPCLK = VCOCLK / p` | 36 | pub struct Pll { |
| 35 | #[derive(Clone, Copy)] | 37 | /// PLL Source clock selection. |
| 36 | pub struct PllConfig { | ||
| 37 | /// The source from which the PLL receives a clock signal | ||
| 38 | pub source: PllSource, | 38 | pub source: PllSource, |
| 39 | /// The initial divisor of that clock signal | ||
| 40 | pub m: Pllm, | ||
| 41 | /// The PLL VCO multiplier, which must be in the range `8..=86`. | ||
| 42 | pub n: Plln, | ||
| 43 | /// The final divisor for `PLLRCLK` output which drives the system clock | ||
| 44 | pub r: Pllr, | ||
| 45 | |||
| 46 | /// The divisor for the `PLLQCLK` output, if desired | ||
| 47 | pub q: Option<Pllq>, | ||
| 48 | |||
| 49 | /// The divisor for the `PLLPCLK` output, if desired | ||
| 50 | pub p: Option<Pllp>, | ||
| 51 | } | ||
| 52 | 39 | ||
| 53 | impl Default for PllConfig { | 40 | /// PLL pre-divider |
| 54 | #[inline] | 41 | pub prediv: PllPreDiv, |
| 55 | fn default() -> PllConfig { | ||
| 56 | // HSI / 1 * 8 / 2 = 64 MHz | ||
| 57 | PllConfig { | ||
| 58 | source: PllSource::HSI, | ||
| 59 | m: Pllm::DIV1, | ||
| 60 | n: Plln::MUL8, | ||
| 61 | r: Pllr::DIV2, | ||
| 62 | q: None, | ||
| 63 | p: None, | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | 42 | ||
| 68 | #[derive(Clone, Copy, Eq, PartialEq)] | 43 | /// PLL multiplication factor for VCO |
| 69 | pub enum PllSource { | 44 | pub mul: PllMul, |
| 70 | HSI, | 45 | |
| 71 | HSE(Hertz, HseMode), | 46 | /// PLL division factor for P clock (ADC Clock) |
| 47 | pub divp: Option<PllPDiv>, | ||
| 48 | |||
| 49 | /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI) | ||
| 50 | pub divq: Option<PllQDiv>, | ||
| 51 | |||
| 52 | /// PLL division factor for R clock (SYSCLK) | ||
| 53 | pub divr: Option<PllRDiv>, | ||
| 72 | } | 54 | } |
| 73 | 55 | ||
| 74 | /// Clocks configutation | 56 | /// Clocks configutation |
| 57 | #[non_exhaustive] | ||
| 75 | pub struct Config { | 58 | pub struct Config { |
| 59 | /// HSI Enable | ||
| 60 | pub hsi: bool, | ||
| 61 | |||
| 62 | /// HSE Configuration | ||
| 63 | pub hse: Option<Hse>, | ||
| 64 | |||
| 65 | /// System Clock Configuration | ||
| 76 | pub sys: Sysclk, | 66 | pub sys: Sysclk, |
| 77 | pub ahb_pre: AHBPrescaler, | 67 | |
| 78 | pub apb_pre: APBPrescaler, | 68 | /// HSI48 Configuration |
| 79 | pub low_power_run: bool, | ||
| 80 | pub ls: super::LsConfig, | ||
| 81 | #[cfg(crs)] | 69 | #[cfg(crs)] |
| 82 | pub hsi48: Option<super::Hsi48Config>, | 70 | pub hsi48: Option<super::Hsi48Config>, |
| 71 | |||
| 72 | /// PLL Configuration | ||
| 73 | pub pll: Option<Pll>, | ||
| 74 | |||
| 75 | /// If PLL is requested as the main clock source in the `sys` field then the PLL configuration | ||
| 76 | /// MUST turn on the PLLR output. | ||
| 77 | pub ahb_pre: AHBPrescaler, | ||
| 78 | pub apb1_pre: APBPrescaler, | ||
| 79 | |||
| 80 | /// Low-Speed Clock Configuration | ||
| 81 | pub ls: super::LsConfig, | ||
| 82 | |||
| 83 | pub low_power_run: bool, | ||
| 84 | |||
| 85 | pub voltage_range: VoltageRange, | ||
| 86 | |||
| 83 | /// Per-peripheral kernel clock selection muxes | 87 | /// Per-peripheral kernel clock selection muxes |
| 84 | pub mux: super::mux::ClockMux, | 88 | pub mux: super::mux::ClockMux, |
| 85 | } | 89 | } |
| @@ -88,248 +92,218 @@ impl Default for Config { | |||
| 88 | #[inline] | 92 | #[inline] |
| 89 | fn default() -> Config { | 93 | fn default() -> Config { |
| 90 | Config { | 94 | Config { |
| 91 | sys: Sysclk::HSI(HSIPrescaler::DIV1), | 95 | hsi: true, |
| 96 | hse: None, | ||
| 97 | sys: Sysclk::HSI, | ||
| 98 | #[cfg(crs)] | ||
| 99 | hsi48: Some(Default::default()), | ||
| 100 | pll: None, | ||
| 92 | ahb_pre: AHBPrescaler::DIV1, | 101 | ahb_pre: AHBPrescaler::DIV1, |
| 93 | apb_pre: APBPrescaler::DIV1, | 102 | apb1_pre: APBPrescaler::DIV1, |
| 94 | low_power_run: false, | 103 | low_power_run: false, |
| 95 | ls: Default::default(), | 104 | ls: Default::default(), |
| 96 | #[cfg(crs)] | 105 | voltage_range: VoltageRange::RANGE1, |
| 97 | hsi48: Some(Default::default()), | ||
| 98 | mux: Default::default(), | 106 | mux: Default::default(), |
| 99 | } | 107 | } |
| 100 | } | 108 | } |
| 101 | } | 109 | } |
| 102 | 110 | ||
| 103 | impl PllConfig { | 111 | #[derive(Default)] |
| 104 | pub(crate) fn init(self) -> (Hertz, Option<Hertz>, Option<Hertz>) { | 112 | pub struct PllFreq { |
| 105 | let (src, input_freq) = match self.source { | 113 | pub pll_p: Option<Hertz>, |
| 106 | PllSource::HSI => (vals::Pllsrc::HSI, HSI_FREQ), | 114 | pub pll_q: Option<Hertz>, |
| 107 | PllSource::HSE(freq, _) => (vals::Pllsrc::HSE, freq), | 115 | pub pll_r: Option<Hertz>, |
| 108 | }; | ||
| 109 | |||
| 110 | let m_freq = input_freq / self.m; | ||
| 111 | // RM0454 § 5.4.4: | ||
| 112 | // > Caution: The software must set these bits so that the PLL input frequency after the | ||
| 113 | // > /M divider is between 2.66 and 16 MHz. | ||
| 114 | debug_assert!(m_freq.0 >= 2_660_000 && m_freq.0 <= 16_000_000); | ||
| 115 | |||
| 116 | let n_freq = m_freq * self.n as u32; | ||
| 117 | // RM0454 § 5.4.4: | ||
| 118 | // > Caution: The software must set these bits so that the VCO output frequency is between | ||
| 119 | // > 64 and 344 MHz. | ||
| 120 | debug_assert!(n_freq.0 >= 64_000_000 && n_freq.0 <= 344_000_000); | ||
| 121 | |||
| 122 | let r_freq = n_freq / self.r; | ||
| 123 | // RM0454 § 5.4.4: | ||
| 124 | // > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock. | ||
| 125 | debug_assert!(r_freq.0 <= 64_000_000); | ||
| 126 | |||
| 127 | let q_freq = self.q.map(|q| n_freq / q); | ||
| 128 | let p_freq = self.p.map(|p| n_freq / p); | ||
| 129 | |||
| 130 | // RM0454 § 5.2.3: | ||
| 131 | // > To modify the PLL configuration, proceed as follows: | ||
| 132 | // > 1. Disable the PLL by setting PLLON to 0 in Clock control register (RCC_CR). | ||
| 133 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 134 | |||
| 135 | // > 2. Wait until PLLRDY is cleared. The PLL is now fully stopped. | ||
| 136 | while RCC.cr().read().pllrdy() {} | ||
| 137 | |||
| 138 | // > 3. Change the desired parameter. | ||
| 139 | // Enable whichever clock source we're using, and wait for it to become ready | ||
| 140 | match self.source { | ||
| 141 | PllSource::HSI => { | ||
| 142 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 143 | while !RCC.cr().read().hsirdy() {} | ||
| 144 | } | ||
| 145 | PllSource::HSE(_, mode) => { | ||
| 146 | RCC.cr().write(|w| { | ||
| 147 | w.set_hsebyp(mode != HseMode::Oscillator); | ||
| 148 | w.set_hseon(true); | ||
| 149 | }); | ||
| 150 | while !RCC.cr().read().hserdy() {} | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | // Configure PLLCFGR | ||
| 155 | RCC.pllcfgr().modify(|w| { | ||
| 156 | w.set_pllr(self.r); | ||
| 157 | w.set_pllren(false); | ||
| 158 | w.set_pllq(self.q.unwrap_or(Pllq::DIV2)); | ||
| 159 | w.set_pllqen(false); | ||
| 160 | w.set_pllp(self.p.unwrap_or(Pllp::DIV2)); | ||
| 161 | w.set_pllpen(false); | ||
| 162 | w.set_plln(self.n); | ||
| 163 | w.set_pllm(self.m); | ||
| 164 | w.set_pllsrc(src) | ||
| 165 | }); | ||
| 166 | |||
| 167 | // > 4. Enable the PLL again by setting PLLON to 1. | ||
| 168 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 169 | |||
| 170 | // Wait for the PLL to become ready | ||
| 171 | while !RCC.cr().read().pllrdy() {} | ||
| 172 | |||
| 173 | // > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL | ||
| 174 | // > configuration register (RCC_PLLCFGR). | ||
| 175 | RCC.pllcfgr().modify(|w| { | ||
| 176 | // We'll use R for system clock, so enable that unconditionally | ||
| 177 | w.set_pllren(true); | ||
| 178 | |||
| 179 | // We may also use Q or P | ||
| 180 | w.set_pllqen(self.q.is_some()); | ||
| 181 | w.set_pllpen(self.p.is_some()); | ||
| 182 | }); | ||
| 183 | |||
| 184 | (r_freq, q_freq, p_freq) | ||
| 185 | } | ||
| 186 | } | 116 | } |
| 187 | 117 | ||
| 188 | pub(crate) unsafe fn init(config: Config) { | 118 | pub(crate) unsafe fn init(config: Config) { |
| 189 | let mut pll1_q_freq = None; | 119 | // Configure HSI |
| 190 | let mut pll1_p_freq = None; | 120 | let hsi = match config.hsi { |
| 191 | 121 | false => { | |
| 192 | let (sys_clk, sw) = match config.sys { | 122 | RCC.cr().modify(|w| w.set_hsion(false)); |
| 193 | Sysclk::HSI(div) => { | 123 | None |
| 194 | // Enable HSI | 124 | } |
| 195 | RCC.cr().write(|w| { | 125 | true => { |
| 196 | w.set_hsidiv(div); | 126 | RCC.cr().modify(|w| w.set_hsion(true)); |
| 197 | w.set_hsion(true) | ||
| 198 | }); | ||
| 199 | while !RCC.cr().read().hsirdy() {} | 127 | while !RCC.cr().read().hsirdy() {} |
| 200 | 128 | Some(HSI_FREQ) | |
| 201 | (HSI_FREQ / div, Sw::HSI) | ||
| 202 | } | 129 | } |
| 203 | Sysclk::HSE(freq, mode) => { | 130 | }; |
| 204 | // Enable HSE | ||
| 205 | RCC.cr().write(|w| { | ||
| 206 | w.set_hseon(true); | ||
| 207 | w.set_hsebyp(mode != HseMode::Oscillator); | ||
| 208 | }); | ||
| 209 | while !RCC.cr().read().hserdy() {} | ||
| 210 | 131 | ||
| 211 | (freq, Sw::HSE) | 132 | // Configure HSE |
| 133 | let hse = match config.hse { | ||
| 134 | None => { | ||
| 135 | RCC.cr().modify(|w| w.set_hseon(false)); | ||
| 136 | None | ||
| 212 | } | 137 | } |
| 213 | Sysclk::PLL(pll) => { | 138 | Some(hse) => { |
| 214 | let (r_freq, q_freq, p_freq) = pll.init(); | 139 | match hse.mode { |
| 215 | 140 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | |
| 216 | pll1_q_freq = q_freq; | 141 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), |
| 217 | pll1_p_freq = p_freq; | 142 | } |
| 218 | 143 | ||
| 219 | (r_freq, Sw::PLL1_R) | 144 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); |
| 220 | } | 145 | RCC.cr().modify(|w| w.set_hseon(true)); |
| 221 | Sysclk::LSI => { | 146 | while !RCC.cr().read().hserdy() {} |
| 222 | // Enable LSI | 147 | Some(hse.freq) |
| 223 | RCC.csr().write(|w| w.set_lsion(true)); | ||
| 224 | while !RCC.csr().read().lsirdy() {} | ||
| 225 | (super::LSI_FREQ, Sw::LSI) | ||
| 226 | } | 148 | } |
| 227 | }; | 149 | }; |
| 228 | 150 | ||
| 229 | // Determine the flash latency implied by the target clock speed | 151 | // Configure HSI48 if required |
| 230 | // RM0454 § 3.3.4: | 152 | #[cfg(crs)] |
| 231 | let target_flash_latency = if sys_clk.0 <= 24_000_000 { | 153 | let hsi48 = config.hsi48.map(super::init_hsi48); |
| 232 | Latency::WS0 | ||
| 233 | } else if sys_clk.0 <= 48_000_000 { | ||
| 234 | Latency::WS1 | ||
| 235 | } else { | ||
| 236 | Latency::WS2 | ||
| 237 | }; | ||
| 238 | 154 | ||
| 239 | // Increase the number of cycles we wait for flash if the new value is higher | 155 | let pll = config |
| 240 | // There's no harm in waiting a little too much before the clock change, but we'll | 156 | .pll |
| 241 | // crash immediately if we don't wait enough after the clock change | 157 | .map(|pll_config| { |
| 242 | let mut set_flash_latency_after = false; | 158 | let src_freq = match pll_config.source { |
| 243 | FLASH.acr().modify(|w| { | 159 | PllSource::HSI => unwrap!(hsi), |
| 244 | // Is the current flash latency less than what we need at the new SYSCLK? | 160 | PllSource::HSE => unwrap!(hse), |
| 245 | if w.latency().to_bits() <= target_flash_latency.to_bits() { | 161 | _ => unreachable!(), |
| 246 | // We must increase the number of wait states now | 162 | }; |
| 247 | w.set_latency(target_flash_latency) | 163 | |
| 248 | } else { | 164 | // Disable PLL before configuration |
| 249 | // We may decrease the number of wait states later | 165 | RCC.cr().modify(|w| w.set_pllon(false)); |
| 250 | set_flash_latency_after = true; | 166 | while RCC.cr().read().pllrdy() {} |
| 251 | } | 167 | |
| 168 | let in_freq = src_freq / pll_config.prediv; | ||
| 169 | assert!(max::PLL_IN.contains(&in_freq)); | ||
| 170 | let internal_freq = in_freq * pll_config.mul; | ||
| 171 | |||
| 172 | assert!(max::PLL_VCO.contains(&internal_freq)); | ||
| 173 | |||
| 174 | RCC.pllcfgr().write(|w| { | ||
| 175 | w.set_plln(pll_config.mul); | ||
| 176 | w.set_pllm(pll_config.prediv); | ||
| 177 | w.set_pllsrc(pll_config.source.into()); | ||
| 178 | }); | ||
| 252 | 179 | ||
| 253 | // RM0454 § 3.3.5: | 180 | let pll_p_freq = pll_config.divp.map(|div_p| { |
| 254 | // > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register | 181 | RCC.pllcfgr().modify(|w| { |
| 255 | // > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the | 182 | w.set_pllp(div_p); |
| 256 | // > Flash memory. | 183 | w.set_pllpen(true); |
| 257 | // | 184 | }); |
| 258 | // Enable flash prefetching if we have at least one wait state, and disable it otherwise. | 185 | let freq = internal_freq / div_p; |
| 259 | w.set_prften(target_flash_latency.to_bits() > 0); | 186 | assert!(max::PLL_P.contains(&freq)); |
| 260 | }); | 187 | freq |
| 188 | }); | ||
| 261 | 189 | ||
| 262 | if !set_flash_latency_after { | 190 | let pll_q_freq = pll_config.divq.map(|div_q| { |
| 263 | // Spin until the effective flash latency is compatible with the clock change | 191 | RCC.pllcfgr().modify(|w| { |
| 264 | while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {} | 192 | w.set_pllq(div_q); |
| 265 | } | 193 | w.set_pllqen(true); |
| 194 | }); | ||
| 195 | let freq = internal_freq / div_q; | ||
| 196 | assert!(max::PLL_Q.contains(&freq)); | ||
| 197 | freq | ||
| 198 | }); | ||
| 266 | 199 | ||
| 267 | // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once | 200 | let pll_r_freq = pll_config.divr.map(|div_r| { |
| 268 | let (sw, hpre, ppre) = (sw.into(), config.ahb_pre, config.apb_pre); | 201 | RCC.pllcfgr().modify(|w| { |
| 269 | RCC.cfgr().modify(|w| { | 202 | w.set_pllr(div_r); |
| 270 | w.set_sw(sw); | 203 | w.set_pllren(true); |
| 271 | w.set_hpre(hpre); | 204 | }); |
| 272 | w.set_ppre(ppre); | 205 | let freq = internal_freq / div_r; |
| 273 | }); | 206 | assert!(max::PLL_R.contains(&freq)); |
| 207 | freq | ||
| 208 | }); | ||
| 274 | 209 | ||
| 275 | if set_flash_latency_after { | 210 | // Enable the PLL |
| 276 | // We can make the flash require fewer wait states | 211 | RCC.cr().modify(|w| w.set_pllon(true)); |
| 277 | // Spin until the SYSCLK changes have taken effect | 212 | while !RCC.cr().read().pllrdy() {} |
| 278 | loop { | 213 | |
| 279 | let cfgr = RCC.cfgr().read(); | 214 | PllFreq { |
| 280 | if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre { | 215 | pll_p: pll_p_freq, |
| 281 | break; | 216 | pll_q: pll_q_freq, |
| 217 | pll_r: pll_r_freq, | ||
| 282 | } | 218 | } |
| 283 | } | 219 | }) |
| 220 | .unwrap_or_default(); | ||
| 221 | |||
| 222 | let sys = match config.sys { | ||
| 223 | Sysclk::HSI => unwrap!(hsi), | ||
| 224 | Sysclk::HSE => unwrap!(hse), | ||
| 225 | Sysclk::PLL1_R => unwrap!(pll.pll_r), | ||
| 226 | _ => unreachable!(), | ||
| 227 | }; | ||
| 284 | 228 | ||
| 285 | // Set the flash latency to require fewer wait states | 229 | assert!(max::SYSCLK.contains(&sys)); |
| 286 | FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); | ||
| 287 | } | ||
| 288 | 230 | ||
| 289 | let ahb_freq = sys_clk / config.ahb_pre; | 231 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. |
| 232 | let hclk = sys / config.ahb_pre; | ||
| 233 | assert!(max::HCLK.contains(&hclk)); | ||
| 290 | 234 | ||
| 291 | let (apb_freq, apb_tim_freq) = match config.apb_pre { | 235 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); |
| 292 | APBPrescaler::DIV1 => (ahb_freq, ahb_freq), | 236 | assert!(max::PCLK.contains(&pclk1)); |
| 293 | pre => { | 237 | |
| 294 | let freq = ahb_freq / pre; | 238 | let latency = match (config.voltage_range, hclk.0) { |
| 295 | (freq, freq * 2u32) | 239 | (VoltageRange::RANGE1, ..=24_000_000) => Latency::WS0, |
| 296 | } | 240 | (VoltageRange::RANGE1, ..=48_000_000) => Latency::WS1, |
| 241 | (VoltageRange::RANGE1, _) => Latency::WS2, | ||
| 242 | (VoltageRange::RANGE2, ..=8_000_000) => Latency::WS0, | ||
| 243 | (VoltageRange::RANGE2, ..=16_000_000) => Latency::WS1, | ||
| 244 | (VoltageRange::RANGE2, _) => Latency::WS2, | ||
| 245 | _ => unreachable!(), | ||
| 297 | }; | 246 | }; |
| 298 | 247 | ||
| 248 | // Configure flash read access latency based on voltage scale and frequency (RM0444 3.3.4) | ||
| 249 | FLASH.acr().modify(|w| { | ||
| 250 | w.set_latency(latency); | ||
| 251 | }); | ||
| 252 | |||
| 253 | // Spin until the effective flash latency is set. | ||
| 254 | while FLASH.acr().read().latency() != latency {} | ||
| 255 | |||
| 256 | // Now that boost mode and flash read access latency are configured, set up SYSCLK | ||
| 257 | RCC.cfgr().modify(|w| { | ||
| 258 | w.set_sw(config.sys); | ||
| 259 | w.set_hpre(config.ahb_pre); | ||
| 260 | w.set_ppre(config.apb1_pre); | ||
| 261 | }); | ||
| 262 | |||
| 299 | if config.low_power_run { | 263 | if config.low_power_run { |
| 300 | assert!(sys_clk.0 <= 2_000_000); | 264 | assert!(sys <= Hertz(2_000_000)); |
| 301 | PWR.cr1().modify(|w| w.set_lpr(true)); | 265 | PWR.cr1().modify(|w| w.set_lpr(true)); |
| 302 | } | 266 | } |
| 303 | 267 | ||
| 304 | let rtc = config.ls.init(); | 268 | let rtc = config.ls.init(); |
| 305 | let lse_freq = config.ls.lse.map(|lse| lse.frequency); | ||
| 306 | |||
| 307 | let hsi_freq = (sw == Sw::HSI).then_some(HSI_FREQ); | ||
| 308 | let hsi_div_8_freq = hsi_freq.map(|f| f / 8u32); | ||
| 309 | let lsi_freq = (sw == Sw::LSI).then_some(super::LSI_FREQ); | ||
| 310 | let hse_freq = (sw == Sw::HSE).then_some(sys_clk); | ||
| 311 | |||
| 312 | #[cfg(crs)] | ||
| 313 | let hsi48 = config.hsi48.map(super::init_hsi48); | ||
| 314 | #[cfg(not(crs))] | ||
| 315 | let hsi48: Option<Hertz> = None; | ||
| 316 | 269 | ||
| 317 | config.mux.init(); | 270 | config.mux.init(); |
| 318 | 271 | ||
| 319 | set_clocks!( | 272 | set_clocks!( |
| 320 | sys: Some(sys_clk), | 273 | sys: Some(sys), |
| 321 | hclk1: Some(ahb_freq), | 274 | hclk1: Some(hclk), |
| 322 | pclk1: Some(apb_freq), | 275 | pclk1: Some(pclk1), |
| 323 | pclk1_tim: Some(apb_tim_freq), | 276 | pclk1_tim: Some(pclk1_tim), |
| 324 | hsi: hsi_freq, | 277 | pll1_p: pll.pll_p, |
| 278 | pll1_q: pll.pll_q, | ||
| 279 | pll1_r: pll.pll_r, | ||
| 280 | hsi: hsi, | ||
| 281 | hse: hse, | ||
| 282 | #[cfg(crs)] | ||
| 325 | hsi48: hsi48, | 283 | hsi48: hsi48, |
| 326 | hsi_div_8: hsi_div_8_freq, | ||
| 327 | hse: hse_freq, | ||
| 328 | lse: lse_freq, | ||
| 329 | lsi: lsi_freq, | ||
| 330 | pll1_q: pll1_q_freq, | ||
| 331 | pll1_p: pll1_p_freq, | ||
| 332 | rtc: rtc, | 284 | rtc: rtc, |
| 333 | hsi_div_488: None, | 285 | hsi_div_8: hsi.map(|h| h / 8u32), |
| 286 | hsi_div_488: hsi.map(|h| h / 488u32), | ||
| 287 | |||
| 288 | // TODO | ||
| 289 | lsi: None, | ||
| 290 | lse: None, | ||
| 334 | ); | 291 | ); |
| 335 | } | 292 | } |
| 293 | |||
| 294 | mod max { | ||
| 295 | use core::ops::RangeInclusive; | ||
| 296 | |||
| 297 | use crate::time::Hertz; | ||
| 298 | |||
| 299 | pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000); | ||
| 300 | pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000); | ||
| 301 | pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(64_000_000); | ||
| 302 | pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(8)..=Hertz(64_000_000); | ||
| 303 | pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(64_000_000); | ||
| 304 | pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(2_660_000)..=Hertz(16_000_000); | ||
| 305 | pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000); | ||
| 306 | pub(crate) const PLL_P: RangeInclusive<Hertz> = Hertz(3_090_000)..=Hertz(122_000_000); | ||
| 307 | pub(crate) const PLL_Q: RangeInclusive<Hertz> = Hertz(12_000_000)..=Hertz(128_000_000); | ||
| 308 | pub(crate) const PLL_R: RangeInclusive<Hertz> = Hertz(12_000_000)..=Hertz(64_000_000); | ||
| 309 | } | ||
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 79bdbeb77..cd2d2a8a2 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs | |||
| @@ -1,12 +1,9 @@ | |||
| 1 | use stm32_metapac::flash::vals::Latency; | 1 | use crate::pac::flash::vals::Latency; |
| 2 | use stm32_metapac::rcc::vals::Sw; | ||
| 3 | use stm32_metapac::FLASH; | ||
| 4 | |||
| 5 | pub use crate::pac::rcc::vals::{ | 2 | pub use crate::pac::rcc::vals::{ |
| 6 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, | 3 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, |
| 7 | Ppre as APBPrescaler, Sw as Sysclk, | 4 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 8 | }; | 5 | }; |
| 9 | use crate::pac::{PWR, RCC}; | 6 | use crate::pac::{FLASH, PWR, RCC}; |
| 10 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 11 | 8 | ||
| 12 | /// HSI speed | 9 | /// HSI speed |
| @@ -37,7 +34,7 @@ pub struct Hse { | |||
| 37 | /// frequency ranges for each of these settings. | 34 | /// frequency ranges for each of these settings. |
| 38 | pub struct Pll { | 35 | pub struct Pll { |
| 39 | /// PLL Source clock selection. | 36 | /// PLL Source clock selection. |
| 40 | pub source: Pllsrc, | 37 | pub source: PllSource, |
| 41 | 38 | ||
| 42 | /// PLL pre-divider | 39 | /// PLL pre-divider |
| 43 | pub prediv: PllPreDiv, | 40 | pub prediv: PllPreDiv, |
| @@ -73,7 +70,7 @@ pub struct Config { | |||
| 73 | /// PLL Configuration | 70 | /// PLL Configuration |
| 74 | pub pll: Option<Pll>, | 71 | pub pll: Option<Pll>, |
| 75 | 72 | ||
| 76 | /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration | 73 | /// If PLL is requested as the main clock source in the `sys` field then the PLL configuration |
| 77 | /// MUST turn on the PLLR output. | 74 | /// MUST turn on the PLLR output. |
| 78 | pub ahb_pre: AHBPrescaler, | 75 | pub ahb_pre: AHBPrescaler, |
| 79 | pub apb1_pre: APBPrescaler, | 76 | pub apb1_pre: APBPrescaler, |
| @@ -112,6 +109,7 @@ impl Default for Config { | |||
| 112 | } | 109 | } |
| 113 | } | 110 | } |
| 114 | 111 | ||
| 112 | #[derive(Default)] | ||
| 115 | pub struct PllFreq { | 113 | pub struct PllFreq { |
| 116 | pub pll_p: Option<Hertz>, | 114 | pub pll_p: Option<Hertz>, |
| 117 | pub pll_q: Option<Hertz>, | 115 | pub pll_q: Option<Hertz>, |
| @@ -154,91 +152,91 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 154 | // Configure HSI48 if required | 152 | // Configure HSI48 if required |
| 155 | let hsi48 = config.hsi48.map(super::init_hsi48); | 153 | let hsi48 = config.hsi48.map(super::init_hsi48); |
| 156 | 154 | ||
| 157 | let pll_freq = config.pll.map(|pll_config| { | 155 | let pll = config |
| 158 | let src_freq = match pll_config.source { | 156 | .pll |
| 159 | Pllsrc::HSI => unwrap!(hsi), | 157 | .map(|pll_config| { |
| 160 | Pllsrc::HSE => unwrap!(hse), | 158 | let src_freq = match pll_config.source { |
| 161 | _ => unreachable!(), | 159 | PllSource::HSI => unwrap!(hsi), |
| 162 | }; | 160 | PllSource::HSE => unwrap!(hse), |
| 163 | 161 | _ => unreachable!(), | |
| 164 | // Disable PLL before configuration | 162 | }; |
| 165 | RCC.cr().modify(|w| w.set_pllon(false)); | 163 | |
| 166 | while RCC.cr().read().pllrdy() {} | 164 | // Disable PLL before configuration |
| 167 | 165 | RCC.cr().modify(|w| w.set_pllon(false)); | |
| 168 | let in_freq = src_freq / pll_config.prediv; | 166 | while RCC.cr().read().pllrdy() {} |
| 169 | assert!(max::PLL_IN.contains(&in_freq)); | 167 | |
| 170 | let internal_freq = in_freq * pll_config.mul; | 168 | let in_freq = src_freq / pll_config.prediv; |
| 171 | 169 | assert!(max::PLL_IN.contains(&in_freq)); | |
| 172 | assert!(max::PLL_VCO.contains(&internal_freq)); | 170 | let internal_freq = in_freq * pll_config.mul; |
| 173 | 171 | ||
| 174 | RCC.pllcfgr().write(|w| { | 172 | assert!(max::PLL_VCO.contains(&internal_freq)); |
| 175 | w.set_plln(pll_config.mul); | 173 | |
| 176 | w.set_pllm(pll_config.prediv); | 174 | RCC.pllcfgr().write(|w| { |
| 177 | w.set_pllsrc(pll_config.source.into()); | 175 | w.set_plln(pll_config.mul); |
| 178 | }); | 176 | w.set_pllm(pll_config.prediv); |
| 179 | 177 | w.set_pllsrc(pll_config.source.into()); | |
| 180 | let pll_p_freq = pll_config.divp.map(|div_p| { | ||
| 181 | RCC.pllcfgr().modify(|w| { | ||
| 182 | w.set_pllp(div_p); | ||
| 183 | w.set_pllpen(true); | ||
| 184 | }); | 178 | }); |
| 185 | let freq = internal_freq / div_p; | 179 | |
| 186 | assert!(max::PCLK.contains(&freq)); | 180 | let pll_p_freq = pll_config.divp.map(|div_p| { |
| 187 | freq | 181 | RCC.pllcfgr().modify(|w| { |
| 188 | }); | 182 | w.set_pllp(div_p); |
| 189 | 183 | w.set_pllpen(true); | |
| 190 | let pll_q_freq = pll_config.divq.map(|div_q| { | 184 | }); |
| 191 | RCC.pllcfgr().modify(|w| { | 185 | let freq = internal_freq / div_p; |
| 192 | w.set_pllq(div_q); | 186 | assert!(max::PLL_P.contains(&freq)); |
| 193 | w.set_pllqen(true); | 187 | freq |
| 194 | }); | 188 | }); |
| 195 | let freq = internal_freq / div_q; | 189 | |
| 196 | assert!(max::PCLK.contains(&freq)); | 190 | let pll_q_freq = pll_config.divq.map(|div_q| { |
| 197 | freq | 191 | RCC.pllcfgr().modify(|w| { |
| 198 | }); | 192 | w.set_pllq(div_q); |
| 199 | 193 | w.set_pllqen(true); | |
| 200 | let pll_r_freq = pll_config.divr.map(|div_r| { | 194 | }); |
| 201 | RCC.pllcfgr().modify(|w| { | 195 | let freq = internal_freq / div_q; |
| 202 | w.set_pllr(div_r); | 196 | assert!(max::PLL_Q.contains(&freq)); |
| 203 | w.set_pllren(true); | 197 | freq |
| 204 | }); | 198 | }); |
| 205 | let freq = internal_freq / div_r; | ||
| 206 | assert!(max::PCLK.contains(&freq)); | ||
| 207 | freq | ||
| 208 | }); | ||
| 209 | |||
| 210 | // Enable the PLL | ||
| 211 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 212 | while !RCC.cr().read().pllrdy() {} | ||
| 213 | |||
| 214 | PllFreq { | ||
| 215 | pll_p: pll_p_freq, | ||
| 216 | pll_q: pll_q_freq, | ||
| 217 | pll_r: pll_r_freq, | ||
| 218 | } | ||
| 219 | }); | ||
| 220 | 199 | ||
| 221 | let (sys_clk, sw) = match config.sys { | 200 | let pll_r_freq = pll_config.divr.map(|div_r| { |
| 222 | Sysclk::HSI => (HSI_FREQ, Sw::HSI), | 201 | RCC.pllcfgr().modify(|w| { |
| 223 | Sysclk::HSE => (unwrap!(hse), Sw::HSE), | 202 | w.set_pllr(div_r); |
| 224 | Sysclk::PLL1_R => { | 203 | w.set_pllren(true); |
| 225 | assert!(pll_freq.is_some()); | 204 | }); |
| 226 | assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); | 205 | let freq = internal_freq / div_r; |
| 206 | assert!(max::PLL_R.contains(&freq)); | ||
| 207 | freq | ||
| 208 | }); | ||
| 227 | 209 | ||
| 228 | let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0; | 210 | // Enable the PLL |
| 211 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 212 | while !RCC.cr().read().pllrdy() {} | ||
| 229 | 213 | ||
| 230 | assert!(max::SYSCLK.contains(&Hertz(freq))); | 214 | PllFreq { |
| 215 | pll_p: pll_p_freq, | ||
| 216 | pll_q: pll_q_freq, | ||
| 217 | pll_r: pll_r_freq, | ||
| 218 | } | ||
| 219 | }) | ||
| 220 | .unwrap_or_default(); | ||
| 231 | 221 | ||
| 232 | (Hertz(freq), Sw::PLL1_R) | 222 | let sys = match config.sys { |
| 233 | } | 223 | Sysclk::HSI => unwrap!(hsi), |
| 224 | Sysclk::HSE => unwrap!(hse), | ||
| 225 | Sysclk::PLL1_R => unwrap!(pll.pll_r), | ||
| 234 | _ => unreachable!(), | 226 | _ => unreachable!(), |
| 235 | }; | 227 | }; |
| 236 | 228 | ||
| 237 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. | 229 | assert!(max::SYSCLK.contains(&sys)); |
| 238 | let hclk = sys_clk / config.ahb_pre; | ||
| 239 | 230 | ||
| 231 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. | ||
| 232 | let hclk = sys / config.ahb_pre; | ||
| 240 | assert!(max::HCLK.contains(&hclk)); | 233 | assert!(max::HCLK.contains(&hclk)); |
| 241 | 234 | ||
| 235 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); | ||
| 236 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); | ||
| 237 | assert!(max::PCLK.contains(&pclk2)); | ||
| 238 | assert!(max::PCLK.contains(&pclk2)); | ||
| 239 | |||
| 242 | // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) | 240 | // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) |
| 243 | if config.boost { | 241 | if config.boost { |
| 244 | // RM0440 p235 | 242 | // RM0440 p235 |
| @@ -253,23 +251,28 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 253 | // 4. Configure and switch to new frequency | 251 | // 4. Configure and switch to new frequency |
| 254 | } | 252 | } |
| 255 | 253 | ||
| 254 | let latency = match (config.boost, hclk.0) { | ||
| 255 | (true, ..=34_000_000) => Latency::WS0, | ||
| 256 | (true, ..=68_000_000) => Latency::WS1, | ||
| 257 | (true, ..=102_000_000) => Latency::WS2, | ||
| 258 | (true, ..=136_000_000) => Latency::WS3, | ||
| 259 | (true, _) => Latency::WS4, | ||
| 260 | |||
| 261 | (false, ..=36_000_000) => Latency::WS0, | ||
| 262 | (false, ..=60_000_000) => Latency::WS1, | ||
| 263 | (false, ..=90_000_000) => Latency::WS2, | ||
| 264 | (false, ..=120_000_000) => Latency::WS3, | ||
| 265 | (false, _) => Latency::WS4, | ||
| 266 | }; | ||
| 267 | |||
| 256 | // Configure flash read access latency based on boost mode and frequency (RM0440 p98) | 268 | // Configure flash read access latency based on boost mode and frequency (RM0440 p98) |
| 257 | FLASH.acr().modify(|w| { | 269 | FLASH.acr().modify(|w| { |
| 258 | w.set_latency(match (config.boost, hclk.0) { | 270 | w.set_latency(latency); |
| 259 | (true, ..=34_000_000) => Latency::WS0, | ||
| 260 | (true, ..=68_000_000) => Latency::WS1, | ||
| 261 | (true, ..=102_000_000) => Latency::WS2, | ||
| 262 | (true, ..=136_000_000) => Latency::WS3, | ||
| 263 | (true, _) => Latency::WS4, | ||
| 264 | |||
| 265 | (false, ..=36_000_000) => Latency::WS0, | ||
| 266 | (false, ..=60_000_000) => Latency::WS1, | ||
| 267 | (false, ..=90_000_000) => Latency::WS2, | ||
| 268 | (false, ..=120_000_000) => Latency::WS3, | ||
| 269 | (false, _) => Latency::WS4, | ||
| 270 | }) | ||
| 271 | }); | 271 | }); |
| 272 | 272 | ||
| 273 | // Spin until the effective flash latency is set. | ||
| 274 | while FLASH.acr().read().latency() != latency {} | ||
| 275 | |||
| 273 | if config.boost { | 276 | if config.boost { |
| 274 | // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency. | 277 | // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency. |
| 275 | cortex_m::asm::delay(16); | 278 | cortex_m::asm::delay(16); |
| @@ -277,17 +280,14 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 277 | 280 | ||
| 278 | // Now that boost mode and flash read access latency are configured, set up SYSCLK | 281 | // Now that boost mode and flash read access latency are configured, set up SYSCLK |
| 279 | RCC.cfgr().modify(|w| { | 282 | RCC.cfgr().modify(|w| { |
| 280 | w.set_sw(sw); | 283 | w.set_sw(config.sys); |
| 281 | w.set_hpre(config.ahb_pre); | 284 | w.set_hpre(config.ahb_pre); |
| 282 | w.set_ppre1(config.apb1_pre); | 285 | w.set_ppre1(config.apb1_pre); |
| 283 | w.set_ppre2(config.apb2_pre); | 286 | w.set_ppre2(config.apb2_pre); |
| 284 | }); | 287 | }); |
| 285 | 288 | ||
| 286 | let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre); | ||
| 287 | let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre); | ||
| 288 | |||
| 289 | if config.low_power_run { | 289 | if config.low_power_run { |
| 290 | assert!(sys_clk <= Hertz(2_000_000)); | 290 | assert!(sys <= Hertz(2_000_000)); |
| 291 | PWR.cr1().modify(|w| w.set_lpr(true)); | 291 | PWR.cr1().modify(|w| w.set_lpr(true)); |
| 292 | } | 292 | } |
| 293 | 293 | ||
| @@ -296,17 +296,18 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 296 | config.mux.init(); | 296 | config.mux.init(); |
| 297 | 297 | ||
| 298 | set_clocks!( | 298 | set_clocks!( |
| 299 | sys: Some(sys_clk), | 299 | sys: Some(sys), |
| 300 | hclk1: Some(hclk), | 300 | hclk1: Some(hclk), |
| 301 | hclk2: Some(hclk), | 301 | hclk2: Some(hclk), |
| 302 | hclk3: Some(hclk), | 302 | hclk3: Some(hclk), |
| 303 | pclk1: Some(apb1_freq), | 303 | pclk1: Some(pclk1), |
| 304 | pclk1_tim: Some(apb1_tim_freq), | 304 | pclk1_tim: Some(pclk1_tim), |
| 305 | pclk2: Some(apb2_freq), | 305 | pclk2: Some(pclk2), |
| 306 | pclk2_tim: Some(apb2_tim_freq), | 306 | pclk2_tim: Some(pclk2_tim), |
| 307 | pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p), | 307 | pll1_p: pll.pll_p, |
| 308 | pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_q), | 308 | pll1_q: pll.pll_q, |
| 309 | pll1_r: pll_freq.as_ref().and_then(|pll| pll.pll_r), | 309 | pll1_r: pll.pll_r, |
| 310 | hsi: hsi, | ||
| 310 | hse: hse, | 311 | hse: hse, |
| 311 | hsi48: hsi48, | 312 | hsi48: hsi48, |
| 312 | rtc: rtc, | 313 | rtc: rtc, |
| @@ -342,4 +343,7 @@ mod max { | |||
| 342 | 343 | ||
| 343 | /// PLL VCO (internal) Frequency Range (STM32G474 Datasheet p123, Table 46) | 344 | /// PLL VCO (internal) Frequency Range (STM32G474 Datasheet p123, Table 46) |
| 344 | pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000); | 345 | pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000); |
| 346 | pub(crate) const PLL_P: RangeInclusive<Hertz> = Hertz(2_064_500)..=Hertz(170_000_000); | ||
| 347 | pub(crate) const PLL_Q: RangeInclusive<Hertz> = Hertz(8_000_000)..=Hertz(170_000_000); | ||
| 348 | pub(crate) const PLL_R: RangeInclusive<Hertz> = Hertz(8_000_000)..=Hertz(170_000_000); | ||
| 345 | } | 349 | } |
diff --git a/examples/stm32g0/src/bin/hf_timer.rs b/examples/stm32g0/src/bin/hf_timer.rs index 647ff0419..3ea06cdee 100644 --- a/examples/stm32g0/src/bin/hf_timer.rs +++ b/examples/stm32g0/src/bin/hf_timer.rs | |||
| @@ -16,15 +16,16 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let mut config = PeripheralConfig::default(); | 16 | let mut config = PeripheralConfig::default(); |
| 17 | { | 17 | { |
| 18 | use embassy_stm32::rcc::*; | 18 | use embassy_stm32::rcc::*; |
| 19 | 19 | config.rcc.hsi = true; | |
| 20 | config.rcc.sys = Sysclk::PLL(PllConfig { | 20 | config.rcc.pll = Some(Pll { |
| 21 | source: PllSource::HSI, | 21 | source: PllSource::HSI, |
| 22 | m: Pllm::DIV1, | 22 | prediv: PllPreDiv::DIV1, |
| 23 | n: Plln::MUL16, | 23 | mul: PllMul::MUL16, |
| 24 | r: Pllr::DIV4, // CPU clock comes from PLLR (HSI (16MHz) / 1 * 16 / 4 = 64MHz) | 24 | divp: None, |
| 25 | q: Some(Pllq::DIV2), // TIM1 or TIM15 can be sourced from PLLQ (HSI (16MHz) / 1 * 16 / 2 = 128MHz) | 25 | divq: Some(PllQDiv::DIV2), // 16 / 1 * 16 / 2 = 128 Mhz |
| 26 | p: None, | 26 | divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz |
| 27 | }); | 27 | }); |
| 28 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 28 | 29 | ||
| 29 | // configure TIM1 mux to select PLLQ as clock source | 30 | // configure TIM1 mux to select PLLQ as clock source |
| 30 | // https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf | 31 | // https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf |
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index f81335f93..ae64bc8e4 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs | |||
| @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | { | 14 | { |
| 15 | use embassy_stm32::rcc::*; | 15 | use embassy_stm32::rcc::*; |
| 16 | config.rcc.pll = Some(Pll { | 16 | config.rcc.pll = Some(Pll { |
| 17 | source: Pllsrc::HSI, | 17 | source: PllSource::HSI, |
| 18 | prediv: PllPreDiv::DIV4, | 18 | prediv: PllPreDiv::DIV4, |
| 19 | mul: PllMul::MUL85, | 19 | mul: PllMul::MUL85, |
| 20 | divp: None, | 20 | divp: None, |
diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs index 93b206de8..4373a89a8 100644 --- a/examples/stm32g4/src/bin/can.rs +++ b/examples/stm32g4/src/bin/can.rs | |||
| @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { | |||
| 24 | mode: HseMode::Oscillator, | 24 | mode: HseMode::Oscillator, |
| 25 | }); | 25 | }); |
| 26 | config.rcc.pll = Some(Pll { | 26 | config.rcc.pll = Some(Pll { |
| 27 | source: Pllsrc::HSE, | 27 | source: PllSource::HSE, |
| 28 | prediv: PllPreDiv::DIV6, | 28 | prediv: PllPreDiv::DIV6, |
| 29 | mul: PllMul::MUL85, | 29 | mul: PllMul::MUL85, |
| 30 | divp: None, | 30 | divp: None, |
diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 2609abfa2..08ed95b34 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk}; | ||
| 7 | use embassy_stm32::Config; | 6 | use embassy_stm32::Config; |
| 8 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -11,20 +10,20 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 11 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| 12 | async fn main(_spawner: Spawner) { | 11 | async fn main(_spawner: Spawner) { |
| 13 | let mut config = Config::default(); | 12 | let mut config = Config::default(); |
| 14 | 13 | { | |
| 15 | config.rcc.hsi = true; | 14 | use embassy_stm32::rcc::*; |
| 16 | config.rcc.pll = Some(Pll { | 15 | config.rcc.hsi = true; |
| 17 | source: Pllsrc::HSI, | 16 | config.rcc.pll = Some(Pll { |
| 18 | prediv: PllPreDiv::DIV4, | 17 | source: PllSource::HSI, |
| 19 | mul: PllMul::MUL85, | 18 | prediv: PllPreDiv::DIV4, |
| 20 | divp: None, | 19 | mul: PllMul::MUL85, |
| 21 | divq: None, | 20 | divp: None, |
| 22 | // Main system clock at 170 MHz | 21 | divq: None, |
| 23 | divr: Some(PllRDiv::DIV2), | 22 | // Main system clock at 170 MHz |
| 24 | }); | 23 | divr: Some(PllRDiv::DIV2), |
| 25 | 24 | }); | |
| 26 | config.rcc.sys = Sysclk::PLL1_R; | 25 | config.rcc.sys = Sysclk::PLL1_R; |
| 27 | 26 | } | |
| 28 | let _p = embassy_stm32::init(config); | 27 | let _p = embassy_stm32::init(config); |
| 29 | info!("Hello World!"); | 28 | info!("Hello World!"); |
| 30 | 29 | ||
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 90caaae14..dc95aa6e5 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs | |||
| @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { | |||
| 28 | mode: HseMode::Oscillator, | 28 | mode: HseMode::Oscillator, |
| 29 | }); | 29 | }); |
| 30 | config.rcc.pll = Some(Pll { | 30 | config.rcc.pll = Some(Pll { |
| 31 | source: Pllsrc::HSE, | 31 | source: PllSource::HSE, |
| 32 | prediv: PllPreDiv::DIV2, | 32 | prediv: PllPreDiv::DIV2, |
| 33 | mul: PllMul::MUL72, | 33 | mul: PllMul::MUL72, |
| 34 | divp: None, | 34 | divp: None, |
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index cf3e04a4b..3297ea7e2 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -260,6 +260,30 @@ pub fn config() -> Config { | |||
| 260 | #[allow(unused_mut)] | 260 | #[allow(unused_mut)] |
| 261 | let mut config = Config::default(); | 261 | let mut config = Config::default(); |
| 262 | 262 | ||
| 263 | #[cfg(feature = "stm32c031c6")] | ||
| 264 | { | ||
| 265 | config.rcc.hsi = Some(Hsi { | ||
| 266 | sys_div: HsiSysDiv::DIV1, // 48Mhz | ||
| 267 | ker_div: HsiKerDiv::DIV3, // 16Mhz | ||
| 268 | }); | ||
| 269 | config.rcc.sys = Sysclk::HSISYS; | ||
| 270 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 271 | config.rcc.apb1_pre = APBPrescaler::DIV1; | ||
| 272 | } | ||
| 273 | |||
| 274 | #[cfg(feature = "stm32g071rb")] | ||
| 275 | { | ||
| 276 | config.rcc.hsi = true; | ||
| 277 | config.rcc.pll = Some(Pll { | ||
| 278 | source: PllSource::HSI, | ||
| 279 | prediv: PllPreDiv::DIV1, | ||
| 280 | mul: PllMul::MUL16, | ||
| 281 | divp: None, | ||
| 282 | divq: None, | ||
| 283 | divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz | ||
| 284 | }); | ||
| 285 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 286 | } | ||
| 263 | #[cfg(feature = "stm32wb55rg")] | 287 | #[cfg(feature = "stm32wb55rg")] |
| 264 | { | 288 | { |
| 265 | config.rcc = embassy_stm32::rcc::WPAN_DEFAULT; | 289 | config.rcc = embassy_stm32::rcc::WPAN_DEFAULT; |
| @@ -456,7 +480,7 @@ pub fn config() -> Config { | |||
| 456 | mode: HseMode::Oscillator, | 480 | mode: HseMode::Oscillator, |
| 457 | }); | 481 | }); |
| 458 | config.rcc.pll = Some(Pll { | 482 | config.rcc.pll = Some(Pll { |
| 459 | source: Pllsrc::HSE, | 483 | source: PllSource::HSE, |
| 460 | prediv: PllPreDiv::DIV6, | 484 | prediv: PllPreDiv::DIV6, |
| 461 | mul: PllMul::MUL85, | 485 | mul: PllMul::MUL85, |
| 462 | divp: None, | 486 | divp: None, |
