diff options
| -rw-r--r-- | embassy-stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/c0.rs | 224 | ||||
| -rw-r--r-- | tests/stm32/src/common.rs | 11 |
3 files changed, 142 insertions, 97 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/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 1587a6fb4..3297ea7e2 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -260,6 +260,17 @@ 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 | |||
| 263 | #[cfg(feature = "stm32g071rb")] | 274 | #[cfg(feature = "stm32g071rb")] |
| 264 | { | 275 | { |
| 265 | config.rcc.hsi = true; | 276 | config.rcc.hsi = true; |
