diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-01-05 12:34:10 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-01-05 12:34:10 +0000 |
| commit | da0c25227f188ebde4ae275f572722d27e578a67 (patch) | |
| tree | 3ea96ba6790b7793e96ca1e7c285de917fe30f64 | |
| parent | b2a85ee519067d3290e328f9ad047184ff033cd7 (diff) | |
| parent | caf48d8a9593cfd408a230600173e510e397c7dd (diff) | |
Merge #566
566: stm32: more RCC cleanups. r=Dirbaio a=Dirbaio
Co-authored-by: Dario Nieuwenhuis <[email protected]>
50 files changed, 4208 insertions, 4856 deletions
diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index cdbc25179..44a8d3b93 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs | |||
| @@ -370,16 +370,14 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 370 | ::core::mem::transmute(t) | 370 | ::core::mem::transmute(t) |
| 371 | } | 371 | } |
| 372 | 372 | ||
| 373 | let mut executor = #embassy_path::executor::Executor::new(); | 373 | #chip_setup |
| 374 | 374 | ||
| 375 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 375 | let executor = unsafe { make_static(&mut executor) }; | 376 | let executor = unsafe { make_static(&mut executor) }; |
| 376 | 377 | ||
| 377 | #chip_setup | ||
| 378 | |||
| 379 | executor.run(|spawner| { | 378 | executor.run(|spawner| { |
| 380 | spawner.must_spawn(__embassy_main(spawner, p)); | 379 | spawner.must_spawn(__embassy_main(spawner, p)); |
| 381 | }) | 380 | }) |
| 382 | |||
| 383 | } | 381 | } |
| 384 | }; | 382 | }; |
| 385 | result.into() | 383 | result.into() |
diff --git a/embassy-stm32/src/dbgmcu/mod.rs b/embassy-stm32/src/dbgmcu/mod.rs deleted file mode 100644 index 8dc4cc53f..000000000 --- a/embassy-stm32/src/dbgmcu/mod.rs +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | pub struct Dbgmcu {} | ||
| 2 | |||
| 3 | impl Dbgmcu { | ||
| 4 | pub unsafe fn enable_all() { | ||
| 5 | crate::pac::DBGMCU.cr().modify(|cr| { | ||
| 6 | crate::pac::dbgmcu! { | ||
| 7 | (cr, $fn_name:ident) => { | ||
| 8 | cr.$fn_name(true); | ||
| 9 | }; | ||
| 10 | } | ||
| 11 | }); | ||
| 12 | } | ||
| 13 | } | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index c8a0e1705..bcd9bd5c1 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -29,8 +29,6 @@ pub mod adc; | |||
| 29 | pub mod can; | 29 | pub mod can; |
| 30 | #[cfg(dac)] | 30 | #[cfg(dac)] |
| 31 | pub mod dac; | 31 | pub mod dac; |
| 32 | #[cfg(dbgmcu)] | ||
| 33 | pub mod dbgmcu; | ||
| 34 | #[cfg(dcmi)] | 32 | #[cfg(dcmi)] |
| 35 | pub mod dcmi; | 33 | pub mod dcmi; |
| 36 | #[cfg(all(eth, feature = "net"))] | 34 | #[cfg(all(eth, feature = "net"))] |
| @@ -43,8 +41,6 @@ pub mod i2c; | |||
| 43 | #[cfg(crc)] | 41 | #[cfg(crc)] |
| 44 | pub mod crc; | 42 | pub mod crc; |
| 45 | pub mod pwm; | 43 | pub mod pwm; |
| 46 | #[cfg(pwr)] | ||
| 47 | pub mod pwr; | ||
| 48 | #[cfg(rng)] | 44 | #[cfg(rng)] |
| 49 | pub mod rng; | 45 | pub mod rng; |
| 50 | #[cfg(sdmmc)] | 46 | #[cfg(sdmmc)] |
| @@ -92,7 +88,13 @@ pub fn init(config: Config) -> Peripherals { | |||
| 92 | 88 | ||
| 93 | unsafe { | 89 | unsafe { |
| 94 | if config.enable_debug_during_sleep { | 90 | if config.enable_debug_during_sleep { |
| 95 | dbgmcu::Dbgmcu::enable_all(); | 91 | crate::pac::DBGMCU.cr().modify(|cr| { |
| 92 | crate::pac::dbgmcu! { | ||
| 93 | (cr, $fn_name:ident) => { | ||
| 94 | cr.$fn_name(true); | ||
| 95 | }; | ||
| 96 | } | ||
| 97 | }); | ||
| 96 | } | 98 | } |
| 97 | 99 | ||
| 98 | gpio::init(); | 100 | gpio::init(); |
diff --git a/embassy-stm32/src/pwr/f3.rs b/embassy-stm32/src/pwr/f3.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/pwr/f3.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/pwr/f4.rs b/embassy-stm32/src/pwr/f4.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/pwr/f4.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/pwr/f7.rs b/embassy-stm32/src/pwr/f7.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/pwr/f7.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/pwr/g0.rs b/embassy-stm32/src/pwr/g0.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/pwr/g0.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/pwr/g4.rs b/embassy-stm32/src/pwr/g4.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/pwr/g4.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/pwr/h7.rs b/embassy-stm32/src/pwr/h7.rs deleted file mode 100644 index 37b049a85..000000000 --- a/embassy-stm32/src/pwr/h7.rs +++ /dev/null | |||
| @@ -1,78 +0,0 @@ | |||
| 1 | use crate::pac::{PWR, RCC, SYSCFG}; | ||
| 2 | use crate::peripherals; | ||
| 3 | |||
| 4 | /// Voltage Scale | ||
| 5 | /// | ||
| 6 | /// Represents the voltage range feeding the CPU core. The maximum core | ||
| 7 | /// clock frequency depends on this value. | ||
| 8 | #[derive(Copy, Clone, PartialEq)] | ||
| 9 | pub enum VoltageScale { | ||
| 10 | /// VOS 0 range VCORE 1.26V - 1.40V | ||
| 11 | Scale0, | ||
| 12 | /// VOS 1 range VCORE 1.15V - 1.26V | ||
| 13 | Scale1, | ||
| 14 | /// VOS 2 range VCORE 1.05V - 1.15V | ||
| 15 | Scale2, | ||
| 16 | /// VOS 3 range VCORE 0.95V - 1.05V | ||
| 17 | Scale3, | ||
| 18 | } | ||
| 19 | |||
| 20 | /// Power Configuration | ||
| 21 | /// | ||
| 22 | /// Generated when the PWR peripheral is frozen. The existence of this | ||
| 23 | /// value indicates that the voltage scaling configuration can no | ||
| 24 | /// longer be changed. | ||
| 25 | pub struct Power { | ||
| 26 | pub(crate) vos: VoltageScale, | ||
| 27 | } | ||
| 28 | |||
| 29 | impl Power { | ||
| 30 | pub fn new(_peri: peripherals::PWR, enable_overdrive: bool) -> Self { | ||
| 31 | // NOTE(unsafe) we have the PWR singleton | ||
| 32 | unsafe { | ||
| 33 | // NB. The lower bytes of CR3 can only be written once after | ||
| 34 | // POR, and must be written with a valid combination. Refer to | ||
| 35 | // RM0433 Rev 7 6.8.4. This is partially enforced by dropping | ||
| 36 | // `self` at the end of this method, but of course we cannot | ||
| 37 | // know what happened between the previous POR and here. | ||
| 38 | #[cfg(pwr_h7)] | ||
| 39 | PWR.cr3().modify(|w| { | ||
| 40 | w.set_scuen(true); | ||
| 41 | w.set_ldoen(true); | ||
| 42 | w.set_bypass(false); | ||
| 43 | }); | ||
| 44 | |||
| 45 | #[cfg(pwr_h7smps)] | ||
| 46 | PWR.cr3().modify(|w| { | ||
| 47 | // hardcode "Direct SPMS" for now, this is what works on nucleos with the | ||
| 48 | // default solderbridge configuration. | ||
| 49 | w.set_sden(true); | ||
| 50 | w.set_ldoen(false); | ||
| 51 | }); | ||
| 52 | |||
| 53 | // Validate the supply configuration. If you are stuck here, it is | ||
| 54 | // because the voltages on your board do not match those specified | ||
| 55 | // in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset | ||
| 56 | // VOS = Scale 3, so check that the voltage on the VCAP pins = | ||
| 57 | // 1.0V. | ||
| 58 | while !PWR.csr1().read().actvosrdy() {} | ||
| 59 | |||
| 60 | // Go to Scale 1 | ||
| 61 | PWR.d3cr().modify(|w| w.set_vos(0b11)); | ||
| 62 | while !PWR.d3cr().read().vosrdy() {} | ||
| 63 | |||
| 64 | let vos = if !enable_overdrive { | ||
| 65 | VoltageScale::Scale1 | ||
| 66 | } else { | ||
| 67 | critical_section::with(|_| { | ||
| 68 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | ||
| 69 | |||
| 70 | SYSCFG.pwrcr().modify(|w| w.set_oden(1)); | ||
| 71 | }); | ||
| 72 | while !PWR.d3cr().read().vosrdy() {} | ||
| 73 | VoltageScale::Scale0 | ||
| 74 | }; | ||
| 75 | Self { vos } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/embassy-stm32/src/pwr/l1.rs b/embassy-stm32/src/pwr/l1.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/pwr/l1.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/pwr/mod.rs b/embassy-stm32/src/pwr/mod.rs deleted file mode 100644 index 18f462bd2..000000000 --- a/embassy-stm32/src/pwr/mod.rs +++ /dev/null | |||
| @@ -1,12 +0,0 @@ | |||
| 1 | #[cfg_attr(any(pwr_h7, pwr_h7smps), path = "h7.rs")] | ||
| 2 | #[cfg_attr(pwr_f3, path = "f3.rs")] | ||
| 3 | #[cfg_attr(pwr_f4, path = "f4.rs")] | ||
| 4 | #[cfg_attr(pwr_f7, path = "f7.rs")] | ||
| 5 | #[cfg_attr(pwr_wl5, path = "wl5.rs")] | ||
| 6 | #[cfg_attr(pwr_g0, path = "g0.rs")] | ||
| 7 | #[cfg_attr(pwr_g4, path = "g4.rs")] | ||
| 8 | #[cfg_attr(pwr_l1, path = "l1.rs")] | ||
| 9 | #[cfg_attr(pwr_u5, path = "u5.rs")] | ||
| 10 | mod _version; | ||
| 11 | |||
| 12 | pub use _version::*; | ||
diff --git a/embassy-stm32/src/pwr/u5.rs b/embassy-stm32/src/pwr/u5.rs deleted file mode 100644 index a90659d9c..000000000 --- a/embassy-stm32/src/pwr/u5.rs +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | use crate::peripherals; | ||
| 2 | |||
| 3 | /// Voltage Scale | ||
| 4 | /// | ||
| 5 | /// Represents the voltage range feeding the CPU core. The maximum core | ||
| 6 | /// clock frequency depends on this value. | ||
| 7 | #[derive(Copy, Clone, PartialEq)] | ||
| 8 | pub enum VoltageScale { | ||
| 9 | // Highest frequency | ||
| 10 | Range1, | ||
| 11 | Range2, | ||
| 12 | Range3, | ||
| 13 | // Lowest power | ||
| 14 | Range4, | ||
| 15 | } | ||
| 16 | |||
| 17 | /// Power Configuration | ||
| 18 | /// | ||
| 19 | /// Generated when the PWR peripheral is frozen. The existence of this | ||
| 20 | /// value indicates that the voltage scaling configuration can no | ||
| 21 | /// longer be changed. | ||
| 22 | pub struct Power { | ||
| 23 | pub(crate) vos: VoltageScale, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl Power { | ||
| 27 | pub fn new(_peri: peripherals::PWR) -> Self { | ||
| 28 | Self { | ||
| 29 | vos: VoltageScale::Range4, | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
diff --git a/embassy-stm32/src/pwr/wl5.rs b/embassy-stm32/src/pwr/wl5.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/pwr/wl5.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/rcc/f0.rs b/embassy-stm32/src/rcc/f0.rs new file mode 100644 index 000000000..1527afa05 --- /dev/null +++ b/embassy-stm32/src/rcc/f0.rs | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Sw, Usbsw}; | ||
| 2 | use crate::pac::{FLASH, RCC}; | ||
| 3 | use crate::time::Hertz; | ||
| 4 | |||
| 5 | use super::{set_freqs, Clocks}; | ||
| 6 | |||
| 7 | const HSI: u32 = 8_000_000; | ||
| 8 | |||
| 9 | /// Configuration of the clocks | ||
| 10 | /// | ||
| 11 | /// hse takes precedence over hsi48 if both are enabled | ||
| 12 | #[non_exhaustive] | ||
| 13 | #[derive(Default)] | ||
| 14 | pub struct Config { | ||
| 15 | pub hse: Option<Hertz>, | ||
| 16 | pub bypass_hse: bool, | ||
| 17 | pub usb_pll: bool, | ||
| 18 | |||
| 19 | #[cfg(rcc_f0)] | ||
| 20 | pub hsi48: bool, | ||
| 21 | |||
| 22 | pub sys_ck: Option<Hertz>, | ||
| 23 | pub hclk: Option<Hertz>, | ||
| 24 | pub pclk: Option<Hertz>, | ||
| 25 | } | ||
| 26 | |||
| 27 | pub(crate) unsafe fn init(config: Config) { | ||
| 28 | let sysclk = config.sys_ck.map(|v| v.0).unwrap_or(HSI); | ||
| 29 | |||
| 30 | let (src_clk, use_hsi48) = config.hse.map(|v| (v.0, false)).unwrap_or_else(|| { | ||
| 31 | #[cfg(rcc_f0)] | ||
| 32 | if config.hsi48 { | ||
| 33 | return (48_000_000, true); | ||
| 34 | } | ||
| 35 | (HSI, false) | ||
| 36 | }); | ||
| 37 | |||
| 38 | let (pllmul_bits, real_sysclk) = if sysclk == src_clk { | ||
| 39 | (None, sysclk) | ||
| 40 | } else { | ||
| 41 | let prediv = if config.hse.is_some() { 1 } else { 2 }; | ||
| 42 | let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2; | ||
| 43 | let pllmul = pllmul.max(2).min(16); | ||
| 44 | |||
| 45 | let pllmul_bits = pllmul as u8 - 2; | ||
| 46 | let real_sysclk = pllmul * src_clk / prediv; | ||
| 47 | (Some(pllmul_bits), real_sysclk) | ||
| 48 | }; | ||
| 49 | |||
| 50 | let hpre_bits = config | ||
| 51 | .hclk | ||
| 52 | .map(|hclk| match real_sysclk / hclk.0 { | ||
| 53 | 0 => unreachable!(), | ||
| 54 | 1 => 0b0111, | ||
| 55 | 2 => 0b1000, | ||
| 56 | 3..=5 => 0b1001, | ||
| 57 | 6..=11 => 0b1010, | ||
| 58 | 12..=39 => 0b1011, | ||
| 59 | 40..=95 => 0b1100, | ||
| 60 | 96..=191 => 0b1101, | ||
| 61 | 192..=383 => 0b1110, | ||
| 62 | _ => 0b1111, | ||
| 63 | }) | ||
| 64 | .unwrap_or(0b0111); | ||
| 65 | let hclk = real_sysclk / (1 << (hpre_bits - 0b0111)); | ||
| 66 | |||
| 67 | let ppre_bits = config | ||
| 68 | .pclk | ||
| 69 | .map(|pclk| match hclk / pclk.0 { | ||
| 70 | 0 => unreachable!(), | ||
| 71 | 1 => 0b011, | ||
| 72 | 2 => 0b100, | ||
| 73 | 3..=5 => 0b101, | ||
| 74 | 6..=11 => 0b110, | ||
| 75 | _ => 0b111, | ||
| 76 | }) | ||
| 77 | .unwrap_or(0b011); | ||
| 78 | |||
| 79 | let ppre: u8 = 1 << (ppre_bits - 0b011); | ||
| 80 | let pclk = hclk / u32::from(ppre); | ||
| 81 | |||
| 82 | let timer_mul = if ppre == 1 { 1 } else { 2 }; | ||
| 83 | |||
| 84 | FLASH.acr().write(|w| { | ||
| 85 | let latency = if real_sysclk <= 24_000_000 { | ||
| 86 | 0 | ||
| 87 | } else if real_sysclk <= 48_000_000 { | ||
| 88 | 1 | ||
| 89 | } else { | ||
| 90 | 2 | ||
| 91 | }; | ||
| 92 | w.latency().0 = latency; | ||
| 93 | }); | ||
| 94 | |||
| 95 | match (config.hse.is_some(), use_hsi48) { | ||
| 96 | (true, _) => { | ||
| 97 | RCC.cr().modify(|w| { | ||
| 98 | w.set_csson(true); | ||
| 99 | w.set_hseon(true); | ||
| 100 | |||
| 101 | if config.bypass_hse { | ||
| 102 | w.set_hsebyp(Hsebyp::BYPASSED); | ||
| 103 | } | ||
| 104 | }); | ||
| 105 | while !RCC.cr().read().hserdy() {} | ||
| 106 | |||
| 107 | if pllmul_bits.is_some() { | ||
| 108 | RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV)) | ||
| 109 | } | ||
| 110 | } | ||
| 111 | (false, true) => { | ||
| 112 | // use_hsi48 will always be false for rcc_f0x0 | ||
| 113 | #[cfg(rcc_f0)] | ||
| 114 | RCC.cr2().modify(|w| w.set_hsi48on(true)); | ||
| 115 | #[cfg(rcc_f0)] | ||
| 116 | while !RCC.cr2().read().hsi48rdy() {} | ||
| 117 | |||
| 118 | #[cfg(rcc_f0)] | ||
| 119 | if pllmul_bits.is_some() { | ||
| 120 | RCC.cfgr() | ||
| 121 | .modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV)) | ||
| 122 | } | ||
| 123 | } | ||
| 124 | _ => { | ||
| 125 | RCC.cr().modify(|w| w.set_hsion(true)); | ||
| 126 | while !RCC.cr().read().hsirdy() {} | ||
| 127 | |||
| 128 | if pllmul_bits.is_some() { | ||
| 129 | RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2)) | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | if config.usb_pll { | ||
| 135 | RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK)); | ||
| 136 | } | ||
| 137 | // TODO: Option to use CRS (Clock Recovery) | ||
| 138 | |||
| 139 | if let Some(pllmul_bits) = pllmul_bits { | ||
| 140 | RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits))); | ||
| 141 | |||
| 142 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 143 | while !RCC.cr().read().pllrdy() {} | ||
| 144 | |||
| 145 | RCC.cfgr().modify(|w| { | ||
| 146 | w.set_ppre(Ppre(ppre_bits)); | ||
| 147 | w.set_hpre(Hpre(hpre_bits)); | ||
| 148 | w.set_sw(Sw::PLL) | ||
| 149 | }); | ||
| 150 | } else { | ||
| 151 | RCC.cfgr().modify(|w| { | ||
| 152 | w.set_ppre(Ppre(ppre_bits)); | ||
| 153 | w.set_hpre(Hpre(hpre_bits)); | ||
| 154 | |||
| 155 | if config.hse.is_some() { | ||
| 156 | w.set_sw(Sw::HSE); | ||
| 157 | } else if use_hsi48 { | ||
| 158 | #[cfg(rcc_f0)] | ||
| 159 | w.set_sw(Sw::HSI48); | ||
| 160 | } else { | ||
| 161 | w.set_sw(Sw::HSI) | ||
| 162 | } | ||
| 163 | }) | ||
| 164 | } | ||
| 165 | |||
| 166 | set_freqs(Clocks { | ||
| 167 | sys: Hertz(real_sysclk), | ||
| 168 | apb1: Hertz(pclk), | ||
| 169 | apb2: Hertz(pclk), | ||
| 170 | apb1_tim: Hertz(pclk * timer_mul), | ||
| 171 | apb2_tim: Hertz(pclk * timer_mul), | ||
| 172 | ahb: Hertz(hclk), | ||
| 173 | }); | ||
| 174 | } | ||
diff --git a/embassy-stm32/src/rcc/f0/mod.rs b/embassy-stm32/src/rcc/f0/mod.rs deleted file mode 100644 index 916ed39fc..000000000 --- a/embassy-stm32/src/rcc/f0/mod.rs +++ /dev/null | |||
| @@ -1,208 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy::util::Unborrow; | ||
| 4 | |||
| 5 | use crate::pac::{FLASH, RCC}; | ||
| 6 | use crate::peripherals; | ||
| 7 | use crate::time::Hertz; | ||
| 8 | |||
| 9 | use super::{set_freqs, Clocks}; | ||
| 10 | |||
| 11 | const HSI: u32 = 8_000_000; | ||
| 12 | |||
| 13 | /// Configuration of the clocks | ||
| 14 | /// | ||
| 15 | /// hse takes precedence over hsi48 if both are enabled | ||
| 16 | #[non_exhaustive] | ||
| 17 | #[derive(Default)] | ||
| 18 | pub struct Config { | ||
| 19 | pub hse: Option<Hertz>, | ||
| 20 | pub bypass_hse: bool, | ||
| 21 | pub usb_pll: bool, | ||
| 22 | |||
| 23 | #[cfg(rcc_f0)] | ||
| 24 | pub hsi48: bool, | ||
| 25 | |||
| 26 | pub sys_ck: Option<Hertz>, | ||
| 27 | pub hclk: Option<Hertz>, | ||
| 28 | pub pclk: Option<Hertz>, | ||
| 29 | } | ||
| 30 | |||
| 31 | pub struct Rcc<'d> { | ||
| 32 | inner: PhantomData<&'d ()>, | ||
| 33 | config: Config, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl<'d> Rcc<'d> { | ||
| 37 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 38 | Self { | ||
| 39 | inner: PhantomData, | ||
| 40 | config, | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn freeze(self) -> Clocks { | ||
| 45 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Sw, Usbsw}; | ||
| 46 | |||
| 47 | let sysclk = self.config.sys_ck.map(|v| v.0).unwrap_or(HSI); | ||
| 48 | |||
| 49 | let (src_clk, use_hsi48) = self.config.hse.map(|v| (v.0, false)).unwrap_or_else(|| { | ||
| 50 | #[cfg(rcc_f0)] | ||
| 51 | if self.config.hsi48 { | ||
| 52 | return (48_000_000, true); | ||
| 53 | } | ||
| 54 | (HSI, false) | ||
| 55 | }); | ||
| 56 | |||
| 57 | let (pllmul_bits, real_sysclk) = if sysclk == src_clk { | ||
| 58 | (None, sysclk) | ||
| 59 | } else { | ||
| 60 | let prediv = if self.config.hse.is_some() { 1 } else { 2 }; | ||
| 61 | let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2; | ||
| 62 | let pllmul = pllmul.max(2).min(16); | ||
| 63 | |||
| 64 | let pllmul_bits = pllmul as u8 - 2; | ||
| 65 | let real_sysclk = pllmul * src_clk / prediv; | ||
| 66 | (Some(pllmul_bits), real_sysclk) | ||
| 67 | }; | ||
| 68 | |||
| 69 | let hpre_bits = self | ||
| 70 | .config | ||
| 71 | .hclk | ||
| 72 | .map(|hclk| match real_sysclk / hclk.0 { | ||
| 73 | 0 => unreachable!(), | ||
| 74 | 1 => 0b0111, | ||
| 75 | 2 => 0b1000, | ||
| 76 | 3..=5 => 0b1001, | ||
| 77 | 6..=11 => 0b1010, | ||
| 78 | 12..=39 => 0b1011, | ||
| 79 | 40..=95 => 0b1100, | ||
| 80 | 96..=191 => 0b1101, | ||
| 81 | 192..=383 => 0b1110, | ||
| 82 | _ => 0b1111, | ||
| 83 | }) | ||
| 84 | .unwrap_or(0b0111); | ||
| 85 | let hclk = real_sysclk / (1 << (hpre_bits - 0b0111)); | ||
| 86 | |||
| 87 | let ppre_bits = self | ||
| 88 | .config | ||
| 89 | .pclk | ||
| 90 | .map(|pclk| match hclk / pclk.0 { | ||
| 91 | 0 => unreachable!(), | ||
| 92 | 1 => 0b011, | ||
| 93 | 2 => 0b100, | ||
| 94 | 3..=5 => 0b101, | ||
| 95 | 6..=11 => 0b110, | ||
| 96 | _ => 0b111, | ||
| 97 | }) | ||
| 98 | .unwrap_or(0b011); | ||
| 99 | |||
| 100 | let ppre: u8 = 1 << (ppre_bits - 0b011); | ||
| 101 | let pclk = hclk / u32::from(ppre); | ||
| 102 | |||
| 103 | let timer_mul = if ppre == 1 { 1 } else { 2 }; | ||
| 104 | |||
| 105 | // NOTE(safety) Atomic write | ||
| 106 | unsafe { | ||
| 107 | FLASH.acr().write(|w| { | ||
| 108 | let latency = if real_sysclk <= 24_000_000 { | ||
| 109 | 0 | ||
| 110 | } else if real_sysclk <= 48_000_000 { | ||
| 111 | 1 | ||
| 112 | } else { | ||
| 113 | 2 | ||
| 114 | }; | ||
| 115 | w.latency().0 = latency; | ||
| 116 | }); | ||
| 117 | } | ||
| 118 | |||
| 119 | // NOTE(unsafe) We have exclusive access to the RCC | ||
| 120 | unsafe { | ||
| 121 | match (self.config.hse.is_some(), use_hsi48) { | ||
| 122 | (true, _) => { | ||
| 123 | RCC.cr().modify(|w| { | ||
| 124 | w.set_csson(true); | ||
| 125 | w.set_hseon(true); | ||
| 126 | |||
| 127 | if self.config.bypass_hse { | ||
| 128 | w.set_hsebyp(Hsebyp::BYPASSED); | ||
| 129 | } | ||
| 130 | }); | ||
| 131 | while !RCC.cr().read().hserdy() {} | ||
| 132 | |||
| 133 | if pllmul_bits.is_some() { | ||
| 134 | RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV)) | ||
| 135 | } | ||
| 136 | } | ||
| 137 | (false, true) => { | ||
| 138 | // use_hsi48 will always be false for rcc_f0x0 | ||
| 139 | #[cfg(rcc_f0)] | ||
| 140 | RCC.cr2().modify(|w| w.set_hsi48on(true)); | ||
| 141 | #[cfg(rcc_f0)] | ||
| 142 | while !RCC.cr2().read().hsi48rdy() {} | ||
| 143 | |||
| 144 | #[cfg(rcc_f0)] | ||
| 145 | if pllmul_bits.is_some() { | ||
| 146 | RCC.cfgr() | ||
| 147 | .modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV)) | ||
| 148 | } | ||
| 149 | } | ||
| 150 | _ => { | ||
| 151 | RCC.cr().modify(|w| w.set_hsion(true)); | ||
| 152 | while !RCC.cr().read().hsirdy() {} | ||
| 153 | |||
| 154 | if pllmul_bits.is_some() { | ||
| 155 | RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2)) | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | if self.config.usb_pll { | ||
| 161 | RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK)); | ||
| 162 | } | ||
| 163 | // TODO: Option to use CRS (Clock Recovery) | ||
| 164 | |||
| 165 | if let Some(pllmul_bits) = pllmul_bits { | ||
| 166 | RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits))); | ||
| 167 | |||
| 168 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 169 | while !RCC.cr().read().pllrdy() {} | ||
| 170 | |||
| 171 | RCC.cfgr().modify(|w| { | ||
| 172 | w.set_ppre(Ppre(ppre_bits)); | ||
| 173 | w.set_hpre(Hpre(hpre_bits)); | ||
| 174 | w.set_sw(Sw::PLL) | ||
| 175 | }); | ||
| 176 | } else { | ||
| 177 | RCC.cfgr().modify(|w| { | ||
| 178 | w.set_ppre(Ppre(ppre_bits)); | ||
| 179 | w.set_hpre(Hpre(hpre_bits)); | ||
| 180 | |||
| 181 | if self.config.hse.is_some() { | ||
| 182 | w.set_sw(Sw::HSE); | ||
| 183 | } else if use_hsi48 { | ||
| 184 | #[cfg(rcc_f0)] | ||
| 185 | w.set_sw(Sw::HSI48); | ||
| 186 | } else { | ||
| 187 | w.set_sw(Sw::HSI) | ||
| 188 | } | ||
| 189 | }) | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | Clocks { | ||
| 194 | sys: Hertz(real_sysclk), | ||
| 195 | apb1: Hertz(pclk), | ||
| 196 | apb2: Hertz(pclk), | ||
| 197 | apb1_tim: Hertz(pclk * timer_mul), | ||
| 198 | apb2_tim: Hertz(pclk * timer_mul), | ||
| 199 | ahb: Hertz(hclk), | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | pub unsafe fn init(config: Config) { | ||
| 205 | let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config); | ||
| 206 | let clocks = rcc.freeze(); | ||
| 207 | set_freqs(clocks); | ||
| 208 | } | ||
diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs new file mode 100644 index 000000000..d44544d28 --- /dev/null +++ b/embassy-stm32/src/rcc/f1.rs | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | use core::convert::TryFrom; | ||
| 2 | |||
| 3 | use super::{set_freqs, Clocks}; | ||
| 4 | use crate::pac::flash::vals::Latency; | ||
| 5 | use crate::pac::rcc::vals::{Adcpre, Hpre, Pllmul, Pllsrc, Ppre1, Sw, Usbpre}; | ||
| 6 | use crate::pac::{FLASH, RCC}; | ||
| 7 | use crate::time::Hertz; | ||
| 8 | |||
| 9 | const HSI: u32 = 8_000_000; | ||
| 10 | |||
| 11 | /// Configuration of the clocks | ||
| 12 | /// | ||
| 13 | #[non_exhaustive] | ||
| 14 | #[derive(Default)] | ||
| 15 | pub struct Config { | ||
| 16 | pub hse: Option<Hertz>, | ||
| 17 | |||
| 18 | pub sys_ck: Option<Hertz>, | ||
| 19 | pub hclk: Option<Hertz>, | ||
| 20 | pub pclk1: Option<Hertz>, | ||
| 21 | pub pclk2: Option<Hertz>, | ||
| 22 | pub adcclk: Option<Hertz>, | ||
| 23 | } | ||
| 24 | |||
| 25 | pub(crate) unsafe fn init(config: Config) { | ||
| 26 | let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI / 2); | ||
| 27 | let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | ||
| 28 | let pllmul = sysclk / pllsrcclk; | ||
| 29 | |||
| 30 | let (pllmul_bits, real_sysclk) = if pllmul == 1 { | ||
| 31 | (None, config.hse.map(|hse| hse.0).unwrap_or(HSI)) | ||
| 32 | } else { | ||
| 33 | let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16); | ||
| 34 | (Some(pllmul as u8 - 2), pllsrcclk * pllmul) | ||
| 35 | }; | ||
| 36 | |||
| 37 | assert!(real_sysclk <= 72_000_000); | ||
| 38 | |||
| 39 | let hpre_bits = config | ||
| 40 | .hclk | ||
| 41 | .map(|hclk| match real_sysclk / hclk.0 { | ||
| 42 | 0 => unreachable!(), | ||
| 43 | 1 => 0b0111, | ||
| 44 | 2 => 0b1000, | ||
| 45 | 3..=5 => 0b1001, | ||
| 46 | 6..=11 => 0b1010, | ||
| 47 | 12..=39 => 0b1011, | ||
| 48 | 40..=95 => 0b1100, | ||
| 49 | 96..=191 => 0b1101, | ||
| 50 | 192..=383 => 0b1110, | ||
| 51 | _ => 0b1111, | ||
| 52 | }) | ||
| 53 | .unwrap_or(0b0111); | ||
| 54 | |||
| 55 | let hclk = if hpre_bits >= 0b1100 { | ||
| 56 | real_sysclk / (1 << (hpre_bits - 0b0110)) | ||
| 57 | } else { | ||
| 58 | real_sysclk / (1 << (hpre_bits - 0b0111)) | ||
| 59 | }; | ||
| 60 | |||
| 61 | assert!(hclk <= 72_000_000); | ||
| 62 | |||
| 63 | let ppre1_bits = config | ||
| 64 | .pclk1 | ||
| 65 | .map(|pclk1| match hclk / pclk1.0 { | ||
| 66 | 0 => unreachable!(), | ||
| 67 | 1 => 0b011, | ||
| 68 | 2 => 0b100, | ||
| 69 | 3..=5 => 0b101, | ||
| 70 | 6..=11 => 0b110, | ||
| 71 | _ => 0b111, | ||
| 72 | }) | ||
| 73 | .unwrap_or(0b011); | ||
| 74 | |||
| 75 | let ppre1 = 1 << (ppre1_bits - 0b011); | ||
| 76 | let pclk1 = hclk / u32::try_from(ppre1).unwrap(); | ||
| 77 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 78 | |||
| 79 | assert!(pclk1 <= 36_000_000); | ||
| 80 | |||
| 81 | let ppre2_bits = config | ||
| 82 | .pclk2 | ||
| 83 | .map(|pclk2| match hclk / pclk2.0 { | ||
| 84 | 0 => unreachable!(), | ||
| 85 | 1 => 0b011, | ||
| 86 | 2 => 0b100, | ||
| 87 | 3..=5 => 0b101, | ||
| 88 | 6..=11 => 0b110, | ||
| 89 | _ => 0b111, | ||
| 90 | }) | ||
| 91 | .unwrap_or(0b011); | ||
| 92 | |||
| 93 | let ppre2 = 1 << (ppre2_bits - 0b011); | ||
| 94 | let pclk2 = hclk / u32::try_from(ppre2).unwrap(); | ||
| 95 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 96 | |||
| 97 | assert!(pclk2 <= 72_000_000); | ||
| 98 | |||
| 99 | // Only needed for stm32f103? | ||
| 100 | FLASH.acr().write(|w| { | ||
| 101 | w.set_latency(if real_sysclk <= 24_000_000 { | ||
| 102 | Latency(0b000) | ||
| 103 | } else if real_sysclk <= 48_000_000 { | ||
| 104 | Latency(0b001) | ||
| 105 | } else { | ||
| 106 | Latency(0b010) | ||
| 107 | }); | ||
| 108 | }); | ||
| 109 | |||
| 110 | // the USB clock is only valid if an external crystal is used, the PLL is enabled, and the | ||
| 111 | // PLL output frequency is a supported one. | ||
| 112 | // usbpre == false: divide clock by 1.5, otherwise no division | ||
| 113 | let (usbpre, _usbclk_valid) = match (config.hse, pllmul_bits, real_sysclk) { | ||
| 114 | (Some(_), Some(_), 72_000_000) => (false, true), | ||
| 115 | (Some(_), Some(_), 48_000_000) => (true, true), | ||
| 116 | _ => (true, false), | ||
| 117 | }; | ||
| 118 | |||
| 119 | let apre_bits: u8 = config | ||
| 120 | .adcclk | ||
| 121 | .map(|adcclk| match pclk2 / adcclk.0 { | ||
| 122 | 0..=2 => 0b00, | ||
| 123 | 3..=4 => 0b01, | ||
| 124 | 5..=7 => 0b10, | ||
| 125 | _ => 0b11, | ||
| 126 | }) | ||
| 127 | .unwrap_or(0b11); | ||
| 128 | |||
| 129 | let apre = (apre_bits + 1) << 1; | ||
| 130 | let adcclk = pclk2 / unwrap!(u32::try_from(apre)); | ||
| 131 | |||
| 132 | assert!(adcclk <= 14_000_000); | ||
| 133 | |||
| 134 | if config.hse.is_some() { | ||
| 135 | // enable HSE and wait for it to be ready | ||
| 136 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 137 | while !RCC.cr().read().hserdy() {} | ||
| 138 | } | ||
| 139 | |||
| 140 | if let Some(pllmul_bits) = pllmul_bits { | ||
| 141 | // enable PLL and wait for it to be ready | ||
| 142 | RCC.cfgr().modify(|w| { | ||
| 143 | w.set_pllmul(Pllmul(pllmul_bits)); | ||
| 144 | w.set_pllsrc(Pllsrc(config.hse.is_some() as u8)); | ||
| 145 | }); | ||
| 146 | |||
| 147 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 148 | while !RCC.cr().read().pllrdy() {} | ||
| 149 | } | ||
| 150 | |||
| 151 | // Only needed for stm32f103? | ||
| 152 | RCC.cfgr().modify(|w| { | ||
| 153 | w.set_adcpre(Adcpre(apre_bits)); | ||
| 154 | w.set_ppre2(Ppre1(ppre2_bits)); | ||
| 155 | w.set_ppre1(Ppre1(ppre1_bits)); | ||
| 156 | w.set_hpre(Hpre(hpre_bits)); | ||
| 157 | w.set_usbpre(Usbpre(usbpre as u8)); | ||
| 158 | w.set_sw(Sw(if pllmul_bits.is_some() { | ||
| 159 | // PLL | ||
| 160 | 0b10 | ||
| 161 | } else if config.hse.is_some() { | ||
| 162 | // HSE | ||
| 163 | 0b1 | ||
| 164 | } else { | ||
| 165 | // HSI | ||
| 166 | 0b0 | ||
| 167 | })); | ||
| 168 | }); | ||
| 169 | |||
| 170 | set_freqs(Clocks { | ||
| 171 | sys: Hertz(real_sysclk), | ||
| 172 | apb1: Hertz(pclk1), | ||
| 173 | apb2: Hertz(pclk2), | ||
| 174 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 175 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 176 | ahb: Hertz(hclk), | ||
| 177 | adc: Hertz(adcclk), | ||
| 178 | }); | ||
| 179 | } | ||
diff --git a/embassy-stm32/src/rcc/f1/mod.rs b/embassy-stm32/src/rcc/f1/mod.rs deleted file mode 100644 index d613f5a2f..000000000 --- a/embassy-stm32/src/rcc/f1/mod.rs +++ /dev/null | |||
| @@ -1,214 +0,0 @@ | |||
| 1 | use core::convert::TryFrom; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy::util::Unborrow; | ||
| 5 | |||
| 6 | use crate::pac::flash::vals::Latency; | ||
| 7 | use crate::pac::{FLASH, RCC}; | ||
| 8 | use crate::peripherals; | ||
| 9 | use crate::time::Hertz; | ||
| 10 | |||
| 11 | use super::{set_freqs, Clocks}; | ||
| 12 | |||
| 13 | const HSI: u32 = 8_000_000; | ||
| 14 | |||
| 15 | /// Configuration of the clocks | ||
| 16 | /// | ||
| 17 | #[non_exhaustive] | ||
| 18 | #[derive(Default)] | ||
| 19 | pub struct Config { | ||
| 20 | pub hse: Option<Hertz>, | ||
| 21 | |||
| 22 | pub sys_ck: Option<Hertz>, | ||
| 23 | pub hclk: Option<Hertz>, | ||
| 24 | pub pclk1: Option<Hertz>, | ||
| 25 | pub pclk2: Option<Hertz>, | ||
| 26 | pub adcclk: Option<Hertz>, | ||
| 27 | } | ||
| 28 | |||
| 29 | pub struct Rcc<'d> { | ||
| 30 | inner: PhantomData<&'d ()>, | ||
| 31 | config: Config, | ||
| 32 | } | ||
| 33 | |||
| 34 | impl<'d> Rcc<'d> { | ||
| 35 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 36 | Self { | ||
| 37 | inner: PhantomData, | ||
| 38 | config, | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn freeze(self) -> Clocks { | ||
| 43 | use crate::pac::rcc::vals::{Adcpre, Hpre, Pllmul, Pllsrc, Ppre1, Sw, Usbpre}; | ||
| 44 | |||
| 45 | let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI / 2); | ||
| 46 | let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | ||
| 47 | let pllmul = sysclk / pllsrcclk; | ||
| 48 | |||
| 49 | let (pllmul_bits, real_sysclk) = if pllmul == 1 { | ||
| 50 | (None, self.config.hse.map(|hse| hse.0).unwrap_or(HSI)) | ||
| 51 | } else { | ||
| 52 | let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16); | ||
| 53 | (Some(pllmul as u8 - 2), pllsrcclk * pllmul) | ||
| 54 | }; | ||
| 55 | |||
| 56 | assert!(real_sysclk <= 72_000_000); | ||
| 57 | |||
| 58 | let hpre_bits = self | ||
| 59 | .config | ||
| 60 | .hclk | ||
| 61 | .map(|hclk| match real_sysclk / hclk.0 { | ||
| 62 | 0 => unreachable!(), | ||
| 63 | 1 => 0b0111, | ||
| 64 | 2 => 0b1000, | ||
| 65 | 3..=5 => 0b1001, | ||
| 66 | 6..=11 => 0b1010, | ||
| 67 | 12..=39 => 0b1011, | ||
| 68 | 40..=95 => 0b1100, | ||
| 69 | 96..=191 => 0b1101, | ||
| 70 | 192..=383 => 0b1110, | ||
| 71 | _ => 0b1111, | ||
| 72 | }) | ||
| 73 | .unwrap_or(0b0111); | ||
| 74 | |||
| 75 | let hclk = if hpre_bits >= 0b1100 { | ||
| 76 | real_sysclk / (1 << (hpre_bits - 0b0110)) | ||
| 77 | } else { | ||
| 78 | real_sysclk / (1 << (hpre_bits - 0b0111)) | ||
| 79 | }; | ||
| 80 | |||
| 81 | assert!(hclk <= 72_000_000); | ||
| 82 | |||
| 83 | let ppre1_bits = self | ||
| 84 | .config | ||
| 85 | .pclk1 | ||
| 86 | .map(|pclk1| match hclk / pclk1.0 { | ||
| 87 | 0 => unreachable!(), | ||
| 88 | 1 => 0b011, | ||
| 89 | 2 => 0b100, | ||
| 90 | 3..=5 => 0b101, | ||
| 91 | 6..=11 => 0b110, | ||
| 92 | _ => 0b111, | ||
| 93 | }) | ||
| 94 | .unwrap_or(0b011); | ||
| 95 | |||
| 96 | let ppre1 = 1 << (ppre1_bits - 0b011); | ||
| 97 | let pclk1 = hclk / u32::try_from(ppre1).unwrap(); | ||
| 98 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 99 | |||
| 100 | assert!(pclk1 <= 36_000_000); | ||
| 101 | |||
| 102 | let ppre2_bits = self | ||
| 103 | .config | ||
| 104 | .pclk2 | ||
| 105 | .map(|pclk2| match hclk / pclk2.0 { | ||
| 106 | 0 => unreachable!(), | ||
| 107 | 1 => 0b011, | ||
| 108 | 2 => 0b100, | ||
| 109 | 3..=5 => 0b101, | ||
| 110 | 6..=11 => 0b110, | ||
| 111 | _ => 0b111, | ||
| 112 | }) | ||
| 113 | .unwrap_or(0b011); | ||
| 114 | |||
| 115 | let ppre2 = 1 << (ppre2_bits - 0b011); | ||
| 116 | let pclk2 = hclk / u32::try_from(ppre2).unwrap(); | ||
| 117 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 118 | |||
| 119 | assert!(pclk2 <= 72_000_000); | ||
| 120 | |||
| 121 | // Only needed for stm32f103? | ||
| 122 | // NOTE(safety) Atomic write | ||
| 123 | unsafe { | ||
| 124 | FLASH.acr().write(|w| { | ||
| 125 | w.set_latency(if real_sysclk <= 24_000_000 { | ||
| 126 | Latency(0b000) | ||
| 127 | } else if real_sysclk <= 48_000_000 { | ||
| 128 | Latency(0b001) | ||
| 129 | } else { | ||
| 130 | Latency(0b010) | ||
| 131 | }); | ||
| 132 | }) | ||
| 133 | } | ||
| 134 | |||
| 135 | // the USB clock is only valid if an external crystal is used, the PLL is enabled, and the | ||
| 136 | // PLL output frequency is a supported one. | ||
| 137 | // usbpre == false: divide clock by 1.5, otherwise no division | ||
| 138 | let (usbpre, _usbclk_valid) = match (self.config.hse, pllmul_bits, real_sysclk) { | ||
| 139 | (Some(_), Some(_), 72_000_000) => (false, true), | ||
| 140 | (Some(_), Some(_), 48_000_000) => (true, true), | ||
| 141 | _ => (true, false), | ||
| 142 | }; | ||
| 143 | |||
| 144 | let apre_bits: u8 = self | ||
| 145 | .config | ||
| 146 | .adcclk | ||
| 147 | .map(|adcclk| match pclk2 / adcclk.0 { | ||
| 148 | 0..=2 => 0b00, | ||
| 149 | 3..=4 => 0b01, | ||
| 150 | 5..=7 => 0b10, | ||
| 151 | _ => 0b11, | ||
| 152 | }) | ||
| 153 | .unwrap_or(0b11); | ||
| 154 | |||
| 155 | let apre = (apre_bits + 1) << 1; | ||
| 156 | let adcclk = pclk2 / unwrap!(u32::try_from(apre)); | ||
| 157 | |||
| 158 | assert!(adcclk <= 14_000_000); | ||
| 159 | |||
| 160 | unsafe { | ||
| 161 | if self.config.hse.is_some() { | ||
| 162 | // enable HSE and wait for it to be ready | ||
| 163 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 164 | while !RCC.cr().read().hserdy() {} | ||
| 165 | } | ||
| 166 | |||
| 167 | if let Some(pllmul_bits) = pllmul_bits { | ||
| 168 | // enable PLL and wait for it to be ready | ||
| 169 | RCC.cfgr().modify(|w| { | ||
| 170 | w.set_pllmul(Pllmul(pllmul_bits)); | ||
| 171 | w.set_pllsrc(Pllsrc(self.config.hse.is_some() as u8)); | ||
| 172 | }); | ||
| 173 | |||
| 174 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 175 | while !RCC.cr().read().pllrdy() {} | ||
| 176 | } | ||
| 177 | |||
| 178 | // Only needed for stm32f103? | ||
| 179 | RCC.cfgr().modify(|w| { | ||
| 180 | w.set_adcpre(Adcpre(apre_bits)); | ||
| 181 | w.set_ppre2(Ppre1(ppre2_bits)); | ||
| 182 | w.set_ppre1(Ppre1(ppre1_bits)); | ||
| 183 | w.set_hpre(Hpre(hpre_bits)); | ||
| 184 | w.set_usbpre(Usbpre(usbpre as u8)); | ||
| 185 | w.set_sw(Sw(if pllmul_bits.is_some() { | ||
| 186 | // PLL | ||
| 187 | 0b10 | ||
| 188 | } else if self.config.hse.is_some() { | ||
| 189 | // HSE | ||
| 190 | 0b1 | ||
| 191 | } else { | ||
| 192 | // HSI | ||
| 193 | 0b0 | ||
| 194 | })); | ||
| 195 | }); | ||
| 196 | } | ||
| 197 | |||
| 198 | Clocks { | ||
| 199 | sys: Hertz(real_sysclk), | ||
| 200 | apb1: Hertz(pclk1), | ||
| 201 | apb2: Hertz(pclk2), | ||
| 202 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 203 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 204 | ahb: Hertz(hclk), | ||
| 205 | adc: Hertz(adcclk), | ||
| 206 | } | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | pub unsafe fn init(config: Config) { | ||
| 211 | let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config); | ||
| 212 | let clocks = rcc.freeze(); | ||
| 213 | set_freqs(clocks); | ||
| 214 | } | ||
diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs new file mode 100644 index 000000000..e16e1e499 --- /dev/null +++ b/embassy-stm32/src/rcc/f3.rs | |||
| @@ -0,0 +1,331 @@ | |||
| 1 | use crate::pac::flash::vals::Latency; | ||
| 2 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; | ||
| 3 | use crate::pac::{FLASH, RCC}; | ||
| 4 | use crate::rcc::{set_freqs, Clocks}; | ||
| 5 | use crate::time::Hertz; | ||
| 6 | |||
| 7 | const HSI: u32 = 8_000_000; | ||
| 8 | |||
| 9 | /// Clocks configutation | ||
| 10 | #[non_exhaustive] | ||
| 11 | #[derive(Default)] | ||
| 12 | pub struct Config { | ||
| 13 | /// Frequency of HSE oscillator | ||
| 14 | /// 4MHz to 32MHz | ||
| 15 | pub hse: Option<Hertz>, | ||
| 16 | /// Bypass HSE for an external clock | ||
| 17 | pub bypass_hse: bool, | ||
| 18 | /// Frequency of the System Clock | ||
| 19 | pub sysclk: Option<Hertz>, | ||
| 20 | /// Frequency of AHB bus | ||
| 21 | pub hclk: Option<Hertz>, | ||
| 22 | /// Frequency of APB1 bus | ||
| 23 | /// - Max frequency 36MHz | ||
| 24 | pub pclk1: Option<Hertz>, | ||
| 25 | /// Frequency of APB2 bus | ||
| 26 | /// - Max frequency with HSE is 72MHz | ||
| 27 | /// - Max frequency without HSE is 64MHz | ||
| 28 | pub pclk2: Option<Hertz>, | ||
| 29 | /// USB clock setup | ||
| 30 | /// It is valid only when, | ||
| 31 | /// - HSE is enabled, | ||
| 32 | /// - The System clock frequency is either 48MHz or 72MHz | ||
| 33 | /// - APB1 clock has a minimum frequency of 10MHz | ||
| 34 | pub pll48: bool, | ||
| 35 | } | ||
| 36 | |||
| 37 | // Information required to setup the PLL clock | ||
| 38 | struct PllConfig { | ||
| 39 | pll_src: Pllsrc, | ||
| 40 | pll_mul: Pllmul, | ||
| 41 | pll_div: Option<Prediv>, | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Initialize and Set the clock frequencies | ||
| 45 | pub(crate) unsafe fn init(config: Config) { | ||
| 46 | // Calculate the real System clock, and PLL configuration if applicable | ||
| 47 | let (Hertz(sysclk), pll_config) = get_sysclk(&config); | ||
| 48 | assert!(sysclk <= 72_000_000); | ||
| 49 | |||
| 50 | // Calculate real AHB clock | ||
| 51 | let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 52 | let (hpre_bits, hpre_div) = match sysclk / hclk { | ||
| 53 | 0 => unreachable!(), | ||
| 54 | 1 => (Hpre::DIV1, 1), | ||
| 55 | 2 => (Hpre::DIV2, 2), | ||
| 56 | 3..=5 => (Hpre::DIV4, 4), | ||
| 57 | 6..=11 => (Hpre::DIV8, 8), | ||
| 58 | 12..=39 => (Hpre::DIV16, 16), | ||
| 59 | 40..=95 => (Hpre::DIV64, 64), | ||
| 60 | 96..=191 => (Hpre::DIV128, 128), | ||
| 61 | 192..=383 => (Hpre::DIV256, 256), | ||
| 62 | _ => (Hpre::DIV512, 512), | ||
| 63 | }; | ||
| 64 | let hclk = sysclk / hpre_div; | ||
| 65 | assert!(hclk <= 72_000_000); | ||
| 66 | |||
| 67 | // Calculate real APB1 clock | ||
| 68 | let pclk1 = config.pclk1.map(|p| p.0).unwrap_or(hclk); | ||
| 69 | let (ppre1_bits, ppre1) = match hclk / pclk1 { | ||
| 70 | 0 => unreachable!(), | ||
| 71 | 1 => (Ppre::DIV1, 1), | ||
| 72 | 2 => (Ppre::DIV2, 2), | ||
| 73 | 3..=5 => (Ppre::DIV4, 4), | ||
| 74 | 6..=11 => (Ppre::DIV8, 8), | ||
| 75 | _ => (Ppre::DIV16, 16), | ||
| 76 | }; | ||
| 77 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 78 | let pclk1 = hclk / ppre1; | ||
| 79 | assert!(pclk1 <= 36_000_000); | ||
| 80 | |||
| 81 | // Calculate real APB2 clock | ||
| 82 | let pclk2 = config.pclk2.map(|p| p.0).unwrap_or(hclk); | ||
| 83 | let (ppre2_bits, ppre2) = match hclk / pclk2 { | ||
| 84 | 0 => unreachable!(), | ||
| 85 | 1 => (Ppre::DIV1, 1), | ||
| 86 | 2 => (Ppre::DIV2, 2), | ||
| 87 | 3..=5 => (Ppre::DIV4, 4), | ||
| 88 | 6..=11 => (Ppre::DIV8, 8), | ||
| 89 | _ => (Ppre::DIV16, 16), | ||
| 90 | }; | ||
| 91 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 92 | let pclk2 = hclk / ppre2; | ||
| 93 | assert!(pclk2 <= 72_000_000); | ||
| 94 | |||
| 95 | // Set latency based on HCLK frquency | ||
| 96 | FLASH.acr().write(|w| { | ||
| 97 | w.set_latency(if hclk <= 24_000_000 { | ||
| 98 | Latency::WS0 | ||
| 99 | } else if hclk <= 48_000_000 { | ||
| 100 | Latency::WS1 | ||
| 101 | } else { | ||
| 102 | Latency::WS2 | ||
| 103 | }); | ||
| 104 | }); | ||
| 105 | |||
| 106 | // Enable HSE | ||
| 107 | if config.hse.is_some() { | ||
| 108 | RCC.cr().write(|w| { | ||
| 109 | w.set_hsebyp(if config.bypass_hse { | ||
| 110 | Hsebyp::BYPASSED | ||
| 111 | } else { | ||
| 112 | Hsebyp::NOTBYPASSED | ||
| 113 | }); | ||
| 114 | // We turn on clock security to switch to HSI when HSE fails | ||
| 115 | w.set_csson(true); | ||
| 116 | w.set_hseon(true); | ||
| 117 | }); | ||
| 118 | while !RCC.cr().read().hserdy() {} | ||
| 119 | } | ||
| 120 | |||
| 121 | // Enable PLL | ||
| 122 | if let Some(ref pll_config) = pll_config { | ||
| 123 | RCC.cfgr().write(|w| { | ||
| 124 | w.set_pllmul(pll_config.pll_mul); | ||
| 125 | w.set_pllsrc(pll_config.pll_src); | ||
| 126 | }); | ||
| 127 | if let Some(pll_div) = pll_config.pll_div { | ||
| 128 | RCC.cfgr2().write(|w| w.set_prediv(pll_div)); | ||
| 129 | } | ||
| 130 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 131 | while !RCC.cr().read().pllrdy() {} | ||
| 132 | } | ||
| 133 | |||
| 134 | if config.pll48 { | ||
| 135 | let usb_pre = get_usb_pre(&config, sysclk, pclk1, &pll_config); | ||
| 136 | RCC.cfgr().write(|w| { | ||
| 137 | w.set_usbpre(usb_pre); | ||
| 138 | }); | ||
| 139 | } | ||
| 140 | |||
| 141 | // Set prescalers | ||
| 142 | RCC.cfgr().write(|w| { | ||
| 143 | w.set_ppre2(ppre2_bits); | ||
| 144 | w.set_ppre1(ppre1_bits); | ||
| 145 | w.set_hpre(hpre_bits); | ||
| 146 | }); | ||
| 147 | |||
| 148 | // Wait for the new prescalers to kick in | ||
| 149 | // "The clocks are divided with the new prescaler factor from | ||
| 150 | // 1 to 16 AHB cycles after write" | ||
| 151 | cortex_m::asm::delay(16); | ||
| 152 | |||
| 153 | RCC.cfgr().write(|w| { | ||
| 154 | w.set_sw(match (pll_config, config.hse) { | ||
| 155 | (Some(_), _) => Sw::PLL, | ||
| 156 | (None, Some(_)) => Sw::HSE, | ||
| 157 | (None, None) => Sw::HSI, | ||
| 158 | }) | ||
| 159 | }); | ||
| 160 | |||
| 161 | set_freqs(Clocks { | ||
| 162 | sys: Hertz(sysclk), | ||
| 163 | apb1: Hertz(pclk1), | ||
| 164 | apb2: Hertz(pclk2), | ||
| 165 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 166 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 167 | ahb: Hertz(hclk), | ||
| 168 | }); | ||
| 169 | } | ||
| 170 | |||
| 171 | #[inline] | ||
| 172 | fn get_sysclk(config: &Config) -> (Hertz, Option<PllConfig>) { | ||
| 173 | match (config.sysclk, config.hse) { | ||
| 174 | (Some(sysclk), Some(hse)) if sysclk == hse => (hse, None), | ||
| 175 | (Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None), | ||
| 176 | // If the user selected System clock is different from HSI or HSE | ||
| 177 | // we will have to setup PLL clock source | ||
| 178 | (Some(sysclk), _) => { | ||
| 179 | let (sysclk, pll_config) = calc_pll(config, sysclk); | ||
| 180 | (sysclk, Some(pll_config)) | ||
| 181 | } | ||
| 182 | (None, Some(hse)) => (hse, None), | ||
| 183 | (None, None) => (Hertz(HSI), None), | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | #[inline] | ||
| 188 | fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { | ||
| 189 | // Calculates the Multiplier and the Divisor to arrive at | ||
| 190 | // the required System clock from PLL source frequency | ||
| 191 | let get_mul_div = |sysclk, pllsrcclk| { | ||
| 192 | let common_div = gcd(sysclk, pllsrcclk); | ||
| 193 | let mut multiplier = sysclk / common_div; | ||
| 194 | let mut divisor = pllsrcclk / common_div; | ||
| 195 | // Minimum PLL multiplier is two | ||
| 196 | if multiplier == 1 { | ||
| 197 | multiplier *= 2; | ||
| 198 | divisor *= 2; | ||
| 199 | } | ||
| 200 | assert!(multiplier <= 16); | ||
| 201 | assert!(divisor <= 16); | ||
| 202 | (multiplier, divisor) | ||
| 203 | }; | ||
| 204 | // Based on the source of Pll, we calculate the actual system clock | ||
| 205 | // frequency, PLL's source identifier, multiplier and divisor | ||
| 206 | let (act_sysclk, pll_src, pll_mul, pll_div) = match config.hse { | ||
| 207 | Some(Hertz(hse)) => { | ||
| 208 | let (multiplier, divisor) = get_mul_div(sysclk, hse); | ||
| 209 | ( | ||
| 210 | Hertz((hse / divisor) * multiplier), | ||
| 211 | Pllsrc::HSE_DIV_PREDIV, | ||
| 212 | into_pll_mul(multiplier), | ||
| 213 | Some(into_pre_div(divisor)), | ||
| 214 | ) | ||
| 215 | } | ||
| 216 | None => { | ||
| 217 | cfg_if::cfg_if! { | ||
| 218 | // For some chips PREDIV is always two, and cannot be changed | ||
| 219 | if #[cfg(any( | ||
| 220 | feature="stm32f302xd", feature="stm32f302xe", feature="stm32f303xd", | ||
| 221 | feature="stm32f303xe", feature="stm32f398xe" | ||
| 222 | ))] { | ||
| 223 | let (multiplier, divisor) = get_mul_div(sysclk, HSI); | ||
| 224 | ( | ||
| 225 | Hertz((hse / divisor) * multiplier), | ||
| 226 | Pllsrc::HSI_DIV_PREDIV, | ||
| 227 | into_pll_mul(multiplier), | ||
| 228 | Some(into_pre_div(divisor)), | ||
| 229 | ) | ||
| 230 | } else { | ||
| 231 | let pllsrcclk = HSI / 2; | ||
| 232 | let multiplier = sysclk / pllsrcclk; | ||
| 233 | assert!(multiplier <= 16); | ||
| 234 | ( | ||
| 235 | Hertz(pllsrcclk * multiplier), | ||
| 236 | Pllsrc::HSI_DIV2, | ||
| 237 | into_pll_mul(multiplier), | ||
| 238 | None, | ||
| 239 | ) | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | ( | ||
| 245 | act_sysclk, | ||
| 246 | PllConfig { | ||
| 247 | pll_src, | ||
| 248 | pll_mul, | ||
| 249 | pll_div, | ||
| 250 | }, | ||
| 251 | ) | ||
| 252 | } | ||
| 253 | |||
| 254 | #[inline] | ||
| 255 | fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre { | ||
| 256 | cfg_if::cfg_if! { | ||
| 257 | // Some chips do not have USB | ||
| 258 | if #[cfg(any(stm32f301, stm32f318, stm32f334))] { | ||
| 259 | panic!("USB clock not supported by the chip"); | ||
| 260 | } else { | ||
| 261 | let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000); | ||
| 262 | match (usb_ok, sysclk) { | ||
| 263 | (true, 72_000_000) => Usbpre::DIV1_5, | ||
| 264 | (true, 48_000_000) => Usbpre::DIV1, | ||
| 265 | _ => panic!( | ||
| 266 | "USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz" | ||
| 267 | ), | ||
| 268 | } | ||
| 269 | } | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | // This function assumes cases when multiplier is one and it | ||
| 274 | // being greater than 16 is made impossible | ||
| 275 | #[inline] | ||
| 276 | fn into_pll_mul(multiplier: u32) -> Pllmul { | ||
| 277 | match multiplier { | ||
| 278 | 2 => Pllmul::MUL2, | ||
| 279 | 3 => Pllmul::MUL3, | ||
| 280 | 4 => Pllmul::MUL4, | ||
| 281 | 5 => Pllmul::MUL5, | ||
| 282 | 6 => Pllmul::MUL6, | ||
| 283 | 7 => Pllmul::MUL7, | ||
| 284 | 8 => Pllmul::MUL8, | ||
| 285 | 9 => Pllmul::MUL9, | ||
| 286 | 10 => Pllmul::MUL10, | ||
| 287 | 11 => Pllmul::MUL11, | ||
| 288 | 12 => Pllmul::MUL12, | ||
| 289 | 13 => Pllmul::MUL13, | ||
| 290 | 14 => Pllmul::MUL14, | ||
| 291 | 15 => Pllmul::MUL15, | ||
| 292 | 16 => Pllmul::MUL16, | ||
| 293 | _ => unreachable!(), | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | // This function assumes the incoming divisor cannot be greater | ||
| 298 | // than 16 | ||
| 299 | #[inline] | ||
| 300 | fn into_pre_div(divisor: u32) -> Prediv { | ||
| 301 | match divisor { | ||
| 302 | 1 => Prediv::DIV1, | ||
| 303 | 2 => Prediv::DIV2, | ||
| 304 | 3 => Prediv::DIV3, | ||
| 305 | 4 => Prediv::DIV4, | ||
| 306 | 5 => Prediv::DIV5, | ||
| 307 | 6 => Prediv::DIV6, | ||
| 308 | 7 => Prediv::DIV7, | ||
| 309 | 8 => Prediv::DIV8, | ||
| 310 | 9 => Prediv::DIV9, | ||
| 311 | 10 => Prediv::DIV10, | ||
| 312 | 11 => Prediv::DIV11, | ||
| 313 | 12 => Prediv::DIV12, | ||
| 314 | 13 => Prediv::DIV13, | ||
| 315 | 14 => Prediv::DIV14, | ||
| 316 | 15 => Prediv::DIV15, | ||
| 317 | 16 => Prediv::DIV16, | ||
| 318 | _ => unreachable!(), | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | // Determine GCD using Euclidean algorithm | ||
| 323 | #[inline] | ||
| 324 | fn gcd(mut a: u32, mut b: u32) -> u32 { | ||
| 325 | while b != 0 { | ||
| 326 | let r = a % b; | ||
| 327 | a = b; | ||
| 328 | b = r; | ||
| 329 | } | ||
| 330 | a | ||
| 331 | } | ||
diff --git a/embassy-stm32/src/rcc/f3/mod.rs b/embassy-stm32/src/rcc/f3/mod.rs deleted file mode 100644 index ab1bd7607..000000000 --- a/embassy-stm32/src/rcc/f3/mod.rs +++ /dev/null | |||
| @@ -1,374 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use embassy::util::Unborrow; | ||
| 3 | |||
| 4 | use crate::pac::{ | ||
| 5 | flash::vals::Latency, | ||
| 6 | rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}, | ||
| 7 | FLASH, RCC, | ||
| 8 | }; | ||
| 9 | use crate::peripherals; | ||
| 10 | use crate::rcc::{set_freqs, Clocks}; | ||
| 11 | use crate::time::Hertz; | ||
| 12 | |||
| 13 | const HSI: u32 = 8_000_000; | ||
| 14 | |||
| 15 | /// RCC peripheral | ||
| 16 | pub struct Rcc<'d> { | ||
| 17 | config: Config, | ||
| 18 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 19 | } | ||
| 20 | |||
| 21 | /// Clocks configutation | ||
| 22 | #[non_exhaustive] | ||
| 23 | #[derive(Default)] | ||
| 24 | pub struct Config { | ||
| 25 | /// Frequency of HSE oscillator | ||
| 26 | /// 4MHz to 32MHz | ||
| 27 | pub hse: Option<Hertz>, | ||
| 28 | /// Bypass HSE for an external clock | ||
| 29 | pub bypass_hse: bool, | ||
| 30 | /// Frequency of the System Clock | ||
| 31 | pub sysclk: Option<Hertz>, | ||
| 32 | /// Frequency of AHB bus | ||
| 33 | pub hclk: Option<Hertz>, | ||
| 34 | /// Frequency of APB1 bus | ||
| 35 | /// - Max frequency 36MHz | ||
| 36 | pub pclk1: Option<Hertz>, | ||
| 37 | /// Frequency of APB2 bus | ||
| 38 | /// - Max frequency with HSE is 72MHz | ||
| 39 | /// - Max frequency without HSE is 64MHz | ||
| 40 | pub pclk2: Option<Hertz>, | ||
| 41 | /// USB clock setup | ||
| 42 | /// It is valid only when, | ||
| 43 | /// - HSE is enabled, | ||
| 44 | /// - The System clock frequency is either 48MHz or 72MHz | ||
| 45 | /// - APB1 clock has a minimum frequency of 10MHz | ||
| 46 | pub pll48: bool, | ||
| 47 | } | ||
| 48 | |||
| 49 | // Information required to setup the PLL clock | ||
| 50 | struct PllConfig { | ||
| 51 | pll_src: Pllsrc, | ||
| 52 | pll_mul: Pllmul, | ||
| 53 | pll_div: Option<Prediv>, | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Initialize and Set the clock frequencies | ||
| 57 | pub unsafe fn init(config: Config) { | ||
| 58 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 59 | let clocks = Rcc::new(r, config).freeze(); | ||
| 60 | set_freqs(clocks); | ||
| 61 | } | ||
| 62 | |||
| 63 | impl<'d> Rcc<'d> { | ||
| 64 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 65 | Self { | ||
| 66 | config, | ||
| 67 | phantom: PhantomData, | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | fn freeze(self) -> Clocks { | ||
| 72 | // Calculate the real System clock, and PLL configuration if applicable | ||
| 73 | let (Hertz(sysclk), pll_config) = self.get_sysclk(); | ||
| 74 | assert!(sysclk <= 72_000_000); | ||
| 75 | |||
| 76 | // Calculate real AHB clock | ||
| 77 | let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 78 | let (hpre_bits, hpre_div) = match sysclk / hclk { | ||
| 79 | 0 => unreachable!(), | ||
| 80 | 1 => (Hpre::DIV1, 1), | ||
| 81 | 2 => (Hpre::DIV2, 2), | ||
| 82 | 3..=5 => (Hpre::DIV4, 4), | ||
| 83 | 6..=11 => (Hpre::DIV8, 8), | ||
| 84 | 12..=39 => (Hpre::DIV16, 16), | ||
| 85 | 40..=95 => (Hpre::DIV64, 64), | ||
| 86 | 96..=191 => (Hpre::DIV128, 128), | ||
| 87 | 192..=383 => (Hpre::DIV256, 256), | ||
| 88 | _ => (Hpre::DIV512, 512), | ||
| 89 | }; | ||
| 90 | let hclk = sysclk / hpre_div; | ||
| 91 | assert!(hclk <= 72_000_000); | ||
| 92 | |||
| 93 | // Calculate real APB1 clock | ||
| 94 | let pclk1 = self.config.pclk1.map(|p| p.0).unwrap_or(hclk); | ||
| 95 | let (ppre1_bits, ppre1) = match hclk / pclk1 { | ||
| 96 | 0 => unreachable!(), | ||
| 97 | 1 => (Ppre::DIV1, 1), | ||
| 98 | 2 => (Ppre::DIV2, 2), | ||
| 99 | 3..=5 => (Ppre::DIV4, 4), | ||
| 100 | 6..=11 => (Ppre::DIV8, 8), | ||
| 101 | _ => (Ppre::DIV16, 16), | ||
| 102 | }; | ||
| 103 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 104 | let pclk1 = hclk / ppre1; | ||
| 105 | assert!(pclk1 <= 36_000_000); | ||
| 106 | |||
| 107 | // Calculate real APB2 clock | ||
| 108 | let pclk2 = self.config.pclk2.map(|p| p.0).unwrap_or(hclk); | ||
| 109 | let (ppre2_bits, ppre2) = match hclk / pclk2 { | ||
| 110 | 0 => unreachable!(), | ||
| 111 | 1 => (Ppre::DIV1, 1), | ||
| 112 | 2 => (Ppre::DIV2, 2), | ||
| 113 | 3..=5 => (Ppre::DIV4, 4), | ||
| 114 | 6..=11 => (Ppre::DIV8, 8), | ||
| 115 | _ => (Ppre::DIV16, 16), | ||
| 116 | }; | ||
| 117 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 118 | let pclk2 = hclk / ppre2; | ||
| 119 | assert!(pclk2 <= 72_000_000); | ||
| 120 | |||
| 121 | // Set latency based on HCLK frquency | ||
| 122 | // NOTE(safety) Atomic write | ||
| 123 | unsafe { | ||
| 124 | FLASH.acr().write(|w| { | ||
| 125 | w.set_latency(if hclk <= 24_000_000 { | ||
| 126 | Latency::WS0 | ||
| 127 | } else if hclk <= 48_000_000 { | ||
| 128 | Latency::WS1 | ||
| 129 | } else { | ||
| 130 | Latency::WS2 | ||
| 131 | }); | ||
| 132 | }) | ||
| 133 | } | ||
| 134 | |||
| 135 | // Enable HSE | ||
| 136 | if self.config.hse.is_some() { | ||
| 137 | // NOTE(unsafe) We own the peripheral block | ||
| 138 | unsafe { | ||
| 139 | RCC.cr().write(|w| { | ||
| 140 | w.set_hsebyp(if self.config.bypass_hse { | ||
| 141 | Hsebyp::BYPASSED | ||
| 142 | } else { | ||
| 143 | Hsebyp::NOTBYPASSED | ||
| 144 | }); | ||
| 145 | // We turn on clock security to switch to HSI when HSE fails | ||
| 146 | w.set_csson(true); | ||
| 147 | w.set_hseon(true); | ||
| 148 | }); | ||
| 149 | while !RCC.cr().read().hserdy() {} | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | // Enable PLL | ||
| 154 | if let Some(ref pll_config) = pll_config { | ||
| 155 | // NOTE(unsafe) We own the peripheral block | ||
| 156 | unsafe { | ||
| 157 | RCC.cfgr().write(|w| { | ||
| 158 | w.set_pllmul(pll_config.pll_mul); | ||
| 159 | w.set_pllsrc(pll_config.pll_src); | ||
| 160 | }); | ||
| 161 | if let Some(pll_div) = pll_config.pll_div { | ||
| 162 | RCC.cfgr2().write(|w| w.set_prediv(pll_div)); | ||
| 163 | } | ||
| 164 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 165 | while !RCC.cr().read().pllrdy() {} | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | if self.config.pll48 { | ||
| 170 | let usb_pre = self.get_usb_pre(sysclk, pclk1, &pll_config); | ||
| 171 | // NOTE(unsafe) We own the peripheral block | ||
| 172 | unsafe { | ||
| 173 | RCC.cfgr().write(|w| { | ||
| 174 | w.set_usbpre(usb_pre); | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | // Set prescalers | ||
| 180 | unsafe { | ||
| 181 | // NOTE(unsafe) We own the peripheral block | ||
| 182 | RCC.cfgr().write(|w| { | ||
| 183 | w.set_ppre2(ppre2_bits); | ||
| 184 | w.set_ppre1(ppre1_bits); | ||
| 185 | w.set_hpre(hpre_bits); | ||
| 186 | }); | ||
| 187 | |||
| 188 | // Wait for the new prescalers to kick in | ||
| 189 | // "The clocks are divided with the new prescaler factor from | ||
| 190 | // 1 to 16 AHB cycles after write" | ||
| 191 | cortex_m::asm::delay(16); | ||
| 192 | |||
| 193 | // NOTE(unsafe) We own the peripheral block | ||
| 194 | RCC.cfgr().write(|w| { | ||
| 195 | w.set_sw(match (pll_config, self.config.hse) { | ||
| 196 | (Some(_), _) => Sw::PLL, | ||
| 197 | (None, Some(_)) => Sw::HSE, | ||
| 198 | (None, None) => Sw::HSI, | ||
| 199 | }) | ||
| 200 | }); | ||
| 201 | } | ||
| 202 | |||
| 203 | Clocks { | ||
| 204 | sys: Hertz(sysclk), | ||
| 205 | apb1: Hertz(pclk1), | ||
| 206 | apb2: Hertz(pclk2), | ||
| 207 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 208 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 209 | ahb: Hertz(hclk), | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | #[inline] | ||
| 214 | fn get_sysclk(&self) -> (Hertz, Option<PllConfig>) { | ||
| 215 | match (self.config.sysclk, self.config.hse) { | ||
| 216 | (Some(sysclk), Some(hse)) if sysclk == hse => (hse, None), | ||
| 217 | (Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None), | ||
| 218 | // If the user selected System clock is different from HSI or HSE | ||
| 219 | // we will have to setup PLL clock source | ||
| 220 | (Some(sysclk), _) => { | ||
| 221 | let (sysclk, pll_config) = self.calc_pll(sysclk); | ||
| 222 | (sysclk, Some(pll_config)) | ||
| 223 | } | ||
| 224 | (None, Some(hse)) => (hse, None), | ||
| 225 | (None, None) => (Hertz(HSI), None), | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | #[inline] | ||
| 230 | fn calc_pll(&self, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { | ||
| 231 | // Calculates the Multiplier and the Divisor to arrive at | ||
| 232 | // the required System clock from PLL source frequency | ||
| 233 | let get_mul_div = |sysclk, pllsrcclk| { | ||
| 234 | let common_div = gcd(sysclk, pllsrcclk); | ||
| 235 | let mut multiplier = sysclk / common_div; | ||
| 236 | let mut divisor = pllsrcclk / common_div; | ||
| 237 | // Minimum PLL multiplier is two | ||
| 238 | if multiplier == 1 { | ||
| 239 | multiplier *= 2; | ||
| 240 | divisor *= 2; | ||
| 241 | } | ||
| 242 | assert!(multiplier <= 16); | ||
| 243 | assert!(divisor <= 16); | ||
| 244 | (multiplier, divisor) | ||
| 245 | }; | ||
| 246 | // Based on the source of Pll, we calculate the actual system clock | ||
| 247 | // frequency, PLL's source identifier, multiplier and divisor | ||
| 248 | let (act_sysclk, pll_src, pll_mul, pll_div) = match self.config.hse { | ||
| 249 | Some(Hertz(hse)) => { | ||
| 250 | let (multiplier, divisor) = get_mul_div(sysclk, hse); | ||
| 251 | ( | ||
| 252 | Hertz((hse / divisor) * multiplier), | ||
| 253 | Pllsrc::HSE_DIV_PREDIV, | ||
| 254 | into_pll_mul(multiplier), | ||
| 255 | Some(into_pre_div(divisor)), | ||
| 256 | ) | ||
| 257 | } | ||
| 258 | None => { | ||
| 259 | cfg_if::cfg_if! { | ||
| 260 | // For some chips PREDIV is always two, and cannot be changed | ||
| 261 | if #[cfg(any( | ||
| 262 | feature="stm32f302xd", feature="stm32f302xe", feature="stm32f303xd", | ||
| 263 | feature="stm32f303xe", feature="stm32f398xe" | ||
| 264 | ))] { | ||
| 265 | let (multiplier, divisor) = get_mul_div(sysclk, HSI); | ||
| 266 | ( | ||
| 267 | Hertz((hse / divisor) * multiplier), | ||
| 268 | Pllsrc::HSI_DIV_PREDIV, | ||
| 269 | into_pll_mul(multiplier), | ||
| 270 | Some(into_pre_div(divisor)), | ||
| 271 | ) | ||
| 272 | } else { | ||
| 273 | let pllsrcclk = HSI / 2; | ||
| 274 | let multiplier = sysclk / pllsrcclk; | ||
| 275 | assert!(multiplier <= 16); | ||
| 276 | ( | ||
| 277 | Hertz(pllsrcclk * multiplier), | ||
| 278 | Pllsrc::HSI_DIV2, | ||
| 279 | into_pll_mul(multiplier), | ||
| 280 | None, | ||
| 281 | ) | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
| 285 | }; | ||
| 286 | ( | ||
| 287 | act_sysclk, | ||
| 288 | PllConfig { | ||
| 289 | pll_src, | ||
| 290 | pll_mul, | ||
| 291 | pll_div, | ||
| 292 | }, | ||
| 293 | ) | ||
| 294 | } | ||
| 295 | |||
| 296 | #[inline] | ||
| 297 | fn get_usb_pre(&self, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre { | ||
| 298 | cfg_if::cfg_if! { | ||
| 299 | // Some chips do not have USB | ||
| 300 | if #[cfg(any(stm32f301, stm32f318, stm32f334))] { | ||
| 301 | panic!("USB clock not supported by the chip"); | ||
| 302 | } else { | ||
| 303 | let usb_ok = self.config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000); | ||
| 304 | match (usb_ok, sysclk) { | ||
| 305 | (true, 72_000_000) => Usbpre::DIV1_5, | ||
| 306 | (true, 48_000_000) => Usbpre::DIV1, | ||
| 307 | _ => panic!( | ||
| 308 | "USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz" | ||
| 309 | ), | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | // This function assumes cases when multiplier is one and it | ||
| 317 | // being greater than 16 is made impossible | ||
| 318 | #[inline] | ||
| 319 | fn into_pll_mul(multiplier: u32) -> Pllmul { | ||
| 320 | match multiplier { | ||
| 321 | 2 => Pllmul::MUL2, | ||
| 322 | 3 => Pllmul::MUL3, | ||
| 323 | 4 => Pllmul::MUL4, | ||
| 324 | 5 => Pllmul::MUL5, | ||
| 325 | 6 => Pllmul::MUL6, | ||
| 326 | 7 => Pllmul::MUL7, | ||
| 327 | 8 => Pllmul::MUL8, | ||
| 328 | 9 => Pllmul::MUL9, | ||
| 329 | 10 => Pllmul::MUL10, | ||
| 330 | 11 => Pllmul::MUL11, | ||
| 331 | 12 => Pllmul::MUL12, | ||
| 332 | 13 => Pllmul::MUL13, | ||
| 333 | 14 => Pllmul::MUL14, | ||
| 334 | 15 => Pllmul::MUL15, | ||
| 335 | 16 => Pllmul::MUL16, | ||
| 336 | _ => unreachable!(), | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | // This function assumes the incoming divisor cannot be greater | ||
| 341 | // than 16 | ||
| 342 | #[inline] | ||
| 343 | fn into_pre_div(divisor: u32) -> Prediv { | ||
| 344 | match divisor { | ||
| 345 | 1 => Prediv::DIV1, | ||
| 346 | 2 => Prediv::DIV2, | ||
| 347 | 3 => Prediv::DIV3, | ||
| 348 | 4 => Prediv::DIV4, | ||
| 349 | 5 => Prediv::DIV5, | ||
| 350 | 6 => Prediv::DIV6, | ||
| 351 | 7 => Prediv::DIV7, | ||
| 352 | 8 => Prediv::DIV8, | ||
| 353 | 9 => Prediv::DIV9, | ||
| 354 | 10 => Prediv::DIV10, | ||
| 355 | 11 => Prediv::DIV11, | ||
| 356 | 12 => Prediv::DIV12, | ||
| 357 | 13 => Prediv::DIV13, | ||
| 358 | 14 => Prediv::DIV14, | ||
| 359 | 15 => Prediv::DIV15, | ||
| 360 | 16 => Prediv::DIV16, | ||
| 361 | _ => unreachable!(), | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | // Determine GCD using Euclidean algorithm | ||
| 366 | #[inline] | ||
| 367 | fn gcd(mut a: u32, mut b: u32) -> u32 { | ||
| 368 | while b != 0 { | ||
| 369 | let r = a % b; | ||
| 370 | a = b; | ||
| 371 | b = r; | ||
| 372 | } | ||
| 373 | a | ||
| 374 | } | ||
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs new file mode 100644 index 000000000..aba8fc0ef --- /dev/null +++ b/embassy-stm32/src/rcc/f4.rs | |||
| @@ -0,0 +1,288 @@ | |||
| 1 | use super::sealed::RccPeripheral; | ||
| 2 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw}; | ||
| 3 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 4 | use crate::rcc::{set_freqs, Clocks}; | ||
| 5 | use crate::time::Hertz; | ||
| 6 | |||
| 7 | const HSI: u32 = 16_000_000; | ||
| 8 | |||
| 9 | /// Clocks configuration | ||
| 10 | #[non_exhaustive] | ||
| 11 | #[derive(Default)] | ||
| 12 | pub struct Config { | ||
| 13 | pub hse: Option<Hertz>, | ||
| 14 | pub bypass_hse: bool, | ||
| 15 | pub hclk: Option<Hertz>, | ||
| 16 | pub sys_ck: Option<Hertz>, | ||
| 17 | pub pclk1: Option<Hertz>, | ||
| 18 | pub pclk2: Option<Hertz>, | ||
| 19 | |||
| 20 | pub pll48: bool, | ||
| 21 | } | ||
| 22 | |||
| 23 | unsafe fn setup_pll( | ||
| 24 | pllsrcclk: u32, | ||
| 25 | use_hse: bool, | ||
| 26 | pllsysclk: Option<u32>, | ||
| 27 | pll48clk: bool, | ||
| 28 | ) -> PllResults { | ||
| 29 | use crate::pac::rcc::vals::{Pllp, Pllsrc}; | ||
| 30 | |||
| 31 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | ||
| 32 | if pllsysclk.is_none() && !pll48clk { | ||
| 33 | RCC.pllcfgr() | ||
| 34 | .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); | ||
| 35 | |||
| 36 | return PllResults { | ||
| 37 | use_pll: false, | ||
| 38 | pllsysclk: None, | ||
| 39 | pll48clk: None, | ||
| 40 | }; | ||
| 41 | } | ||
| 42 | // Input divisor from PLL source clock, must result to frequency in | ||
| 43 | // the range from 1 to 2 MHz | ||
| 44 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 45 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 46 | |||
| 47 | // Sysclk output divisor must be one of 2, 4, 6 or 8 | ||
| 48 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); | ||
| 49 | |||
| 50 | let target_freq = if pll48clk { | ||
| 51 | 48_000_000 | ||
| 52 | } else { | ||
| 53 | sysclk * sysclk_div | ||
| 54 | }; | ||
| 55 | |||
| 56 | // Find the lowest pllm value that minimize the difference between | ||
| 57 | // target frequency and the real vco_out frequency. | ||
| 58 | let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { | ||
| 59 | let vco_in = pllsrcclk / pllm; | ||
| 60 | let plln = target_freq / vco_in; | ||
| 61 | target_freq - vco_in * plln | ||
| 62 | })); | ||
| 63 | |||
| 64 | let vco_in = pllsrcclk / pllm; | ||
| 65 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 66 | |||
| 67 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 68 | // and <= 432MHz, min 50, max 432 | ||
| 69 | let plln = if pll48clk { | ||
| 70 | // try the different valid pllq according to the valid | ||
| 71 | // main scaller values, and take the best | ||
| 72 | let pllq = unwrap!((4..=9).min_by_key(|pllq| { | ||
| 73 | let plln = 48_000_000 * pllq / vco_in; | ||
| 74 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 75 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 76 | (pll48_diff, sysclk_diff) | ||
| 77 | })); | ||
| 78 | 48_000_000 * pllq / vco_in | ||
| 79 | } else { | ||
| 80 | sysclk * sysclk_div / vco_in | ||
| 81 | }; | ||
| 82 | |||
| 83 | let pllp = (sysclk_div / 2) - 1; | ||
| 84 | |||
| 85 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; | ||
| 86 | let real_pll48clk = vco_in * plln / pllq; | ||
| 87 | |||
| 88 | RCC.pllcfgr().modify(|w| { | ||
| 89 | w.set_pllm(pllm as u8); | ||
| 90 | w.set_plln(plln as u16); | ||
| 91 | w.set_pllp(Pllp(pllp as u8)); | ||
| 92 | w.set_pllq(pllq as u8); | ||
| 93 | w.set_pllsrc(Pllsrc(use_hse as u8)); | ||
| 94 | }); | ||
| 95 | |||
| 96 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 97 | |||
| 98 | PllResults { | ||
| 99 | use_pll: true, | ||
| 100 | pllsysclk: Some(real_pllsysclk), | ||
| 101 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | unsafe fn flash_setup(sysclk: u32) { | ||
| 106 | use crate::pac::flash::vals::Latency; | ||
| 107 | |||
| 108 | // Be conservative with voltage ranges | ||
| 109 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 110 | |||
| 111 | critical_section::with(|_| { | ||
| 112 | FLASH | ||
| 113 | .acr() | ||
| 114 | .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 115 | }); | ||
| 116 | } | ||
| 117 | |||
| 118 | pub(crate) unsafe fn init(config: Config) { | ||
| 119 | crate::peripherals::PWR::enable(); | ||
| 120 | |||
| 121 | let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI); | ||
| 122 | let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | ||
| 123 | let sysclk_on_pll = sysclk != pllsrcclk; | ||
| 124 | |||
| 125 | let plls = setup_pll( | ||
| 126 | pllsrcclk, | ||
| 127 | config.hse.is_some(), | ||
| 128 | if sysclk_on_pll { Some(sysclk) } else { None }, | ||
| 129 | config.pll48, | ||
| 130 | ); | ||
| 131 | |||
| 132 | if config.pll48 { | ||
| 133 | let freq = unwrap!(plls.pll48clk); | ||
| 134 | |||
| 135 | assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32); | ||
| 136 | } | ||
| 137 | |||
| 138 | let sysclk = if sysclk_on_pll { | ||
| 139 | unwrap!(plls.pllsysclk) | ||
| 140 | } else { | ||
| 141 | sysclk | ||
| 142 | }; | ||
| 143 | |||
| 144 | // AHB prescaler | ||
| 145 | let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 146 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { | ||
| 147 | 0 => unreachable!(), | ||
| 148 | 1 => (Hpre::DIV1, 1), | ||
| 149 | 2 => (Hpre::DIV2, 2), | ||
| 150 | 3..=5 => (Hpre::DIV4, 4), | ||
| 151 | 6..=11 => (Hpre::DIV8, 8), | ||
| 152 | 12..=39 => (Hpre::DIV16, 16), | ||
| 153 | 40..=95 => (Hpre::DIV64, 64), | ||
| 154 | 96..=191 => (Hpre::DIV128, 128), | ||
| 155 | 192..=383 => (Hpre::DIV256, 256), | ||
| 156 | _ => (Hpre::DIV512, 512), | ||
| 157 | }; | ||
| 158 | |||
| 159 | // Calculate real AHB clock | ||
| 160 | let hclk = sysclk / hpre_div; | ||
| 161 | |||
| 162 | let pclk1 = config | ||
| 163 | .pclk1 | ||
| 164 | .map(|p| p.0) | ||
| 165 | .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); | ||
| 166 | |||
| 167 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | ||
| 168 | 0 => unreachable!(), | ||
| 169 | 1 => (0b000, 1), | ||
| 170 | 2 => (0b100, 2), | ||
| 171 | 3..=5 => (0b101, 4), | ||
| 172 | 6..=11 => (0b110, 8), | ||
| 173 | _ => (0b111, 16), | ||
| 174 | }; | ||
| 175 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 176 | |||
| 177 | // Calculate real APB1 clock | ||
| 178 | let pclk1 = hclk / ppre1; | ||
| 179 | assert!(pclk1 <= max::PCLK1_MAX); | ||
| 180 | |||
| 181 | let pclk2 = config | ||
| 182 | .pclk2 | ||
| 183 | .map(|p| p.0) | ||
| 184 | .unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk)); | ||
| 185 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { | ||
| 186 | 0 => unreachable!(), | ||
| 187 | 1 => (0b000, 1), | ||
| 188 | 2 => (0b100, 2), | ||
| 189 | 3..=5 => (0b101, 4), | ||
| 190 | 6..=11 => (0b110, 8), | ||
| 191 | _ => (0b111, 16), | ||
| 192 | }; | ||
| 193 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 194 | |||
| 195 | // Calculate real APB2 clock | ||
| 196 | let pclk2 = hclk / ppre2; | ||
| 197 | assert!(pclk2 <= max::PCLK2_MAX); | ||
| 198 | |||
| 199 | flash_setup(sysclk); | ||
| 200 | |||
| 201 | if config.hse.is_some() { | ||
| 202 | RCC.cr().modify(|w| { | ||
| 203 | w.set_hsebyp(Hsebyp(config.bypass_hse as u8)); | ||
| 204 | w.set_hseon(true); | ||
| 205 | }); | ||
| 206 | while !RCC.cr().read().hserdy() {} | ||
| 207 | } | ||
| 208 | |||
| 209 | if plls.use_pll { | ||
| 210 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 211 | |||
| 212 | if hclk > max::HCLK_OVERDRIVE_FREQUENCY { | ||
| 213 | PWR.cr1().modify(|w| w.set_oden(true)); | ||
| 214 | while !PWR.csr1().read().odrdy() {} | ||
| 215 | |||
| 216 | PWR.cr1().modify(|w| w.set_odswen(true)); | ||
| 217 | while !PWR.csr1().read().odswrdy() {} | ||
| 218 | } | ||
| 219 | |||
| 220 | while !RCC.cr().read().pllrdy() {} | ||
| 221 | } | ||
| 222 | |||
| 223 | RCC.cfgr().modify(|w| { | ||
| 224 | w.set_ppre2(Ppre(ppre2_bits)); | ||
| 225 | w.set_ppre1(Ppre(ppre1_bits)); | ||
| 226 | w.set_hpre(hpre_bits); | ||
| 227 | }); | ||
| 228 | |||
| 229 | // Wait for the new prescalers to kick in | ||
| 230 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | ||
| 231 | cortex_m::asm::delay(16); | ||
| 232 | |||
| 233 | RCC.cfgr().modify(|w| { | ||
| 234 | w.set_sw(if sysclk_on_pll { | ||
| 235 | Sw::PLL | ||
| 236 | } else if config.hse.is_some() { | ||
| 237 | Sw::HSE | ||
| 238 | } else { | ||
| 239 | Sw::HSI | ||
| 240 | }) | ||
| 241 | }); | ||
| 242 | |||
| 243 | set_freqs(Clocks { | ||
| 244 | sys: Hertz(sysclk), | ||
| 245 | apb1: Hertz(pclk1), | ||
| 246 | apb2: Hertz(pclk2), | ||
| 247 | |||
| 248 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 249 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 250 | |||
| 251 | ahb1: Hertz(hclk), | ||
| 252 | ahb2: Hertz(hclk), | ||
| 253 | ahb3: Hertz(hclk), | ||
| 254 | |||
| 255 | pll48: plls.pll48clk.map(Hertz), | ||
| 256 | }); | ||
| 257 | } | ||
| 258 | |||
| 259 | struct PllResults { | ||
| 260 | use_pll: bool, | ||
| 261 | pllsysclk: Option<u32>, | ||
| 262 | pll48clk: Option<u32>, | ||
| 263 | } | ||
| 264 | |||
| 265 | mod max { | ||
| 266 | #[cfg(stm32f401)] | ||
| 267 | pub(crate) const SYSCLK_MAX: u32 = 84_000_000; | ||
| 268 | #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))] | ||
| 269 | pub(crate) const SYSCLK_MAX: u32 = 168_000_000; | ||
| 270 | #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 271 | pub(crate) const SYSCLK_MAX: u32 = 100_000_000; | ||
| 272 | #[cfg(any( | ||
| 273 | stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479, | ||
| 274 | ))] | ||
| 275 | pub(crate) const SYSCLK_MAX: u32 = 180_000_000; | ||
| 276 | |||
| 277 | pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 168_000_000; | ||
| 278 | |||
| 279 | pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2; | ||
| 280 | |||
| 281 | #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 282 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX; | ||
| 283 | #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))] | ||
| 284 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | ||
| 285 | |||
| 286 | pub(crate) const PLL_48_CLK: u32 = 48_000_000; | ||
| 287 | pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; | ||
| 288 | } | ||
diff --git a/embassy-stm32/src/rcc/f4/max.rs b/embassy-stm32/src/rcc/f4/max.rs deleted file mode 100644 index dd8de3da9..000000000 --- a/embassy-stm32/src/rcc/f4/max.rs +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | #[cfg(stm32f401)] | ||
| 2 | pub(crate) const SYSCLK_MAX: u32 = 84_000_000; | ||
| 3 | |||
| 4 | #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))] | ||
| 5 | pub(crate) const SYSCLK_MAX: u32 = 168_000_000; | ||
| 6 | |||
| 7 | #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 8 | pub(crate) const SYSCLK_MAX: u32 = 100_000_000; | ||
| 9 | |||
| 10 | #[cfg(any( | ||
| 11 | stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479, | ||
| 12 | ))] | ||
| 13 | pub(crate) const SYSCLK_MAX: u32 = 180_000_000; | ||
| 14 | |||
| 15 | #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] | ||
| 16 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX; | ||
| 17 | |||
| 18 | #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))] | ||
| 19 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | ||
| 20 | |||
| 21 | pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2; | ||
diff --git a/embassy-stm32/src/rcc/f4/mod.rs b/embassy-stm32/src/rcc/f4/mod.rs deleted file mode 100644 index 04083dfa1..000000000 --- a/embassy-stm32/src/rcc/f4/mod.rs +++ /dev/null | |||
| @@ -1,311 +0,0 @@ | |||
| 1 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 2 | use crate::peripherals; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use core::marker::PhantomData; | ||
| 6 | use embassy::util::Unborrow; | ||
| 7 | |||
| 8 | mod max; | ||
| 9 | use max::{PCLK1_MAX, PCLK2_MAX}; | ||
| 10 | |||
| 11 | const HSI: u32 = 16_000_000; | ||
| 12 | |||
| 13 | /// Clocks configutation | ||
| 14 | #[non_exhaustive] | ||
| 15 | #[derive(Default)] | ||
| 16 | pub struct Config { | ||
| 17 | pub hse: Option<Hertz>, | ||
| 18 | pub bypass_hse: bool, | ||
| 19 | pub pll48: bool, | ||
| 20 | pub sys_ck: Option<Hertz>, | ||
| 21 | pub hclk: Option<Hertz>, | ||
| 22 | pub pclk1: Option<Hertz>, | ||
| 23 | pub pclk2: Option<Hertz>, | ||
| 24 | } | ||
| 25 | |||
| 26 | /// RCC peripheral | ||
| 27 | pub struct Rcc<'d> { | ||
| 28 | config: Config, | ||
| 29 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'d> Rcc<'d> { | ||
| 33 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 34 | Self { | ||
| 35 | config, | ||
| 36 | phantom: PhantomData, | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | fn freeze(mut self) -> Clocks { | ||
| 41 | use super::sealed::RccPeripheral; | ||
| 42 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw}; | ||
| 43 | |||
| 44 | let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI); | ||
| 45 | let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | ||
| 46 | let sysclk_on_pll = sysclk != pllsrcclk; | ||
| 47 | |||
| 48 | let plls = self.setup_pll( | ||
| 49 | pllsrcclk, | ||
| 50 | self.config.hse.is_some(), | ||
| 51 | if sysclk_on_pll { Some(sysclk) } else { None }, | ||
| 52 | self.config.pll48, | ||
| 53 | ); | ||
| 54 | |||
| 55 | if self.config.pll48 { | ||
| 56 | assert!( | ||
| 57 | // USB specification allows +-0.25% | ||
| 58 | plls.pll48clk | ||
| 59 | .map(|freq| (48_000_000 - freq as i32).abs() <= 120_000) | ||
| 60 | .unwrap_or(false) | ||
| 61 | ); | ||
| 62 | } | ||
| 63 | |||
| 64 | let sysclk = if sysclk_on_pll { | ||
| 65 | unwrap!(plls.pllsysclk) | ||
| 66 | } else { | ||
| 67 | sysclk | ||
| 68 | }; | ||
| 69 | |||
| 70 | let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 71 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { | ||
| 72 | 0 => unreachable!(), | ||
| 73 | 1 => (Hpre::DIV1, 1), | ||
| 74 | 2 => (Hpre::DIV2, 2), | ||
| 75 | 3..=5 => (Hpre::DIV4, 4), | ||
| 76 | 6..=11 => (Hpre::DIV8, 8), | ||
| 77 | 12..=39 => (Hpre::DIV16, 16), | ||
| 78 | 40..=95 => (Hpre::DIV64, 64), | ||
| 79 | 96..=191 => (Hpre::DIV128, 128), | ||
| 80 | 192..=383 => (Hpre::DIV256, 256), | ||
| 81 | _ => (Hpre::DIV512, 512), | ||
| 82 | }; | ||
| 83 | |||
| 84 | // Calculate real AHB clock | ||
| 85 | let hclk = sysclk / hpre_div; | ||
| 86 | |||
| 87 | let pclk1 = self | ||
| 88 | .config | ||
| 89 | .pclk1 | ||
| 90 | .map(|p| p.0) | ||
| 91 | .unwrap_or_else(|| core::cmp::min(PCLK1_MAX, hclk)); | ||
| 92 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | ||
| 93 | 0 => unreachable!(), | ||
| 94 | 1 => (0b000, 1), | ||
| 95 | 2 => (0b100, 2), | ||
| 96 | 3..=5 => (0b101, 4), | ||
| 97 | 6..=11 => (0b110, 8), | ||
| 98 | _ => (0b111, 16), | ||
| 99 | }; | ||
| 100 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 101 | |||
| 102 | // Calculate real APB1 clock | ||
| 103 | let pclk1 = hclk / ppre1; | ||
| 104 | assert!(pclk1 <= PCLK1_MAX); | ||
| 105 | |||
| 106 | let pclk2 = self | ||
| 107 | .config | ||
| 108 | .pclk2 | ||
| 109 | .map(|p| p.0) | ||
| 110 | .unwrap_or_else(|| core::cmp::min(PCLK2_MAX, hclk)); | ||
| 111 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { | ||
| 112 | 0 => unreachable!(), | ||
| 113 | 1 => (0b000, 1), | ||
| 114 | 2 => (0b100, 2), | ||
| 115 | 3..=5 => (0b101, 4), | ||
| 116 | 6..=11 => (0b110, 8), | ||
| 117 | _ => (0b111, 16), | ||
| 118 | }; | ||
| 119 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 120 | |||
| 121 | // Calculate real APB2 clock | ||
| 122 | let pclk2 = hclk / ppre2; | ||
| 123 | assert!(pclk2 <= PCLK2_MAX); | ||
| 124 | |||
| 125 | Self::flash_setup(sysclk); | ||
| 126 | |||
| 127 | if self.config.hse.is_some() { | ||
| 128 | // NOTE(unsafe) We own the peripheral block | ||
| 129 | unsafe { | ||
| 130 | RCC.cr().modify(|w| { | ||
| 131 | w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8)); | ||
| 132 | w.set_hseon(true); | ||
| 133 | }); | ||
| 134 | while !RCC.cr().read().hserdy() {} | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | if plls.use_pll { | ||
| 139 | unsafe { | ||
| 140 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 141 | |||
| 142 | if hclk > 168_000_000 { | ||
| 143 | peripherals::PWR::enable(); | ||
| 144 | |||
| 145 | PWR.cr().modify(|w| w.set_oden(true)); | ||
| 146 | while !PWR.csr().read().odrdy() {} | ||
| 147 | |||
| 148 | PWR.cr().modify(|w| w.set_odswen(true)); | ||
| 149 | while !PWR.csr().read().odswrdy() {} | ||
| 150 | } | ||
| 151 | |||
| 152 | while !RCC.cr().read().pllrdy() {} | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | unsafe { | ||
| 157 | RCC.cfgr().modify(|w| { | ||
| 158 | w.set_ppre2(Ppre(ppre2_bits)); | ||
| 159 | w.set_ppre1(Ppre(ppre1_bits)); | ||
| 160 | w.set_hpre(hpre_bits); | ||
| 161 | }); | ||
| 162 | |||
| 163 | // Wait for the new prescalers to kick in | ||
| 164 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | ||
| 165 | cortex_m::asm::delay(16); | ||
| 166 | |||
| 167 | RCC.cfgr().modify(|w| { | ||
| 168 | w.set_sw(if sysclk_on_pll { | ||
| 169 | Sw::PLL | ||
| 170 | } else if self.config.hse.is_some() { | ||
| 171 | Sw::HSE | ||
| 172 | } else { | ||
| 173 | Sw::HSI | ||
| 174 | }) | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | |||
| 178 | Clocks { | ||
| 179 | sys: Hertz(sysclk), | ||
| 180 | apb1: Hertz(pclk1), | ||
| 181 | apb2: Hertz(pclk2), | ||
| 182 | |||
| 183 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 184 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 185 | |||
| 186 | ahb1: Hertz(hclk), | ||
| 187 | ahb2: Hertz(hclk), | ||
| 188 | ahb3: Hertz(hclk), | ||
| 189 | |||
| 190 | pll48: plls.pll48clk.map(Hertz), | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | // Safety: RCC init must have been called | ||
| 195 | pub fn clocks(&self) -> &'static Clocks { | ||
| 196 | unsafe { get_freqs() } | ||
| 197 | } | ||
| 198 | |||
| 199 | fn setup_pll( | ||
| 200 | &mut self, | ||
| 201 | pllsrcclk: u32, | ||
| 202 | use_hse: bool, | ||
| 203 | pllsysclk: Option<u32>, | ||
| 204 | pll48clk: bool, | ||
| 205 | ) -> PllResults { | ||
| 206 | use crate::pac::rcc::vals::{Pllp, Pllsrc}; | ||
| 207 | |||
| 208 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | ||
| 209 | if pllsysclk.is_none() && !pll48clk { | ||
| 210 | // NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock | ||
| 211 | unsafe { | ||
| 212 | RCC.pllcfgr() | ||
| 213 | .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); | ||
| 214 | } | ||
| 215 | |||
| 216 | return PllResults { | ||
| 217 | use_pll: false, | ||
| 218 | pllsysclk: None, | ||
| 219 | pll48clk: None, | ||
| 220 | }; | ||
| 221 | } | ||
| 222 | // Input divisor from PLL source clock, must result to frequency in | ||
| 223 | // the range from 1 to 2 MHz | ||
| 224 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 225 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 226 | |||
| 227 | // Sysclk output divisor must be one of 2, 4, 6 or 8 | ||
| 228 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); | ||
| 229 | |||
| 230 | let target_freq = if pll48clk { | ||
| 231 | 48_000_000 | ||
| 232 | } else { | ||
| 233 | sysclk * sysclk_div | ||
| 234 | }; | ||
| 235 | |||
| 236 | // Find the lowest pllm value that minimize the difference between | ||
| 237 | // target frequency and the real vco_out frequency. | ||
| 238 | let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { | ||
| 239 | let vco_in = pllsrcclk / pllm; | ||
| 240 | let plln = target_freq / vco_in; | ||
| 241 | target_freq - vco_in * plln | ||
| 242 | })); | ||
| 243 | |||
| 244 | let vco_in = pllsrcclk / pllm; | ||
| 245 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 246 | |||
| 247 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 248 | // and <= 432MHz, min 50, max 432 | ||
| 249 | let plln = if pll48clk { | ||
| 250 | // try the different valid pllq according to the valid | ||
| 251 | // main scaller values, and take the best | ||
| 252 | let pllq = unwrap!((4..=9).min_by_key(|pllq| { | ||
| 253 | let plln = 48_000_000 * pllq / vco_in; | ||
| 254 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 255 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 256 | (pll48_diff, sysclk_diff) | ||
| 257 | })); | ||
| 258 | 48_000_000 * pllq / vco_in | ||
| 259 | } else { | ||
| 260 | sysclk * sysclk_div / vco_in | ||
| 261 | }; | ||
| 262 | |||
| 263 | let pllp = (sysclk_div / 2) - 1; | ||
| 264 | |||
| 265 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; | ||
| 266 | let real_pll48clk = vco_in * plln / pllq; | ||
| 267 | |||
| 268 | unsafe { | ||
| 269 | RCC.pllcfgr().modify(|w| { | ||
| 270 | w.set_pllm(pllm as u8); | ||
| 271 | w.set_plln(plln as u16); | ||
| 272 | w.set_pllp(Pllp(pllp as u8)); | ||
| 273 | w.set_pllq(pllq as u8); | ||
| 274 | w.set_pllsrc(Pllsrc(use_hse as u8)); | ||
| 275 | }); | ||
| 276 | } | ||
| 277 | |||
| 278 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 279 | |||
| 280 | PllResults { | ||
| 281 | use_pll: true, | ||
| 282 | pllsysclk: Some(real_pllsysclk), | ||
| 283 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | fn flash_setup(sysclk: u32) { | ||
| 288 | use crate::pac::flash::vals::Latency; | ||
| 289 | |||
| 290 | // Be conservative with voltage ranges | ||
| 291 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 292 | |||
| 293 | critical_section::with(|_| unsafe { | ||
| 294 | FLASH | ||
| 295 | .acr() | ||
| 296 | .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 297 | }); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | pub unsafe fn init(config: Config) { | ||
| 302 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 303 | let clocks = Rcc::new(r, config).freeze(); | ||
| 304 | set_freqs(clocks); | ||
| 305 | } | ||
| 306 | |||
| 307 | struct PllResults { | ||
| 308 | use_pll: bool, | ||
| 309 | pllsysclk: Option<u32>, | ||
| 310 | pll48clk: Option<u32>, | ||
| 311 | } | ||
diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs new file mode 100644 index 000000000..1a0530296 --- /dev/null +++ b/embassy-stm32/src/rcc/f7.rs | |||
| @@ -0,0 +1,321 @@ | |||
| 1 | use super::sealed::RccPeripheral; | ||
| 2 | use crate::pac::pwr::vals::Vos; | ||
| 3 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw}; | ||
| 4 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 5 | use crate::rcc::{set_freqs, Clocks}; | ||
| 6 | use crate::time::Hertz; | ||
| 7 | |||
| 8 | const HSI: u32 = 16_000_000; | ||
| 9 | |||
| 10 | /// Clocks configuration | ||
| 11 | #[non_exhaustive] | ||
| 12 | #[derive(Default)] | ||
| 13 | pub struct Config { | ||
| 14 | pub hse: Option<Hertz>, | ||
| 15 | pub bypass_hse: bool, | ||
| 16 | pub hclk: Option<Hertz>, | ||
| 17 | pub sys_ck: Option<Hertz>, | ||
| 18 | pub pclk1: Option<Hertz>, | ||
| 19 | pub pclk2: Option<Hertz>, | ||
| 20 | |||
| 21 | pub pll48: bool, | ||
| 22 | } | ||
| 23 | |||
| 24 | unsafe fn setup_pll( | ||
| 25 | pllsrcclk: u32, | ||
| 26 | use_hse: bool, | ||
| 27 | pllsysclk: Option<u32>, | ||
| 28 | pll48clk: bool, | ||
| 29 | ) -> PllResults { | ||
| 30 | use crate::pac::rcc::vals::{Pllp, Pllsrc}; | ||
| 31 | |||
| 32 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | ||
| 33 | if pllsysclk.is_none() && !pll48clk { | ||
| 34 | RCC.pllcfgr() | ||
| 35 | .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); | ||
| 36 | |||
| 37 | return PllResults { | ||
| 38 | use_pll: false, | ||
| 39 | pllsysclk: None, | ||
| 40 | pll48clk: None, | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | // Input divisor from PLL source clock, must result to frequency in | ||
| 44 | // the range from 1 to 2 MHz | ||
| 45 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 46 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 47 | |||
| 48 | // Sysclk output divisor must be one of 2, 4, 6 or 8 | ||
| 49 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); | ||
| 50 | |||
| 51 | let target_freq = if pll48clk { | ||
| 52 | 48_000_000 | ||
| 53 | } else { | ||
| 54 | sysclk * sysclk_div | ||
| 55 | }; | ||
| 56 | |||
| 57 | // Find the lowest pllm value that minimize the difference between | ||
| 58 | // target frequency and the real vco_out frequency. | ||
| 59 | let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { | ||
| 60 | let vco_in = pllsrcclk / pllm; | ||
| 61 | let plln = target_freq / vco_in; | ||
| 62 | target_freq - vco_in * plln | ||
| 63 | })); | ||
| 64 | |||
| 65 | let vco_in = pllsrcclk / pllm; | ||
| 66 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 67 | |||
| 68 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 69 | // and <= 432MHz, min 50, max 432 | ||
| 70 | let plln = if pll48clk { | ||
| 71 | // try the different valid pllq according to the valid | ||
| 72 | // main scaller values, and take the best | ||
| 73 | let pllq = unwrap!((4..=9).min_by_key(|pllq| { | ||
| 74 | let plln = 48_000_000 * pllq / vco_in; | ||
| 75 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 76 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 77 | (pll48_diff, sysclk_diff) | ||
| 78 | })); | ||
| 79 | 48_000_000 * pllq / vco_in | ||
| 80 | } else { | ||
| 81 | sysclk * sysclk_div / vco_in | ||
| 82 | }; | ||
| 83 | |||
| 84 | let pllp = (sysclk_div / 2) - 1; | ||
| 85 | |||
| 86 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; | ||
| 87 | let real_pll48clk = vco_in * plln / pllq; | ||
| 88 | |||
| 89 | RCC.pllcfgr().modify(|w| { | ||
| 90 | w.set_pllm(pllm as u8); | ||
| 91 | w.set_plln(plln as u16); | ||
| 92 | w.set_pllp(Pllp(pllp as u8)); | ||
| 93 | w.set_pllq(pllq as u8); | ||
| 94 | w.set_pllsrc(Pllsrc(use_hse as u8)); | ||
| 95 | }); | ||
| 96 | |||
| 97 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 98 | |||
| 99 | PllResults { | ||
| 100 | use_pll: true, | ||
| 101 | pllsysclk: Some(real_pllsysclk), | ||
| 102 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | unsafe fn flash_setup(sysclk: u32) { | ||
| 107 | use crate::pac::flash::vals::Latency; | ||
| 108 | |||
| 109 | // Be conservative with voltage ranges | ||
| 110 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 111 | |||
| 112 | critical_section::with(|_| { | ||
| 113 | FLASH | ||
| 114 | .acr() | ||
| 115 | .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 116 | }); | ||
| 117 | } | ||
| 118 | |||
| 119 | pub(crate) unsafe fn init(config: Config) { | ||
| 120 | crate::peripherals::PWR::enable(); | ||
| 121 | |||
| 122 | if let Some(hse) = config.hse { | ||
| 123 | if config.bypass_hse { | ||
| 124 | assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0)); | ||
| 125 | } else { | ||
| 126 | assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0)); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI); | ||
| 131 | let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); | ||
| 132 | let sysclk_on_pll = sysclk != pllsrcclk; | ||
| 133 | |||
| 134 | assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk)); | ||
| 135 | |||
| 136 | let plls = setup_pll( | ||
| 137 | pllsrcclk, | ||
| 138 | config.hse.is_some(), | ||
| 139 | if sysclk_on_pll { Some(sysclk) } else { None }, | ||
| 140 | config.pll48, | ||
| 141 | ); | ||
| 142 | |||
| 143 | if config.pll48 { | ||
| 144 | let freq = unwrap!(plls.pll48clk); | ||
| 145 | |||
| 146 | assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32); | ||
| 147 | } | ||
| 148 | |||
| 149 | let sysclk = if sysclk_on_pll { | ||
| 150 | unwrap!(plls.pllsysclk) | ||
| 151 | } else { | ||
| 152 | sysclk | ||
| 153 | }; | ||
| 154 | |||
| 155 | // AHB prescaler | ||
| 156 | let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 157 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { | ||
| 158 | 0 => unreachable!(), | ||
| 159 | 1 => (Hpre::DIV1, 1), | ||
| 160 | 2 => (Hpre::DIV2, 2), | ||
| 161 | 3..=5 => (Hpre::DIV4, 4), | ||
| 162 | 6..=11 => (Hpre::DIV8, 8), | ||
| 163 | 12..=39 => (Hpre::DIV16, 16), | ||
| 164 | 40..=95 => (Hpre::DIV64, 64), | ||
| 165 | 96..=191 => (Hpre::DIV128, 128), | ||
| 166 | 192..=383 => (Hpre::DIV256, 256), | ||
| 167 | _ => (Hpre::DIV512, 512), | ||
| 168 | }; | ||
| 169 | |||
| 170 | // Calculate real AHB clock | ||
| 171 | let hclk = sysclk / hpre_div; | ||
| 172 | |||
| 173 | assert!(hclk < max::HCLK_MAX); | ||
| 174 | |||
| 175 | let pclk1 = config | ||
| 176 | .pclk1 | ||
| 177 | .map(|p| p.0) | ||
| 178 | .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); | ||
| 179 | |||
| 180 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | ||
| 181 | 0 => unreachable!(), | ||
| 182 | 1 => (0b000, 1), | ||
| 183 | 2 => (0b100, 2), | ||
| 184 | 3..=5 => (0b101, 4), | ||
| 185 | 6..=11 => (0b110, 8), | ||
| 186 | _ => (0b111, 16), | ||
| 187 | }; | ||
| 188 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 189 | |||
| 190 | // Calculate real APB1 clock | ||
| 191 | let pclk1 = hclk / ppre1; | ||
| 192 | assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1)); | ||
| 193 | |||
| 194 | let pclk2 = config | ||
| 195 | .pclk2 | ||
| 196 | .map(|p| p.0) | ||
| 197 | .unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk)); | ||
| 198 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { | ||
| 199 | 0 => unreachable!(), | ||
| 200 | 1 => (0b000, 1), | ||
| 201 | 2 => (0b100, 2), | ||
| 202 | 3..=5 => (0b101, 4), | ||
| 203 | 6..=11 => (0b110, 8), | ||
| 204 | _ => (0b111, 16), | ||
| 205 | }; | ||
| 206 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 207 | |||
| 208 | // Calculate real APB2 clock | ||
| 209 | let pclk2 = hclk / ppre2; | ||
| 210 | assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2)); | ||
| 211 | |||
| 212 | flash_setup(sysclk); | ||
| 213 | |||
| 214 | if config.hse.is_some() { | ||
| 215 | RCC.cr().modify(|w| { | ||
| 216 | w.set_hsebyp(Hsebyp(config.bypass_hse as u8)); | ||
| 217 | w.set_hseon(true); | ||
| 218 | }); | ||
| 219 | while !RCC.cr().read().hserdy() {} | ||
| 220 | } | ||
| 221 | |||
| 222 | if plls.use_pll { | ||
| 223 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 224 | |||
| 225 | // enable PWR and setup VOSScale | ||
| 226 | |||
| 227 | RCC.apb1enr().modify(|w| w.set_pwren(true)); | ||
| 228 | |||
| 229 | let vos_scale = if sysclk <= 144_000_000 { | ||
| 230 | 3 | ||
| 231 | } else if sysclk <= 168_000_000 { | ||
| 232 | 2 | ||
| 233 | } else { | ||
| 234 | 1 | ||
| 235 | }; | ||
| 236 | PWR.cr1().modify(|w| { | ||
| 237 | w.set_vos(match vos_scale { | ||
| 238 | 3 => Vos::SCALE3, | ||
| 239 | 2 => Vos::SCALE2, | ||
| 240 | 1 => Vos::SCALE1, | ||
| 241 | _ => panic!("Invalid VOS Scale."), | ||
| 242 | }) | ||
| 243 | }); | ||
| 244 | |||
| 245 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 246 | |||
| 247 | if hclk > max::HCLK_OVERDRIVE_FREQUENCY { | ||
| 248 | PWR.cr1().modify(|w| w.set_oden(true)); | ||
| 249 | while !PWR.csr1().read().odrdy() {} | ||
| 250 | |||
| 251 | PWR.cr1().modify(|w| w.set_odswen(true)); | ||
| 252 | while !PWR.csr1().read().odswrdy() {} | ||
| 253 | } | ||
| 254 | |||
| 255 | while !RCC.cr().read().pllrdy() {} | ||
| 256 | } | ||
| 257 | |||
| 258 | RCC.cfgr().modify(|w| { | ||
| 259 | w.set_ppre2(Ppre(ppre2_bits)); | ||
| 260 | w.set_ppre1(Ppre(ppre1_bits)); | ||
| 261 | w.set_hpre(hpre_bits); | ||
| 262 | }); | ||
| 263 | |||
| 264 | // Wait for the new prescalers to kick in | ||
| 265 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | ||
| 266 | cortex_m::asm::delay(16); | ||
| 267 | |||
| 268 | RCC.cfgr().modify(|w| { | ||
| 269 | w.set_sw(if sysclk_on_pll { | ||
| 270 | Sw::PLL | ||
| 271 | } else if config.hse.is_some() { | ||
| 272 | Sw::HSE | ||
| 273 | } else { | ||
| 274 | Sw::HSI | ||
| 275 | }) | ||
| 276 | }); | ||
| 277 | |||
| 278 | set_freqs(Clocks { | ||
| 279 | sys: Hertz(sysclk), | ||
| 280 | apb1: Hertz(pclk1), | ||
| 281 | apb2: Hertz(pclk2), | ||
| 282 | |||
| 283 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 284 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 285 | |||
| 286 | ahb1: Hertz(hclk), | ||
| 287 | ahb2: Hertz(hclk), | ||
| 288 | ahb3: Hertz(hclk), | ||
| 289 | |||
| 290 | pll48: plls.pll48clk.map(Hertz), | ||
| 291 | }); | ||
| 292 | } | ||
| 293 | |||
| 294 | struct PllResults { | ||
| 295 | use_pll: bool, | ||
| 296 | pllsysclk: Option<u32>, | ||
| 297 | pll48clk: Option<u32>, | ||
| 298 | } | ||
| 299 | |||
| 300 | mod max { | ||
| 301 | pub(crate) const HSE_OSC_MIN: u32 = 4_000_000; | ||
| 302 | pub(crate) const HSE_OSC_MAX: u32 = 26_000_000; | ||
| 303 | pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000; | ||
| 304 | pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000; | ||
| 305 | |||
| 306 | pub(crate) const HCLK_MAX: u32 = 216_000_000; | ||
| 307 | pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000; | ||
| 308 | |||
| 309 | pub(crate) const SYSCLK_MIN: u32 = 12_500_000; | ||
| 310 | pub(crate) const SYSCLK_MAX: u32 = 216_000_000; | ||
| 311 | |||
| 312 | pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN; | ||
| 313 | pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4; | ||
| 314 | |||
| 315 | pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN; | ||
| 316 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | ||
| 317 | |||
| 318 | // USB specification allows +-0.25% | ||
| 319 | pub(crate) const PLL_48_CLK: u32 = 48_000_000; | ||
| 320 | pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; | ||
| 321 | } | ||
diff --git a/embassy-stm32/src/rcc/f7/max.rs b/embassy-stm32/src/rcc/f7/max.rs deleted file mode 100644 index db00fdf3e..000000000 --- a/embassy-stm32/src/rcc/f7/max.rs +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | pub(crate) const HSE_OSC_MIN: u32 = 4_000_000; | ||
| 2 | pub(crate) const HSE_OSC_MAX: u32 = 26_000_000; | ||
| 3 | pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000; | ||
| 4 | pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000; | ||
| 5 | |||
| 6 | pub(crate) const HCLK_MAX: u32 = 216_000_000; | ||
| 7 | pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000; | ||
| 8 | |||
| 9 | pub(crate) const SYSCLK_MIN: u32 = 12_500_000; | ||
| 10 | pub(crate) const SYSCLK_MAX: u32 = 216_000_000; | ||
| 11 | |||
| 12 | pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN; | ||
| 13 | pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4; | ||
| 14 | |||
| 15 | pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN; | ||
| 16 | pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; | ||
| 17 | |||
| 18 | pub(crate) const PLL_48_CLK: u32 = 48_000_000; | ||
| 19 | pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; | ||
diff --git a/embassy-stm32/src/rcc/f7/mod.rs b/embassy-stm32/src/rcc/f7/mod.rs deleted file mode 100644 index c13e20167..000000000 --- a/embassy-stm32/src/rcc/f7/mod.rs +++ /dev/null | |||
| @@ -1,347 +0,0 @@ | |||
| 1 | mod max; | ||
| 2 | |||
| 3 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 4 | use crate::peripherals; | ||
| 5 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 6 | use crate::time::Hertz; | ||
| 7 | use core::marker::PhantomData; | ||
| 8 | use embassy::util::Unborrow; | ||
| 9 | |||
| 10 | const HSI: u32 = 16_000_000; | ||
| 11 | |||
| 12 | #[non_exhaustive] | ||
| 13 | #[derive(Default)] | ||
| 14 | pub struct Config { | ||
| 15 | pub hse: Option<Hertz>, | ||
| 16 | pub bypass_hse: bool, | ||
| 17 | pub hclk: Option<Hertz>, | ||
| 18 | pub sys_ck: Option<Hertz>, | ||
| 19 | pub pclk1: Option<Hertz>, | ||
| 20 | pub pclk2: Option<Hertz>, | ||
| 21 | |||
| 22 | pub pll48: bool, | ||
| 23 | } | ||
| 24 | |||
| 25 | /// RCC peripheral | ||
| 26 | pub struct Rcc<'d> { | ||
| 27 | config: Config, | ||
| 28 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl<'d> Rcc<'d> { | ||
| 32 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 33 | if let Some(hse) = config.hse { | ||
| 34 | if config.bypass_hse { | ||
| 35 | assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0)); | ||
| 36 | } else { | ||
| 37 | assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0)); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | Self { | ||
| 41 | config, | ||
| 42 | phantom: PhantomData, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | fn freeze(mut self) -> Clocks { | ||
| 47 | use super::sealed::RccPeripheral; | ||
| 48 | use crate::pac::pwr::vals::Vos; | ||
| 49 | use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw}; | ||
| 50 | |||
| 51 | let base_clock = self.config.hse.map(|hse| hse.0).unwrap_or(HSI); | ||
| 52 | let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(base_clock); | ||
| 53 | let sysclk_on_pll = sysclk != base_clock; | ||
| 54 | |||
| 55 | assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk)); | ||
| 56 | |||
| 57 | let plls = self.setup_pll( | ||
| 58 | base_clock, | ||
| 59 | self.config.hse.is_some(), | ||
| 60 | if sysclk_on_pll { Some(sysclk) } else { None }, | ||
| 61 | self.config.pll48, | ||
| 62 | ); | ||
| 63 | |||
| 64 | if self.config.pll48 { | ||
| 65 | assert!( | ||
| 66 | // USB specification allows +-0.25% | ||
| 67 | plls.pll48clk | ||
| 68 | .map(|freq| (max::PLL_48_CLK as i32 - freq as i32).abs() | ||
| 69 | <= max::PLL_48_TOLERANCE as i32) | ||
| 70 | .unwrap_or(false) | ||
| 71 | ); | ||
| 72 | } | ||
| 73 | |||
| 74 | let sysclk = if sysclk_on_pll { | ||
| 75 | unwrap!(plls.pllsysclk) | ||
| 76 | } else { | ||
| 77 | sysclk | ||
| 78 | }; | ||
| 79 | |||
| 80 | // AHB prescaler | ||
| 81 | let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk); | ||
| 82 | let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { | ||
| 83 | 0 => unreachable!(), | ||
| 84 | 1 => (Hpre::DIV1, 1), | ||
| 85 | 2 => (Hpre::DIV2, 2), | ||
| 86 | 3..=5 => (Hpre::DIV4, 4), | ||
| 87 | 6..=11 => (Hpre::DIV8, 8), | ||
| 88 | 12..=39 => (Hpre::DIV16, 16), | ||
| 89 | 40..=95 => (Hpre::DIV64, 64), | ||
| 90 | 96..=191 => (Hpre::DIV128, 128), | ||
| 91 | 192..=383 => (Hpre::DIV256, 256), | ||
| 92 | _ => (Hpre::DIV512, 512), | ||
| 93 | }; | ||
| 94 | |||
| 95 | // Calculate real AHB clock | ||
| 96 | let hclk = sysclk / hpre_div; | ||
| 97 | |||
| 98 | assert!(hclk < max::HCLK_MAX); | ||
| 99 | |||
| 100 | let pclk1 = self | ||
| 101 | .config | ||
| 102 | .pclk1 | ||
| 103 | .map(|p| p.0) | ||
| 104 | .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); | ||
| 105 | |||
| 106 | let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { | ||
| 107 | 0 => unreachable!(), | ||
| 108 | 1 => (0b000, 1), | ||
| 109 | 2 => (0b100, 2), | ||
| 110 | 3..=5 => (0b101, 4), | ||
| 111 | 6..=11 => (0b110, 8), | ||
| 112 | _ => (0b111, 16), | ||
| 113 | }; | ||
| 114 | let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; | ||
| 115 | |||
| 116 | // Calculate real APB1 clock | ||
| 117 | let pclk1 = hclk / ppre1; | ||
| 118 | assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1)); | ||
| 119 | |||
| 120 | let pclk2 = self | ||
| 121 | .config | ||
| 122 | .pclk2 | ||
| 123 | .map(|p| p.0) | ||
| 124 | .unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk)); | ||
| 125 | let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { | ||
| 126 | 0 => unreachable!(), | ||
| 127 | 1 => (0b000, 1), | ||
| 128 | 2 => (0b100, 2), | ||
| 129 | 3..=5 => (0b101, 4), | ||
| 130 | 6..=11 => (0b110, 8), | ||
| 131 | _ => (0b111, 16), | ||
| 132 | }; | ||
| 133 | let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; | ||
| 134 | |||
| 135 | // Calculate real APB2 clock | ||
| 136 | let pclk2 = hclk / ppre2; | ||
| 137 | assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2)); | ||
| 138 | |||
| 139 | Self::flash_setup(sysclk); | ||
| 140 | |||
| 141 | if self.config.hse.is_some() { | ||
| 142 | // NOTE(unsafe) We own the peripheral block | ||
| 143 | unsafe { | ||
| 144 | RCC.cr().modify(|w| { | ||
| 145 | w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8)); | ||
| 146 | w.set_hseon(true); | ||
| 147 | }); | ||
| 148 | while !RCC.cr().read().hserdy() {} | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | if plls.use_pll { | ||
| 153 | unsafe { | ||
| 154 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 155 | |||
| 156 | // enable PWR and setup VOSScale | ||
| 157 | |||
| 158 | RCC.apb1enr().modify(|w| w.set_pwren(true)); | ||
| 159 | |||
| 160 | let vos_scale = if sysclk <= 144_000_000 { | ||
| 161 | 3 | ||
| 162 | } else if sysclk <= 168_000_000 { | ||
| 163 | 2 | ||
| 164 | } else { | ||
| 165 | 1 | ||
| 166 | }; | ||
| 167 | PWR.cr1().modify(|w| { | ||
| 168 | w.set_vos(match vos_scale { | ||
| 169 | 3 => Vos::SCALE3, | ||
| 170 | 2 => Vos::SCALE2, | ||
| 171 | 1 => Vos::SCALE1, | ||
| 172 | _ => panic!("Invalid VOS Scale."), | ||
| 173 | }) | ||
| 174 | }); | ||
| 175 | |||
| 176 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 177 | |||
| 178 | while !RCC.cr().read().pllrdy() {} | ||
| 179 | |||
| 180 | if hclk > max::HCLK_OVERDRIVE_FREQUENCY { | ||
| 181 | peripherals::PWR::enable(); | ||
| 182 | |||
| 183 | PWR.cr1().modify(|w| w.set_oden(true)); | ||
| 184 | while !PWR.csr1().read().odrdy() {} | ||
| 185 | |||
| 186 | PWR.cr1().modify(|w| w.set_odswen(true)); | ||
| 187 | while !PWR.csr1().read().odswrdy() {} | ||
| 188 | } | ||
| 189 | |||
| 190 | while !RCC.cr().read().pllrdy() {} | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | unsafe { | ||
| 195 | RCC.cfgr().modify(|w| { | ||
| 196 | w.set_ppre2(Ppre(ppre2_bits)); | ||
| 197 | w.set_ppre1(Ppre(ppre1_bits)); | ||
| 198 | w.set_hpre(hpre_bits); | ||
| 199 | }); | ||
| 200 | |||
| 201 | // Wait for the new prescalers to kick in | ||
| 202 | // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" | ||
| 203 | cortex_m::asm::delay(16); | ||
| 204 | |||
| 205 | RCC.cfgr().modify(|w| { | ||
| 206 | w.set_sw(if sysclk_on_pll { | ||
| 207 | Sw::PLL | ||
| 208 | } else if self.config.hse.is_some() { | ||
| 209 | Sw::HSE | ||
| 210 | } else { | ||
| 211 | Sw::HSI | ||
| 212 | }) | ||
| 213 | }); | ||
| 214 | } | ||
| 215 | |||
| 216 | Clocks { | ||
| 217 | sys: Hertz(sysclk), | ||
| 218 | apb1: Hertz(pclk1), | ||
| 219 | apb2: Hertz(pclk2), | ||
| 220 | |||
| 221 | apb1_tim: Hertz(pclk1 * timer_mul1), | ||
| 222 | apb2_tim: Hertz(pclk2 * timer_mul2), | ||
| 223 | |||
| 224 | ahb1: Hertz(hclk), | ||
| 225 | ahb2: Hertz(hclk), | ||
| 226 | ahb3: Hertz(hclk), | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | // Safety: RCC init must have been called | ||
| 231 | pub fn clocks(&self) -> &'static Clocks { | ||
| 232 | unsafe { get_freqs() } | ||
| 233 | } | ||
| 234 | |||
| 235 | fn setup_pll( | ||
| 236 | &mut self, | ||
| 237 | pllsrcclk: u32, | ||
| 238 | use_hse: bool, | ||
| 239 | pllsysclk: Option<u32>, | ||
| 240 | pll48clk: bool, | ||
| 241 | ) -> PllResults { | ||
| 242 | use crate::pac::rcc::vals::{Pllp, Pllsrc}; | ||
| 243 | |||
| 244 | let sysclk = pllsysclk.unwrap_or(pllsrcclk); | ||
| 245 | if pllsysclk.is_none() && !pll48clk { | ||
| 246 | // NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock | ||
| 247 | unsafe { | ||
| 248 | RCC.pllcfgr() | ||
| 249 | .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); | ||
| 250 | } | ||
| 251 | |||
| 252 | return PllResults { | ||
| 253 | use_pll: false, | ||
| 254 | pllsysclk: None, | ||
| 255 | pll48clk: None, | ||
| 256 | }; | ||
| 257 | } | ||
| 258 | // Input divisor from PLL source clock, must result to frequency in | ||
| 259 | // the range from 1 to 2 MHz | ||
| 260 | let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; | ||
| 261 | let pllm_max = pllsrcclk / 1_000_000; | ||
| 262 | |||
| 263 | // Sysclk output divisor must be one of 2, 4, 6 or 8 | ||
| 264 | let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); | ||
| 265 | |||
| 266 | let target_freq = if pll48clk { | ||
| 267 | 48_000_000 | ||
| 268 | } else { | ||
| 269 | sysclk * sysclk_div | ||
| 270 | }; | ||
| 271 | |||
| 272 | // Find the lowest pllm value that minimize the difference between | ||
| 273 | // target frequency and the real vco_out frequency. | ||
| 274 | let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { | ||
| 275 | let vco_in = pllsrcclk / pllm; | ||
| 276 | let plln = target_freq / vco_in; | ||
| 277 | target_freq - vco_in * plln | ||
| 278 | })); | ||
| 279 | |||
| 280 | let vco_in = pllsrcclk / pllm; | ||
| 281 | assert!((1_000_000..=2_000_000).contains(&vco_in)); | ||
| 282 | |||
| 283 | // Main scaler, must result in >= 100MHz (>= 192MHz for F401) | ||
| 284 | // and <= 432MHz, min 50, max 432 | ||
| 285 | let plln = if pll48clk { | ||
| 286 | // try the different valid pllq according to the valid | ||
| 287 | // main scaller values, and take the best | ||
| 288 | let pllq = unwrap!((4..=9).min_by_key(|pllq| { | ||
| 289 | let plln = 48_000_000 * pllq / vco_in; | ||
| 290 | let pll48_diff = 48_000_000 - vco_in * plln / pllq; | ||
| 291 | let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); | ||
| 292 | (pll48_diff, sysclk_diff) | ||
| 293 | })); | ||
| 294 | 48_000_000 * pllq / vco_in | ||
| 295 | } else { | ||
| 296 | sysclk * sysclk_div / vco_in | ||
| 297 | }; | ||
| 298 | |||
| 299 | let pllp = (sysclk_div / 2) - 1; | ||
| 300 | |||
| 301 | let pllq = (vco_in * plln + 47_999_999) / 48_000_000; | ||
| 302 | let real_pll48clk = vco_in * plln / pllq; | ||
| 303 | |||
| 304 | unsafe { | ||
| 305 | RCC.pllcfgr().modify(|w| { | ||
| 306 | w.set_pllm(pllm as u8); | ||
| 307 | w.set_plln(plln as u16); | ||
| 308 | w.set_pllp(Pllp(pllp as u8)); | ||
| 309 | w.set_pllq(pllq as u8); | ||
| 310 | w.set_pllsrc(Pllsrc(use_hse as u8)); | ||
| 311 | }); | ||
| 312 | } | ||
| 313 | |||
| 314 | let real_pllsysclk = vco_in * plln / sysclk_div; | ||
| 315 | |||
| 316 | PllResults { | ||
| 317 | use_pll: true, | ||
| 318 | pllsysclk: Some(real_pllsysclk), | ||
| 319 | pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | fn flash_setup(sysclk: u32) { | ||
| 324 | use crate::pac::flash::vals::Latency; | ||
| 325 | |||
| 326 | // Be conservative with voltage ranges | ||
| 327 | const FLASH_LATENCY_STEP: u32 = 30_000_000; | ||
| 328 | |||
| 329 | critical_section::with(|_| unsafe { | ||
| 330 | FLASH | ||
| 331 | .acr() | ||
| 332 | .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); | ||
| 333 | }); | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | pub unsafe fn init(config: Config) { | ||
| 338 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 339 | let clocks = Rcc::new(r, config).freeze(); | ||
| 340 | set_freqs(clocks); | ||
| 341 | } | ||
| 342 | |||
| 343 | struct PllResults { | ||
| 344 | use_pll: bool, | ||
| 345 | pllsysclk: Option<u32>, | ||
| 346 | pll48clk: Option<u32>, | ||
| 347 | } | ||
diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs new file mode 100644 index 000000000..a3a4c197f --- /dev/null +++ b/embassy-stm32/src/rcc/g0.rs | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | use crate::pac::{PWR, RCC}; | ||
| 2 | use crate::rcc::{set_freqs, Clocks}; | ||
| 3 | use crate::time::Hertz; | ||
| 4 | use crate::time::U32Ext; | ||
| 5 | |||
| 6 | /// HSI speed | ||
| 7 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 8 | |||
| 9 | /// LSI speed | ||
| 10 | pub const LSI_FREQ: u32 = 32_000; | ||
| 11 | |||
| 12 | /// System clock mux source | ||
| 13 | #[derive(Clone, Copy)] | ||
| 14 | pub enum ClockSrc { | ||
| 15 | HSE(Hertz), | ||
| 16 | HSI16(HSI16Prescaler), | ||
| 17 | LSI, | ||
| 18 | } | ||
| 19 | |||
| 20 | #[derive(Clone, Copy)] | ||
| 21 | pub enum HSI16Prescaler { | ||
| 22 | NotDivided, | ||
| 23 | Div2, | ||
| 24 | Div4, | ||
| 25 | Div8, | ||
| 26 | Div16, | ||
| 27 | Div32, | ||
| 28 | Div64, | ||
| 29 | Div128, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl Into<u8> for HSI16Prescaler { | ||
| 33 | fn into(self) -> u8 { | ||
| 34 | match self { | ||
| 35 | HSI16Prescaler::NotDivided => 0x00, | ||
| 36 | HSI16Prescaler::Div2 => 0x01, | ||
| 37 | HSI16Prescaler::Div4 => 0x02, | ||
| 38 | HSI16Prescaler::Div8 => 0x03, | ||
| 39 | HSI16Prescaler::Div16 => 0x04, | ||
| 40 | HSI16Prescaler::Div32 => 0x05, | ||
| 41 | HSI16Prescaler::Div64 => 0x06, | ||
| 42 | HSI16Prescaler::Div128 => 0x07, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | /// AHB prescaler | ||
| 48 | #[derive(Clone, Copy, PartialEq)] | ||
| 49 | pub enum AHBPrescaler { | ||
| 50 | NotDivided, | ||
| 51 | Div2, | ||
| 52 | Div4, | ||
| 53 | Div8, | ||
| 54 | Div16, | ||
| 55 | Div64, | ||
| 56 | Div128, | ||
| 57 | Div256, | ||
| 58 | Div512, | ||
| 59 | } | ||
| 60 | |||
| 61 | /// APB prescaler | ||
| 62 | #[derive(Clone, Copy)] | ||
| 63 | pub enum APBPrescaler { | ||
| 64 | NotDivided, | ||
| 65 | Div2, | ||
| 66 | Div4, | ||
| 67 | Div8, | ||
| 68 | Div16, | ||
| 69 | } | ||
| 70 | |||
| 71 | impl Into<u8> for APBPrescaler { | ||
| 72 | fn into(self) -> u8 { | ||
| 73 | match self { | ||
| 74 | APBPrescaler::NotDivided => 1, | ||
| 75 | APBPrescaler::Div2 => 0x04, | ||
| 76 | APBPrescaler::Div4 => 0x05, | ||
| 77 | APBPrescaler::Div8 => 0x06, | ||
| 78 | APBPrescaler::Div16 => 0x07, | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | impl Into<u8> for AHBPrescaler { | ||
| 84 | fn into(self) -> u8 { | ||
| 85 | match self { | ||
| 86 | AHBPrescaler::NotDivided => 1, | ||
| 87 | AHBPrescaler::Div2 => 0x08, | ||
| 88 | AHBPrescaler::Div4 => 0x09, | ||
| 89 | AHBPrescaler::Div8 => 0x0a, | ||
| 90 | AHBPrescaler::Div16 => 0x0b, | ||
| 91 | AHBPrescaler::Div64 => 0x0c, | ||
| 92 | AHBPrescaler::Div128 => 0x0d, | ||
| 93 | AHBPrescaler::Div256 => 0x0e, | ||
| 94 | AHBPrescaler::Div512 => 0x0f, | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Clocks configutation | ||
| 100 | pub struct Config { | ||
| 101 | pub mux: ClockSrc, | ||
| 102 | pub ahb_pre: AHBPrescaler, | ||
| 103 | pub apb_pre: APBPrescaler, | ||
| 104 | pub low_power_run: bool, | ||
| 105 | } | ||
| 106 | |||
| 107 | impl Default for Config { | ||
| 108 | #[inline] | ||
| 109 | fn default() -> Config { | ||
| 110 | Config { | ||
| 111 | mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided), | ||
| 112 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 113 | apb_pre: APBPrescaler::NotDivided, | ||
| 114 | low_power_run: false, | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | pub(crate) unsafe fn init(config: Config) { | ||
| 120 | let (sys_clk, sw) = match config.mux { | ||
| 121 | ClockSrc::HSI16(div) => { | ||
| 122 | // Enable HSI16 | ||
| 123 | let div: u8 = div.into(); | ||
| 124 | RCC.cr().write(|w| { | ||
| 125 | w.set_hsidiv(div); | ||
| 126 | w.set_hsion(true) | ||
| 127 | }); | ||
| 128 | while !RCC.cr().read().hsirdy() {} | ||
| 129 | |||
| 130 | (HSI_FREQ >> div, 0x00) | ||
| 131 | } | ||
| 132 | ClockSrc::HSE(freq) => { | ||
| 133 | // Enable HSE | ||
| 134 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 135 | while !RCC.cr().read().hserdy() {} | ||
| 136 | |||
| 137 | (freq.0, 0x01) | ||
| 138 | } | ||
| 139 | ClockSrc::LSI => { | ||
| 140 | // Enable LSI | ||
| 141 | RCC.csr().write(|w| w.set_lsion(true)); | ||
| 142 | while !RCC.csr().read().lsirdy() {} | ||
| 143 | (LSI_FREQ, 0x03) | ||
| 144 | } | ||
| 145 | }; | ||
| 146 | |||
| 147 | RCC.cfgr().modify(|w| { | ||
| 148 | w.set_sw(sw.into()); | ||
| 149 | w.set_hpre(config.ahb_pre.into()); | ||
| 150 | w.set_ppre(config.apb_pre.into()); | ||
| 151 | }); | ||
| 152 | |||
| 153 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 154 | AHBPrescaler::NotDivided => sys_clk, | ||
| 155 | pre => { | ||
| 156 | let pre: u8 = pre.into(); | ||
| 157 | let pre = 1 << (pre as u32 - 7); | ||
| 158 | sys_clk / pre | ||
| 159 | } | ||
| 160 | }; | ||
| 161 | |||
| 162 | let (apb_freq, apb_tim_freq) = match config.apb_pre { | ||
| 163 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 164 | pre => { | ||
| 165 | let pre: u8 = pre.into(); | ||
| 166 | let pre: u8 = 1 << (pre - 3); | ||
| 167 | let freq = ahb_freq / pre as u32; | ||
| 168 | (freq, freq * 2) | ||
| 169 | } | ||
| 170 | }; | ||
| 171 | |||
| 172 | if config.low_power_run { | ||
| 173 | assert!(sys_clk.hz() <= 2_000_000.hz()); | ||
| 174 | PWR.cr1().modify(|w| w.set_lpr(true)); | ||
| 175 | } | ||
| 176 | |||
| 177 | set_freqs(Clocks { | ||
| 178 | sys: sys_clk.hz(), | ||
| 179 | ahb: ahb_freq.hz(), | ||
| 180 | apb: apb_freq.hz(), | ||
| 181 | apb_tim: apb_tim_freq.hz(), | ||
| 182 | }); | ||
| 183 | } | ||
diff --git a/embassy-stm32/src/rcc/g0/mod.rs b/embassy-stm32/src/rcc/g0/mod.rs deleted file mode 100644 index 7f7af2fc9..000000000 --- a/embassy-stm32/src/rcc/g0/mod.rs +++ /dev/null | |||
| @@ -1,234 +0,0 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::{self, RCC}; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use embassy::util::Unborrow; | ||
| 8 | use embassy_hal_common::unborrow; | ||
| 9 | |||
| 10 | /// HSI speed | ||
| 11 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 12 | |||
| 13 | /// LSI speed | ||
| 14 | pub const LSI_FREQ: u32 = 32_000; | ||
| 15 | |||
| 16 | /// System clock mux source | ||
| 17 | #[derive(Clone, Copy)] | ||
| 18 | pub enum ClockSrc { | ||
| 19 | HSE(Hertz), | ||
| 20 | HSI16(HSI16Prescaler), | ||
| 21 | LSI, | ||
| 22 | } | ||
| 23 | |||
| 24 | #[derive(Clone, Copy)] | ||
| 25 | pub enum HSI16Prescaler { | ||
| 26 | NotDivided, | ||
| 27 | Div2, | ||
| 28 | Div4, | ||
| 29 | Div8, | ||
| 30 | Div16, | ||
| 31 | Div32, | ||
| 32 | Div64, | ||
| 33 | Div128, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl Into<u8> for HSI16Prescaler { | ||
| 37 | fn into(self) -> u8 { | ||
| 38 | match self { | ||
| 39 | HSI16Prescaler::NotDivided => 0x00, | ||
| 40 | HSI16Prescaler::Div2 => 0x01, | ||
| 41 | HSI16Prescaler::Div4 => 0x02, | ||
| 42 | HSI16Prescaler::Div8 => 0x03, | ||
| 43 | HSI16Prescaler::Div16 => 0x04, | ||
| 44 | HSI16Prescaler::Div32 => 0x05, | ||
| 45 | HSI16Prescaler::Div64 => 0x06, | ||
| 46 | HSI16Prescaler::Div128 => 0x07, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | /// AHB prescaler | ||
| 52 | #[derive(Clone, Copy, PartialEq)] | ||
| 53 | pub enum AHBPrescaler { | ||
| 54 | NotDivided, | ||
| 55 | Div2, | ||
| 56 | Div4, | ||
| 57 | Div8, | ||
| 58 | Div16, | ||
| 59 | Div64, | ||
| 60 | Div128, | ||
| 61 | Div256, | ||
| 62 | Div512, | ||
| 63 | } | ||
| 64 | |||
| 65 | /// APB prescaler | ||
| 66 | #[derive(Clone, Copy)] | ||
| 67 | pub enum APBPrescaler { | ||
| 68 | NotDivided, | ||
| 69 | Div2, | ||
| 70 | Div4, | ||
| 71 | Div8, | ||
| 72 | Div16, | ||
| 73 | } | ||
| 74 | |||
| 75 | impl Into<u8> for APBPrescaler { | ||
| 76 | fn into(self) -> u8 { | ||
| 77 | match self { | ||
| 78 | APBPrescaler::NotDivided => 1, | ||
| 79 | APBPrescaler::Div2 => 0x04, | ||
| 80 | APBPrescaler::Div4 => 0x05, | ||
| 81 | APBPrescaler::Div8 => 0x06, | ||
| 82 | APBPrescaler::Div16 => 0x07, | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | impl Into<u8> for AHBPrescaler { | ||
| 88 | fn into(self) -> u8 { | ||
| 89 | match self { | ||
| 90 | AHBPrescaler::NotDivided => 1, | ||
| 91 | AHBPrescaler::Div2 => 0x08, | ||
| 92 | AHBPrescaler::Div4 => 0x09, | ||
| 93 | AHBPrescaler::Div8 => 0x0a, | ||
| 94 | AHBPrescaler::Div16 => 0x0b, | ||
| 95 | AHBPrescaler::Div64 => 0x0c, | ||
| 96 | AHBPrescaler::Div128 => 0x0d, | ||
| 97 | AHBPrescaler::Div256 => 0x0e, | ||
| 98 | AHBPrescaler::Div512 => 0x0f, | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Clocks configutation | ||
| 104 | pub struct Config { | ||
| 105 | pub mux: ClockSrc, | ||
| 106 | pub ahb_pre: AHBPrescaler, | ||
| 107 | pub apb_pre: APBPrescaler, | ||
| 108 | pub low_power_run: bool, | ||
| 109 | } | ||
| 110 | |||
| 111 | impl Default for Config { | ||
| 112 | #[inline] | ||
| 113 | fn default() -> Config { | ||
| 114 | Config { | ||
| 115 | mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided), | ||
| 116 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 117 | apb_pre: APBPrescaler::NotDivided, | ||
| 118 | low_power_run: false, | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | /// RCC peripheral | ||
| 124 | pub struct Rcc<'d> { | ||
| 125 | _rb: peripherals::RCC, | ||
| 126 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl<'d> Rcc<'d> { | ||
| 130 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | ||
| 131 | unborrow!(rcc); | ||
| 132 | Self { | ||
| 133 | _rb: rcc, | ||
| 134 | phantom: PhantomData, | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | // Safety: RCC init must have been called | ||
| 139 | pub fn clocks(&self) -> &'static Clocks { | ||
| 140 | unsafe { get_freqs() } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | ||
| 145 | pub trait RccExt { | ||
| 146 | fn freeze(self, config: Config) -> Clocks; | ||
| 147 | } | ||
| 148 | |||
| 149 | impl RccExt for RCC { | ||
| 150 | #[inline] | ||
| 151 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 152 | let rcc = pac::RCC; | ||
| 153 | let (sys_clk, sw) = match cfgr.mux { | ||
| 154 | ClockSrc::HSI16(div) => { | ||
| 155 | // Enable HSI16 | ||
| 156 | let div: u8 = div.into(); | ||
| 157 | unsafe { | ||
| 158 | rcc.cr().write(|w| { | ||
| 159 | w.set_hsidiv(div); | ||
| 160 | w.set_hsion(true) | ||
| 161 | }); | ||
| 162 | while !rcc.cr().read().hsirdy() {} | ||
| 163 | } | ||
| 164 | |||
| 165 | (HSI_FREQ >> div, 0x00) | ||
| 166 | } | ||
| 167 | ClockSrc::HSE(freq) => { | ||
| 168 | // Enable HSE | ||
| 169 | unsafe { | ||
| 170 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 171 | while !rcc.cr().read().hserdy() {} | ||
| 172 | } | ||
| 173 | |||
| 174 | (freq.0, 0x01) | ||
| 175 | } | ||
| 176 | ClockSrc::LSI => { | ||
| 177 | // Enable LSI | ||
| 178 | unsafe { | ||
| 179 | rcc.csr().write(|w| w.set_lsion(true)); | ||
| 180 | while !rcc.csr().read().lsirdy() {} | ||
| 181 | } | ||
| 182 | (LSI_FREQ, 0x03) | ||
| 183 | } | ||
| 184 | }; | ||
| 185 | |||
| 186 | unsafe { | ||
| 187 | rcc.cfgr().modify(|w| { | ||
| 188 | w.set_sw(sw.into()); | ||
| 189 | w.set_hpre(cfgr.ahb_pre.into()); | ||
| 190 | w.set_ppre(cfgr.apb_pre.into()); | ||
| 191 | }); | ||
| 192 | } | ||
| 193 | |||
| 194 | let ahb_freq: u32 = match cfgr.ahb_pre { | ||
| 195 | AHBPrescaler::NotDivided => sys_clk, | ||
| 196 | pre => { | ||
| 197 | let pre: u8 = pre.into(); | ||
| 198 | let pre = 1 << (pre as u32 - 7); | ||
| 199 | sys_clk / pre | ||
| 200 | } | ||
| 201 | }; | ||
| 202 | |||
| 203 | let (apb_freq, apb_tim_freq) = match cfgr.apb_pre { | ||
| 204 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 205 | pre => { | ||
| 206 | let pre: u8 = pre.into(); | ||
| 207 | let pre: u8 = 1 << (pre - 3); | ||
| 208 | let freq = ahb_freq / pre as u32; | ||
| 209 | (freq, freq * 2) | ||
| 210 | } | ||
| 211 | }; | ||
| 212 | |||
| 213 | let pwr = pac::PWR; | ||
| 214 | if cfgr.low_power_run { | ||
| 215 | assert!(sys_clk.hz() <= 2_000_000.hz()); | ||
| 216 | unsafe { | ||
| 217 | pwr.cr1().modify(|w| w.set_lpr(true)); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | Clocks { | ||
| 222 | sys: sys_clk.hz(), | ||
| 223 | ahb: ahb_freq.hz(), | ||
| 224 | apb: apb_freq.hz(), | ||
| 225 | apb_tim: apb_tim_freq.hz(), | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | pub unsafe fn init(config: Config) { | ||
| 231 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 232 | let clocks = r.freeze(config); | ||
| 233 | set_freqs(clocks); | ||
| 234 | } | ||
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs new file mode 100644 index 000000000..ce8cca45d --- /dev/null +++ b/embassy-stm32/src/rcc/g4.rs | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | use crate::pac::{PWR, RCC}; | ||
| 2 | use crate::rcc::{set_freqs, Clocks}; | ||
| 3 | use crate::time::Hertz; | ||
| 4 | use crate::time::U32Ext; | ||
| 5 | |||
| 6 | /// HSI speed | ||
| 7 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 8 | |||
| 9 | /// LSI speed | ||
| 10 | pub const LSI_FREQ: u32 = 32_000; | ||
| 11 | |||
| 12 | /// System clock mux source | ||
| 13 | #[derive(Clone, Copy)] | ||
| 14 | pub enum ClockSrc { | ||
| 15 | HSE(Hertz), | ||
| 16 | HSI16, | ||
| 17 | } | ||
| 18 | |||
| 19 | /// AHB prescaler | ||
| 20 | #[derive(Clone, Copy, PartialEq)] | ||
| 21 | pub enum AHBPrescaler { | ||
| 22 | NotDivided, | ||
| 23 | Div2, | ||
| 24 | Div4, | ||
| 25 | Div8, | ||
| 26 | Div16, | ||
| 27 | Div64, | ||
| 28 | Div128, | ||
| 29 | Div256, | ||
| 30 | Div512, | ||
| 31 | } | ||
| 32 | |||
| 33 | /// APB prescaler | ||
| 34 | #[derive(Clone, Copy)] | ||
| 35 | pub enum APBPrescaler { | ||
| 36 | NotDivided, | ||
| 37 | Div2, | ||
| 38 | Div4, | ||
| 39 | Div8, | ||
| 40 | Div16, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Into<u8> for APBPrescaler { | ||
| 44 | fn into(self) -> u8 { | ||
| 45 | match self { | ||
| 46 | APBPrescaler::NotDivided => 1, | ||
| 47 | APBPrescaler::Div2 => 0x04, | ||
| 48 | APBPrescaler::Div4 => 0x05, | ||
| 49 | APBPrescaler::Div8 => 0x06, | ||
| 50 | APBPrescaler::Div16 => 0x07, | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | impl Into<u8> for AHBPrescaler { | ||
| 56 | fn into(self) -> u8 { | ||
| 57 | match self { | ||
| 58 | AHBPrescaler::NotDivided => 1, | ||
| 59 | AHBPrescaler::Div2 => 0x08, | ||
| 60 | AHBPrescaler::Div4 => 0x09, | ||
| 61 | AHBPrescaler::Div8 => 0x0a, | ||
| 62 | AHBPrescaler::Div16 => 0x0b, | ||
| 63 | AHBPrescaler::Div64 => 0x0c, | ||
| 64 | AHBPrescaler::Div128 => 0x0d, | ||
| 65 | AHBPrescaler::Div256 => 0x0e, | ||
| 66 | AHBPrescaler::Div512 => 0x0f, | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Clocks configutation | ||
| 72 | pub struct Config { | ||
| 73 | pub mux: ClockSrc, | ||
| 74 | pub ahb_pre: AHBPrescaler, | ||
| 75 | pub apb1_pre: APBPrescaler, | ||
| 76 | pub apb2_pre: APBPrescaler, | ||
| 77 | pub low_power_run: bool, | ||
| 78 | } | ||
| 79 | |||
| 80 | impl Default for Config { | ||
| 81 | #[inline] | ||
| 82 | fn default() -> Config { | ||
| 83 | Config { | ||
| 84 | mux: ClockSrc::HSI16, | ||
| 85 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 86 | apb1_pre: APBPrescaler::NotDivided, | ||
| 87 | apb2_pre: APBPrescaler::NotDivided, | ||
| 88 | low_power_run: false, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | pub(crate) unsafe fn init(config: Config) { | ||
| 94 | let (sys_clk, sw) = match config.mux { | ||
| 95 | ClockSrc::HSI16 => { | ||
| 96 | // Enable HSI16 | ||
| 97 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 98 | while !RCC.cr().read().hsirdy() {} | ||
| 99 | |||
| 100 | (HSI_FREQ, 0x01) | ||
| 101 | } | ||
| 102 | ClockSrc::HSE(freq) => { | ||
| 103 | // Enable HSE | ||
| 104 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 105 | while !RCC.cr().read().hserdy() {} | ||
| 106 | |||
| 107 | (freq.0, 0x02) | ||
| 108 | } | ||
| 109 | }; | ||
| 110 | |||
| 111 | RCC.cfgr().modify(|w| { | ||
| 112 | w.set_sw(sw.into()); | ||
| 113 | w.set_hpre(config.ahb_pre.into()); | ||
| 114 | w.set_ppre1(config.apb1_pre.into()); | ||
| 115 | w.set_ppre2(config.apb2_pre.into()); | ||
| 116 | }); | ||
| 117 | |||
| 118 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 119 | AHBPrescaler::NotDivided => sys_clk, | ||
| 120 | pre => { | ||
| 121 | let pre: u8 = pre.into(); | ||
| 122 | let pre = 1 << (pre as u32 - 7); | ||
| 123 | sys_clk / pre | ||
| 124 | } | ||
| 125 | }; | ||
| 126 | |||
| 127 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 128 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 129 | pre => { | ||
| 130 | let pre: u8 = pre.into(); | ||
| 131 | let pre: u8 = 1 << (pre - 3); | ||
| 132 | let freq = ahb_freq / pre as u32; | ||
| 133 | (freq, freq * 2) | ||
| 134 | } | ||
| 135 | }; | ||
| 136 | |||
| 137 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 138 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 139 | pre => { | ||
| 140 | let pre: u8 = pre.into(); | ||
| 141 | let pre: u8 = 1 << (pre - 3); | ||
| 142 | let freq = ahb_freq / pre as u32; | ||
| 143 | (freq, freq * 2) | ||
| 144 | } | ||
| 145 | }; | ||
| 146 | |||
| 147 | if config.low_power_run { | ||
| 148 | assert!(sys_clk.hz() <= 2_000_000.hz()); | ||
| 149 | PWR.cr1().modify(|w| w.set_lpr(true)); | ||
| 150 | } | ||
| 151 | |||
| 152 | set_freqs(Clocks { | ||
| 153 | sys: sys_clk.hz(), | ||
| 154 | ahb1: ahb_freq.hz(), | ||
| 155 | ahb2: ahb_freq.hz(), | ||
| 156 | apb1: apb1_freq.hz(), | ||
| 157 | apb1_tim: apb1_tim_freq.hz(), | ||
| 158 | apb2: apb2_freq.hz(), | ||
| 159 | apb2_tim: apb2_tim_freq.hz(), | ||
| 160 | }); | ||
| 161 | } | ||
diff --git a/embassy-stm32/src/rcc/g4/mod.rs b/embassy-stm32/src/rcc/g4/mod.rs deleted file mode 100644 index 8a75b2e03..000000000 --- a/embassy-stm32/src/rcc/g4/mod.rs +++ /dev/null | |||
| @@ -1,210 +0,0 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::{self, RCC}; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use embassy::util::Unborrow; | ||
| 8 | use embassy_hal_common::unborrow; | ||
| 9 | |||
| 10 | /// HSI speed | ||
| 11 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 12 | |||
| 13 | /// LSI speed | ||
| 14 | pub const LSI_FREQ: u32 = 32_000; | ||
| 15 | |||
| 16 | /// System clock mux source | ||
| 17 | #[derive(Clone, Copy)] | ||
| 18 | pub enum ClockSrc { | ||
| 19 | HSE(Hertz), | ||
| 20 | HSI16, | ||
| 21 | } | ||
| 22 | |||
| 23 | /// AHB prescaler | ||
| 24 | #[derive(Clone, Copy, PartialEq)] | ||
| 25 | pub enum AHBPrescaler { | ||
| 26 | NotDivided, | ||
| 27 | Div2, | ||
| 28 | Div4, | ||
| 29 | Div8, | ||
| 30 | Div16, | ||
| 31 | Div64, | ||
| 32 | Div128, | ||
| 33 | Div256, | ||
| 34 | Div512, | ||
| 35 | } | ||
| 36 | |||
| 37 | /// APB prescaler | ||
| 38 | #[derive(Clone, Copy)] | ||
| 39 | pub enum APBPrescaler { | ||
| 40 | NotDivided, | ||
| 41 | Div2, | ||
| 42 | Div4, | ||
| 43 | Div8, | ||
| 44 | Div16, | ||
| 45 | } | ||
| 46 | |||
| 47 | impl Into<u8> for APBPrescaler { | ||
| 48 | fn into(self) -> u8 { | ||
| 49 | match self { | ||
| 50 | APBPrescaler::NotDivided => 1, | ||
| 51 | APBPrescaler::Div2 => 0x04, | ||
| 52 | APBPrescaler::Div4 => 0x05, | ||
| 53 | APBPrescaler::Div8 => 0x06, | ||
| 54 | APBPrescaler::Div16 => 0x07, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | impl Into<u8> for AHBPrescaler { | ||
| 60 | fn into(self) -> u8 { | ||
| 61 | match self { | ||
| 62 | AHBPrescaler::NotDivided => 1, | ||
| 63 | AHBPrescaler::Div2 => 0x08, | ||
| 64 | AHBPrescaler::Div4 => 0x09, | ||
| 65 | AHBPrescaler::Div8 => 0x0a, | ||
| 66 | AHBPrescaler::Div16 => 0x0b, | ||
| 67 | AHBPrescaler::Div64 => 0x0c, | ||
| 68 | AHBPrescaler::Div128 => 0x0d, | ||
| 69 | AHBPrescaler::Div256 => 0x0e, | ||
| 70 | AHBPrescaler::Div512 => 0x0f, | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Clocks configutation | ||
| 76 | pub struct Config { | ||
| 77 | pub mux: ClockSrc, | ||
| 78 | pub ahb_pre: AHBPrescaler, | ||
| 79 | pub apb1_pre: APBPrescaler, | ||
| 80 | pub apb2_pre: APBPrescaler, | ||
| 81 | pub low_power_run: bool, | ||
| 82 | } | ||
| 83 | |||
| 84 | impl Default for Config { | ||
| 85 | #[inline] | ||
| 86 | fn default() -> Config { | ||
| 87 | Config { | ||
| 88 | mux: ClockSrc::HSI16, | ||
| 89 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 90 | apb1_pre: APBPrescaler::NotDivided, | ||
| 91 | apb2_pre: APBPrescaler::NotDivided, | ||
| 92 | low_power_run: false, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | /// RCC peripheral | ||
| 98 | pub struct Rcc<'d> { | ||
| 99 | _rb: peripherals::RCC, | ||
| 100 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 101 | } | ||
| 102 | |||
| 103 | impl<'d> Rcc<'d> { | ||
| 104 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | ||
| 105 | unborrow!(rcc); | ||
| 106 | Self { | ||
| 107 | _rb: rcc, | ||
| 108 | phantom: PhantomData, | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | // Safety: RCC init must have been called | ||
| 113 | pub fn clocks(&self) -> &'static Clocks { | ||
| 114 | unsafe { get_freqs() } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | ||
| 119 | pub trait RccExt { | ||
| 120 | fn freeze(self, config: Config) -> Clocks; | ||
| 121 | } | ||
| 122 | |||
| 123 | impl RccExt for RCC { | ||
| 124 | #[inline] | ||
| 125 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 126 | let rcc = pac::RCC; | ||
| 127 | let (sys_clk, sw) = match cfgr.mux { | ||
| 128 | ClockSrc::HSI16 => { | ||
| 129 | // Enable HSI16 | ||
| 130 | unsafe { | ||
| 131 | rcc.cr().write(|w| w.set_hsion(true)); | ||
| 132 | while !rcc.cr().read().hsirdy() {} | ||
| 133 | } | ||
| 134 | |||
| 135 | (HSI_FREQ, 0x01) | ||
| 136 | } | ||
| 137 | ClockSrc::HSE(freq) => { | ||
| 138 | // Enable HSE | ||
| 139 | unsafe { | ||
| 140 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 141 | while !rcc.cr().read().hserdy() {} | ||
| 142 | } | ||
| 143 | |||
| 144 | (freq.0, 0x02) | ||
| 145 | } | ||
| 146 | }; | ||
| 147 | |||
| 148 | unsafe { | ||
| 149 | rcc.cfgr().modify(|w| { | ||
| 150 | w.set_sw(sw.into()); | ||
| 151 | w.set_hpre(cfgr.ahb_pre.into()); | ||
| 152 | w.set_ppre1(cfgr.apb1_pre.into()); | ||
| 153 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 154 | }); | ||
| 155 | } | ||
| 156 | |||
| 157 | let ahb_freq: u32 = match cfgr.ahb_pre { | ||
| 158 | AHBPrescaler::NotDivided => sys_clk, | ||
| 159 | pre => { | ||
| 160 | let pre: u8 = pre.into(); | ||
| 161 | let pre = 1 << (pre as u32 - 7); | ||
| 162 | sys_clk / pre | ||
| 163 | } | ||
| 164 | }; | ||
| 165 | |||
| 166 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | ||
| 167 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 168 | pre => { | ||
| 169 | let pre: u8 = pre.into(); | ||
| 170 | let pre: u8 = 1 << (pre - 3); | ||
| 171 | let freq = ahb_freq / pre as u32; | ||
| 172 | (freq, freq * 2) | ||
| 173 | } | ||
| 174 | }; | ||
| 175 | |||
| 176 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | ||
| 177 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 178 | pre => { | ||
| 179 | let pre: u8 = pre.into(); | ||
| 180 | let pre: u8 = 1 << (pre - 3); | ||
| 181 | let freq = ahb_freq / pre as u32; | ||
| 182 | (freq, freq * 2) | ||
| 183 | } | ||
| 184 | }; | ||
| 185 | |||
| 186 | let pwr = pac::PWR; | ||
| 187 | if cfgr.low_power_run { | ||
| 188 | assert!(sys_clk.hz() <= 2_000_000.hz()); | ||
| 189 | unsafe { | ||
| 190 | pwr.cr1().modify(|w| w.set_lpr(true)); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | Clocks { | ||
| 195 | sys: sys_clk.hz(), | ||
| 196 | ahb1: ahb_freq.hz(), | ||
| 197 | ahb2: ahb_freq.hz(), | ||
| 198 | apb1: apb1_freq.hz(), | ||
| 199 | apb1_tim: apb1_tim_freq.hz(), | ||
| 200 | apb2: apb2_freq.hz(), | ||
| 201 | apb2_tim: apb2_tim_freq.hz(), | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | pub unsafe fn init(config: Config) { | ||
| 207 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 208 | let clocks = r.freeze(config); | ||
| 209 | set_freqs(clocks); | ||
| 210 | } | ||
diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs new file mode 100644 index 000000000..55ddf073a --- /dev/null +++ b/embassy-stm32/src/rcc/h7.rs | |||
| @@ -0,0 +1,875 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy::util::Unborrow; | ||
| 4 | use embassy_hal_common::unborrow; | ||
| 5 | use stm32_metapac::rcc::vals::{Mco1, Mco2}; | ||
| 6 | |||
| 7 | use crate::gpio::sealed::Pin as __GpioPin; | ||
| 8 | use crate::gpio::Pin; | ||
| 9 | use crate::pac::rcc::vals::Timpre; | ||
| 10 | use crate::pac::rcc::vals::{Ckpersel, Dppre, Hpre, Hsebyp, Hsidiv, Pllsrc, Sw}; | ||
| 11 | use crate::pac::{PWR, RCC, SYSCFG}; | ||
| 12 | use crate::peripherals; | ||
| 13 | use crate::rcc::{set_freqs, Clocks}; | ||
| 14 | use crate::time::Hertz; | ||
| 15 | |||
| 16 | pub use pll::PllConfig; | ||
| 17 | |||
| 18 | const HSI: Hertz = Hertz(64_000_000); | ||
| 19 | const CSI: Hertz = Hertz(4_000_000); | ||
| 20 | const HSI48: Hertz = Hertz(48_000_000); | ||
| 21 | const LSI: Hertz = Hertz(32_000); | ||
| 22 | |||
| 23 | /// Voltage Scale | ||
| 24 | /// | ||
| 25 | /// Represents the voltage range feeding the CPU core. The maximum core | ||
| 26 | /// clock frequency depends on this value. | ||
| 27 | #[derive(Copy, Clone, PartialEq)] | ||
| 28 | pub enum VoltageScale { | ||
| 29 | /// VOS 0 range VCORE 1.26V - 1.40V | ||
| 30 | Scale0, | ||
| 31 | /// VOS 1 range VCORE 1.15V - 1.26V | ||
| 32 | Scale1, | ||
| 33 | /// VOS 2 range VCORE 1.05V - 1.15V | ||
| 34 | Scale2, | ||
| 35 | /// VOS 3 range VCORE 0.95V - 1.05V | ||
| 36 | Scale3, | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Core clock frequencies | ||
| 40 | #[derive(Clone, Copy)] | ||
| 41 | pub struct CoreClocks { | ||
| 42 | pub hclk: Hertz, | ||
| 43 | pub pclk1: Hertz, | ||
| 44 | pub pclk2: Hertz, | ||
| 45 | pub pclk3: Hertz, | ||
| 46 | pub pclk4: Hertz, | ||
| 47 | pub ppre1: u8, | ||
| 48 | pub ppre2: u8, | ||
| 49 | pub ppre3: u8, | ||
| 50 | pub ppre4: u8, | ||
| 51 | pub csi_ck: Option<Hertz>, | ||
| 52 | pub hsi_ck: Option<Hertz>, | ||
| 53 | pub hsi48_ck: Option<Hertz>, | ||
| 54 | pub lsi_ck: Option<Hertz>, | ||
| 55 | pub per_ck: Option<Hertz>, | ||
| 56 | pub hse_ck: Option<Hertz>, | ||
| 57 | pub pll1_p_ck: Option<Hertz>, | ||
| 58 | pub pll1_q_ck: Option<Hertz>, | ||
| 59 | pub pll1_r_ck: Option<Hertz>, | ||
| 60 | pub pll2_p_ck: Option<Hertz>, | ||
| 61 | pub pll2_q_ck: Option<Hertz>, | ||
| 62 | pub pll2_r_ck: Option<Hertz>, | ||
| 63 | pub pll3_p_ck: Option<Hertz>, | ||
| 64 | pub pll3_q_ck: Option<Hertz>, | ||
| 65 | pub pll3_r_ck: Option<Hertz>, | ||
| 66 | pub timx_ker_ck: Option<Hertz>, | ||
| 67 | pub timy_ker_ck: Option<Hertz>, | ||
| 68 | pub sys_ck: Hertz, | ||
| 69 | pub c_ck: Hertz, | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Configuration of the core clocks | ||
| 73 | #[non_exhaustive] | ||
| 74 | #[derive(Default)] | ||
| 75 | pub struct Config { | ||
| 76 | pub hse: Option<Hertz>, | ||
| 77 | pub bypass_hse: bool, | ||
| 78 | pub sys_ck: Option<Hertz>, | ||
| 79 | pub per_ck: Option<Hertz>, | ||
| 80 | rcc_hclk: Option<Hertz>, | ||
| 81 | pub hclk: Option<Hertz>, | ||
| 82 | pub pclk1: Option<Hertz>, | ||
| 83 | pub pclk2: Option<Hertz>, | ||
| 84 | pub pclk3: Option<Hertz>, | ||
| 85 | pub pclk4: Option<Hertz>, | ||
| 86 | pub pll1: PllConfig, | ||
| 87 | pub pll2: PllConfig, | ||
| 88 | pub pll3: PllConfig, | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Setup traceclk | ||
| 92 | /// Returns a pll1_r_ck | ||
| 93 | fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) { | ||
| 94 | let pll1_r_ck = match (sys_use_pll1_p, config.pll1.r_ck) { | ||
| 95 | // pll1_p_ck selected as system clock but pll1_r_ck not | ||
| 96 | // set. The traceclk mux is synchronous with the system | ||
| 97 | // clock mux, but has pll1_r_ck as an input. In order to | ||
| 98 | // keep traceclk running, we force a pll1_r_ck. | ||
| 99 | (true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)), | ||
| 100 | |||
| 101 | // Either pll1 not selected as system clock, free choice | ||
| 102 | // of pll1_r_ck. Or pll1 is selected, assume user has set | ||
| 103 | // a suitable pll1_r_ck frequency. | ||
| 104 | _ => config.pll1.r_ck, | ||
| 105 | }; | ||
| 106 | config.pll1.r_ck = pll1_r_ck; | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Divider calculator for pclk 1 - 4 | ||
| 110 | /// | ||
| 111 | /// Returns real pclk, bits, ppre and the timer kernel clock | ||
| 112 | fn ppre_calculate( | ||
| 113 | requested_pclk: u32, | ||
| 114 | hclk: u32, | ||
| 115 | max_pclk: u32, | ||
| 116 | tim_pre: Option<Timpre>, | ||
| 117 | ) -> (u32, u8, u8, Option<u32>) { | ||
| 118 | let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk { | ||
| 119 | 0 => panic!(), | ||
| 120 | 1 => (0b000, 1), | ||
| 121 | 2 => (0b100, 2), | ||
| 122 | 3..=5 => (0b101, 4), | ||
| 123 | 6..=11 => (0b110, 8), | ||
| 124 | _ => (0b111, 16), | ||
| 125 | }; | ||
| 126 | let real_pclk = hclk / u32::from(ppre); | ||
| 127 | assert!(real_pclk <= max_pclk); | ||
| 128 | |||
| 129 | let tim_ker_clk = if let Some(tim_pre) = tim_pre { | ||
| 130 | let clk = match (bits, tim_pre) { | ||
| 131 | (0b101, Timpre::DEFAULTX2) => hclk / 2, | ||
| 132 | (0b110, Timpre::DEFAULTX4) => hclk / 2, | ||
| 133 | (0b110, Timpre::DEFAULTX2) => hclk / 4, | ||
| 134 | (0b111, Timpre::DEFAULTX4) => hclk / 4, | ||
| 135 | (0b111, Timpre::DEFAULTX2) => hclk / 8, | ||
| 136 | _ => hclk, | ||
| 137 | }; | ||
| 138 | Some(clk) | ||
| 139 | } else { | ||
| 140 | None | ||
| 141 | }; | ||
| 142 | (real_pclk, bits, ppre, tim_ker_clk) | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Setup sys_ck | ||
| 146 | /// Returns sys_ck frequency, and a pll1_p_ck | ||
| 147 | fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) { | ||
| 148 | // Compare available with wanted clocks | ||
| 149 | let sys_ck = config.sys_ck.unwrap_or(srcclk); | ||
| 150 | |||
| 151 | if sys_ck != srcclk { | ||
| 152 | // The requested system clock is not the immediately available | ||
| 153 | // HSE/HSI clock. Perhaps there are other ways of obtaining | ||
| 154 | // the requested system clock (such as `HSIDIV`) but we will | ||
| 155 | // ignore those for now. | ||
| 156 | // | ||
| 157 | // Therefore we must use pll1_p_ck | ||
| 158 | let pll1_p_ck = match config.pll1.p_ck { | ||
| 159 | Some(p_ck) => { | ||
| 160 | assert!(p_ck == sys_ck, | ||
| 161 | "Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck"); | ||
| 162 | Some(p_ck) | ||
| 163 | } | ||
| 164 | None => Some(sys_ck), | ||
| 165 | }; | ||
| 166 | config.pll1.p_ck = pll1_p_ck; | ||
| 167 | |||
| 168 | (sys_ck, true) | ||
| 169 | } else { | ||
| 170 | // sys_ck is derived directly from a source clock | ||
| 171 | // (HSE/HSI). pll1_p_ck can be as requested | ||
| 172 | (sys_ck, false) | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { | ||
| 177 | use crate::pac::FLASH; | ||
| 178 | |||
| 179 | // ACLK in MHz, round down and subtract 1 from integers. eg. | ||
| 180 | // 61_999_999 -> 61MHz | ||
| 181 | // 62_000_000 -> 61MHz | ||
| 182 | // 62_000_001 -> 62MHz | ||
| 183 | let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000; | ||
| 184 | |||
| 185 | // See RM0433 Rev 7 Table 17. FLASH recommended number of wait | ||
| 186 | // states and programming delay | ||
| 187 | let (wait_states, progr_delay) = match vos { | ||
| 188 | // VOS 0 range VCORE 1.26V - 1.40V | ||
| 189 | VoltageScale::Scale0 => match rcc_aclk_mhz { | ||
| 190 | 0..=69 => (0, 0), | ||
| 191 | 70..=139 => (1, 1), | ||
| 192 | 140..=184 => (2, 1), | ||
| 193 | 185..=209 => (2, 2), | ||
| 194 | 210..=224 => (3, 2), | ||
| 195 | 225..=239 => (4, 2), | ||
| 196 | _ => (7, 3), | ||
| 197 | }, | ||
| 198 | // VOS 1 range VCORE 1.15V - 1.26V | ||
| 199 | VoltageScale::Scale1 => match rcc_aclk_mhz { | ||
| 200 | 0..=69 => (0, 0), | ||
| 201 | 70..=139 => (1, 1), | ||
| 202 | 140..=184 => (2, 1), | ||
| 203 | 185..=209 => (2, 2), | ||
| 204 | 210..=224 => (3, 2), | ||
| 205 | _ => (7, 3), | ||
| 206 | }, | ||
| 207 | // VOS 2 range VCORE 1.05V - 1.15V | ||
| 208 | VoltageScale::Scale2 => match rcc_aclk_mhz { | ||
| 209 | 0..=54 => (0, 0), | ||
| 210 | 55..=109 => (1, 1), | ||
| 211 | 110..=164 => (2, 1), | ||
| 212 | 165..=224 => (3, 2), | ||
| 213 | _ => (7, 3), | ||
| 214 | }, | ||
| 215 | // VOS 3 range VCORE 0.95V - 1.05V | ||
| 216 | VoltageScale::Scale3 => match rcc_aclk_mhz { | ||
| 217 | 0..=44 => (0, 0), | ||
| 218 | 45..=89 => (1, 1), | ||
| 219 | 90..=134 => (2, 1), | ||
| 220 | 135..=179 => (3, 2), | ||
| 221 | 180..=224 => (4, 2), | ||
| 222 | _ => (7, 3), | ||
| 223 | }, | ||
| 224 | }; | ||
| 225 | |||
| 226 | // NOTE(unsafe) Atomic write | ||
| 227 | unsafe { | ||
| 228 | FLASH.acr().write(|w| { | ||
| 229 | w.set_wrhighfreq(progr_delay); | ||
| 230 | w.set_latency(wait_states) | ||
| 231 | }); | ||
| 232 | while FLASH.acr().read().latency() != wait_states {} | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | pub enum McoClock { | ||
| 237 | Disabled, | ||
| 238 | Bypassed, | ||
| 239 | Divided(u8), | ||
| 240 | } | ||
| 241 | |||
| 242 | impl McoClock { | ||
| 243 | fn into_raw(&self) -> u8 { | ||
| 244 | match self { | ||
| 245 | McoClock::Disabled => 0, | ||
| 246 | McoClock::Bypassed => 1, | ||
| 247 | McoClock::Divided(divisor) => { | ||
| 248 | if *divisor > 15 { | ||
| 249 | panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.") | ||
| 250 | } | ||
| 251 | *divisor | ||
| 252 | } | ||
| 253 | } | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | #[derive(Copy, Clone)] | ||
| 258 | pub enum Mco1Source { | ||
| 259 | Hsi, | ||
| 260 | Lse, | ||
| 261 | Hse, | ||
| 262 | Pll1Q, | ||
| 263 | Hsi48, | ||
| 264 | } | ||
| 265 | |||
| 266 | impl Default for Mco1Source { | ||
| 267 | fn default() -> Self { | ||
| 268 | Self::Hsi | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | pub trait McoSource { | ||
| 273 | type Raw; | ||
| 274 | |||
| 275 | fn into_raw(&self) -> Self::Raw; | ||
| 276 | } | ||
| 277 | |||
| 278 | impl McoSource for Mco1Source { | ||
| 279 | type Raw = Mco1; | ||
| 280 | fn into_raw(&self) -> Self::Raw { | ||
| 281 | match self { | ||
| 282 | Mco1Source::Hsi => Mco1::HSI, | ||
| 283 | Mco1Source::Lse => Mco1::LSE, | ||
| 284 | Mco1Source::Hse => Mco1::HSE, | ||
| 285 | Mco1Source::Pll1Q => Mco1::PLL1_Q, | ||
| 286 | Mco1Source::Hsi48 => Mco1::HSI48, | ||
| 287 | } | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | #[derive(Copy, Clone)] | ||
| 292 | pub enum Mco2Source { | ||
| 293 | SysClk, | ||
| 294 | Pll2Q, | ||
| 295 | Hse, | ||
| 296 | Pll1Q, | ||
| 297 | Csi, | ||
| 298 | Lsi, | ||
| 299 | } | ||
| 300 | |||
| 301 | impl Default for Mco2Source { | ||
| 302 | fn default() -> Self { | ||
| 303 | Self::SysClk | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | impl McoSource for Mco2Source { | ||
| 308 | type Raw = Mco2; | ||
| 309 | fn into_raw(&self) -> Self::Raw { | ||
| 310 | match self { | ||
| 311 | Mco2Source::SysClk => Mco2::SYSCLK, | ||
| 312 | Mco2Source::Pll2Q => Mco2::PLL2_P, | ||
| 313 | Mco2Source::Hse => Mco2::HSE, | ||
| 314 | Mco2Source::Pll1Q => Mco2::PLL1_P, | ||
| 315 | Mco2Source::Csi => Mco2::CSI, | ||
| 316 | Mco2Source::Lsi => Mco2::LSI, | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | pub(crate) mod sealed { | ||
| 322 | use super::*; | ||
| 323 | |||
| 324 | pub trait McoInstance { | ||
| 325 | type Source; | ||
| 326 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); | ||
| 327 | } | ||
| 328 | |||
| 329 | pub trait McoPin<T: McoInstance>: Pin { | ||
| 330 | fn configure(&mut self); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 335 | |||
| 336 | pub trait McoPin<T: McoInstance>: sealed::McoPin<T> + 'static {} | ||
| 337 | |||
| 338 | macro_rules! impl_peri { | ||
| 339 | ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { | ||
| 340 | impl sealed::McoInstance for peripherals::$peri { | ||
| 341 | type Source = $source; | ||
| 342 | |||
| 343 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { | ||
| 344 | RCC.cfgr().modify(|w| { | ||
| 345 | w.$set_source(source); | ||
| 346 | w.$set_prescaler(prescaler); | ||
| 347 | }); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | impl McoInstance for peripherals::$peri {} | ||
| 352 | }; | ||
| 353 | } | ||
| 354 | |||
| 355 | impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); | ||
| 356 | impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); | ||
| 357 | |||
| 358 | macro_rules! impl_pin { | ||
| 359 | ($peri:ident, $pin:ident, $af:expr) => { | ||
| 360 | impl McoPin<peripherals::$peri> for peripherals::$pin {} | ||
| 361 | |||
| 362 | impl sealed::McoPin<peripherals::$peri> for peripherals::$pin { | ||
| 363 | fn configure(&mut self) { | ||
| 364 | critical_section::with(|_| unsafe { | ||
| 365 | self.set_as_af($af, crate::gpio::sealed::AFType::OutputPushPull); | ||
| 366 | self.block().ospeedr().modify(|w| { | ||
| 367 | w.set_ospeedr( | ||
| 368 | self.pin() as usize, | ||
| 369 | crate::pac::gpio::vals::Ospeedr::VERYHIGHSPEED, | ||
| 370 | ) | ||
| 371 | }); | ||
| 372 | }) | ||
| 373 | } | ||
| 374 | } | ||
| 375 | }; | ||
| 376 | } | ||
| 377 | |||
| 378 | crate::pac::peripheral_pins!( | ||
| 379 | ($inst:ident, rcc, RCC, $pin:ident, MCO_1, $af:expr) => { | ||
| 380 | impl_pin!(MCO1, $pin, $af); | ||
| 381 | }; | ||
| 382 | ($inst:ident, rcc, RCC, $pin:ident, MCO_2, $af:expr) => { | ||
| 383 | impl_pin!(MCO2, $pin, $af); | ||
| 384 | }; | ||
| 385 | ); | ||
| 386 | |||
| 387 | pub struct Mco<'d, T: McoInstance> { | ||
| 388 | phantom: PhantomData<&'d mut T>, | ||
| 389 | } | ||
| 390 | |||
| 391 | impl<'d, T: McoInstance> Mco<'d, T> { | ||
| 392 | pub fn new( | ||
| 393 | _peri: impl Unborrow<Target = T> + 'd, | ||
| 394 | pin: impl Unborrow<Target = impl McoPin<T>> + 'd, | ||
| 395 | source: impl McoSource<Raw = T::Source>, | ||
| 396 | prescaler: McoClock, | ||
| 397 | ) -> Self { | ||
| 398 | unborrow!(pin); | ||
| 399 | |||
| 400 | unsafe { | ||
| 401 | T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); | ||
| 402 | } | ||
| 403 | |||
| 404 | pin.configure(); | ||
| 405 | |||
| 406 | Self { | ||
| 407 | phantom: PhantomData, | ||
| 408 | } | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | pub(crate) unsafe fn init(mut config: Config) { | ||
| 413 | // TODO make configurable? | ||
| 414 | let enable_overdrive = false; | ||
| 415 | |||
| 416 | // NB. The lower bytes of CR3 can only be written once after | ||
| 417 | // POR, and must be written with a valid combination. Refer to | ||
| 418 | // RM0433 Rev 7 6.8.4. This is partially enforced by dropping | ||
| 419 | // `self` at the end of this method, but of course we cannot | ||
| 420 | // know what happened between the previous POR and here. | ||
| 421 | #[cfg(pwr_h7)] | ||
| 422 | PWR.cr3().modify(|w| { | ||
| 423 | w.set_scuen(true); | ||
| 424 | w.set_ldoen(true); | ||
| 425 | w.set_bypass(false); | ||
| 426 | }); | ||
| 427 | |||
| 428 | #[cfg(pwr_h7smps)] | ||
| 429 | PWR.cr3().modify(|w| { | ||
| 430 | // hardcode "Direct SPMS" for now, this is what works on nucleos with the | ||
| 431 | // default solderbridge configuration. | ||
| 432 | w.set_sden(true); | ||
| 433 | w.set_ldoen(false); | ||
| 434 | }); | ||
| 435 | |||
| 436 | // Validate the supply configuration. If you are stuck here, it is | ||
| 437 | // because the voltages on your board do not match those specified | ||
| 438 | // in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset | ||
| 439 | // VOS = Scale 3, so check that the voltage on the VCAP pins = | ||
| 440 | // 1.0V. | ||
| 441 | while !PWR.csr1().read().actvosrdy() {} | ||
| 442 | |||
| 443 | // Go to Scale 1 | ||
| 444 | PWR.d3cr().modify(|w| w.set_vos(0b11)); | ||
| 445 | while !PWR.d3cr().read().vosrdy() {} | ||
| 446 | |||
| 447 | let pwr_vos = if !enable_overdrive { | ||
| 448 | VoltageScale::Scale1 | ||
| 449 | } else { | ||
| 450 | critical_section::with(|_| { | ||
| 451 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | ||
| 452 | |||
| 453 | SYSCFG.pwrcr().modify(|w| w.set_oden(1)); | ||
| 454 | }); | ||
| 455 | while !PWR.d3cr().read().vosrdy() {} | ||
| 456 | VoltageScale::Scale0 | ||
| 457 | }; | ||
| 458 | |||
| 459 | // Freeze the core clocks, returning a Core Clocks Distribution | ||
| 460 | // and Reset (CCDR) structure. The actual frequency of the clocks | ||
| 461 | // configured is returned in the `clocks` member of the CCDR | ||
| 462 | // structure. | ||
| 463 | // | ||
| 464 | // Note that `freeze` will never result in a clock _faster_ than | ||
| 465 | // that specified. It may result in a clock that is a factor of [1, | ||
| 466 | // 2) slower. | ||
| 467 | // | ||
| 468 | // `syscfg` is required to enable the I/O compensation cell. | ||
| 469 | // | ||
| 470 | // # Panics | ||
| 471 | // | ||
| 472 | // If a clock specification cannot be achieved within the | ||
| 473 | // hardware specification then this function will panic. This | ||
| 474 | // function may also panic if a clock specification can be | ||
| 475 | // achieved, but the mechanism for doing so is not yet | ||
| 476 | // implemented here. | ||
| 477 | |||
| 478 | let srcclk = config.hse.unwrap_or(HSI); // Available clocks | ||
| 479 | let (sys_ck, sys_use_pll1_p) = sys_ck_setup(&mut config, srcclk); | ||
| 480 | |||
| 481 | // Configure traceclk from PLL if needed | ||
| 482 | traceclk_setup(&mut config, sys_use_pll1_p); | ||
| 483 | |||
| 484 | // NOTE(unsafe) We have exclusive access to the RCC | ||
| 485 | let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0); | ||
| 486 | let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1); | ||
| 487 | let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2); | ||
| 488 | |||
| 489 | let sys_ck = if sys_use_pll1_p { | ||
| 490 | Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup | ||
| 491 | } else { | ||
| 492 | sys_ck | ||
| 493 | }; | ||
| 494 | |||
| 495 | // This routine does not support HSIDIV != 1. To | ||
| 496 | // do so it would need to ensure all PLLxON bits are clear | ||
| 497 | // before changing the value of HSIDIV | ||
| 498 | let cr = RCC.cr().read(); | ||
| 499 | assert!(cr.hsion()); | ||
| 500 | assert!(cr.hsidiv() == Hsidiv::DIV1); | ||
| 501 | |||
| 502 | RCC.csr().modify(|w| w.set_lsion(true)); | ||
| 503 | while !RCC.csr().read().lsirdy() {} | ||
| 504 | |||
| 505 | // per_ck from HSI by default | ||
| 506 | let (per_ck, ckpersel) = match (config.per_ck == config.hse, config.per_ck) { | ||
| 507 | (true, Some(hse)) => (hse, Ckpersel::HSE), // HSE | ||
| 508 | (_, Some(CSI)) => (CSI, Ckpersel::CSI), // CSI | ||
| 509 | _ => (HSI, Ckpersel::HSI), // HSI | ||
| 510 | }; | ||
| 511 | |||
| 512 | // D1 Core Prescaler | ||
| 513 | // Set to 1 | ||
| 514 | let d1cpre_bits = 0; | ||
| 515 | let d1cpre_div = 1; | ||
| 516 | let sys_d1cpre_ck = sys_ck.0 / d1cpre_div; | ||
| 517 | |||
| 518 | // Refer to part datasheet "General operating conditions" | ||
| 519 | // table for (rev V). We do not assert checks for earlier | ||
| 520 | // revisions which may have lower limits. | ||
| 521 | let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match pwr_vos { | ||
| 522 | VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000), | ||
| 523 | VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000), | ||
| 524 | VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000), | ||
| 525 | _ => (200_000_000, 100_000_000, 50_000_000), | ||
| 526 | }; | ||
| 527 | assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max); | ||
| 528 | |||
| 529 | let rcc_hclk = config.rcc_hclk.map(|v| v.0).unwrap_or(sys_d1cpre_ck / 2); | ||
| 530 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 531 | |||
| 532 | // Estimate divisor | ||
| 533 | let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk { | ||
| 534 | 0 => panic!(), | ||
| 535 | 1 => (Hpre::DIV1, 1), | ||
| 536 | 2 => (Hpre::DIV2, 2), | ||
| 537 | 3..=5 => (Hpre::DIV4, 4), | ||
| 538 | 6..=11 => (Hpre::DIV8, 8), | ||
| 539 | 12..=39 => (Hpre::DIV16, 16), | ||
| 540 | 40..=95 => (Hpre::DIV64, 64), | ||
| 541 | 96..=191 => (Hpre::DIV128, 128), | ||
| 542 | 192..=383 => (Hpre::DIV256, 256), | ||
| 543 | _ => (Hpre::DIV512, 512), | ||
| 544 | }; | ||
| 545 | // Calculate real AXI and AHB clock | ||
| 546 | let rcc_hclk = sys_d1cpre_ck / hpre_div; | ||
| 547 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 548 | let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7 | ||
| 549 | // Timer prescaler selection | ||
| 550 | let timpre = Timpre::DEFAULTX2; | ||
| 551 | |||
| 552 | let requested_pclk1 = config | ||
| 553 | .pclk1 | ||
| 554 | .map(|v| v.0) | ||
| 555 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 556 | let (rcc_pclk1, ppre1_bits, ppre1, rcc_timerx_ker_ck) = | ||
| 557 | ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre)); | ||
| 558 | |||
| 559 | let requested_pclk2 = config | ||
| 560 | .pclk2 | ||
| 561 | .map(|v| v.0) | ||
| 562 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 563 | let (rcc_pclk2, ppre2_bits, ppre2, rcc_timery_ker_ck) = | ||
| 564 | ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre)); | ||
| 565 | |||
| 566 | let requested_pclk3 = config | ||
| 567 | .pclk3 | ||
| 568 | .map(|v| v.0) | ||
| 569 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 570 | let (rcc_pclk3, ppre3_bits, ppre3, _) = | ||
| 571 | ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None); | ||
| 572 | |||
| 573 | let requested_pclk4 = config | ||
| 574 | .pclk4 | ||
| 575 | .map(|v| v.0) | ||
| 576 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 577 | let (rcc_pclk4, ppre4_bits, ppre4, _) = | ||
| 578 | ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None); | ||
| 579 | |||
| 580 | flash_setup(rcc_aclk, pwr_vos); | ||
| 581 | |||
| 582 | // Start switching clocks ------------------- | ||
| 583 | |||
| 584 | // Ensure CSI is on and stable | ||
| 585 | RCC.cr().modify(|w| w.set_csion(true)); | ||
| 586 | while !RCC.cr().read().csirdy() {} | ||
| 587 | |||
| 588 | // Ensure HSI48 is on and stable | ||
| 589 | RCC.cr().modify(|w| w.set_hsi48on(true)); | ||
| 590 | while !RCC.cr().read().hsi48on() {} | ||
| 591 | |||
| 592 | // XXX: support MCO ? | ||
| 593 | |||
| 594 | let hse_ck = match config.hse { | ||
| 595 | Some(hse) => { | ||
| 596 | // Ensure HSE is on and stable | ||
| 597 | RCC.cr().modify(|w| { | ||
| 598 | w.set_hseon(true); | ||
| 599 | w.set_hsebyp(if config.bypass_hse { | ||
| 600 | Hsebyp::BYPASSED | ||
| 601 | } else { | ||
| 602 | Hsebyp::NOTBYPASSED | ||
| 603 | }); | ||
| 604 | }); | ||
| 605 | while !RCC.cr().read().hserdy() {} | ||
| 606 | Some(hse) | ||
| 607 | } | ||
| 608 | None => None, | ||
| 609 | }; | ||
| 610 | |||
| 611 | let pllsrc = if config.hse.is_some() { | ||
| 612 | Pllsrc::HSE | ||
| 613 | } else { | ||
| 614 | Pllsrc::HSI | ||
| 615 | }; | ||
| 616 | RCC.pllckselr().modify(|w| w.set_pllsrc(pllsrc)); | ||
| 617 | |||
| 618 | let enable_pll = |pll| { | ||
| 619 | RCC.cr().modify(|w| w.set_pllon(pll, true)); | ||
| 620 | while !RCC.cr().read().pllrdy(pll) {} | ||
| 621 | }; | ||
| 622 | |||
| 623 | if pll1_p_ck.is_some() { | ||
| 624 | enable_pll(0); | ||
| 625 | } | ||
| 626 | |||
| 627 | if pll2_p_ck.is_some() { | ||
| 628 | enable_pll(1); | ||
| 629 | } | ||
| 630 | |||
| 631 | if pll3_p_ck.is_some() { | ||
| 632 | enable_pll(2); | ||
| 633 | } | ||
| 634 | |||
| 635 | // Core Prescaler / AHB Prescaler / APB3 Prescaler | ||
| 636 | RCC.d1cfgr().modify(|w| { | ||
| 637 | w.set_d1cpre(Hpre(d1cpre_bits)); | ||
| 638 | w.set_d1ppre(Dppre(ppre3_bits)); | ||
| 639 | w.set_hpre(hpre_bits) | ||
| 640 | }); | ||
| 641 | // Ensure core prescaler value is valid before future lower | ||
| 642 | // core voltage | ||
| 643 | while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {} | ||
| 644 | |||
| 645 | // APB1 / APB2 Prescaler | ||
| 646 | RCC.d2cfgr().modify(|w| { | ||
| 647 | w.set_d2ppre1(Dppre(ppre1_bits)); | ||
| 648 | w.set_d2ppre2(Dppre(ppre2_bits)); | ||
| 649 | }); | ||
| 650 | |||
| 651 | // APB4 Prescaler | ||
| 652 | RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits))); | ||
| 653 | |||
| 654 | // Peripheral Clock (per_ck) | ||
| 655 | RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel)); | ||
| 656 | |||
| 657 | // Set timer clocks prescaler setting | ||
| 658 | RCC.cfgr().modify(|w| w.set_timpre(timpre)); | ||
| 659 | |||
| 660 | // Select system clock source | ||
| 661 | let sw = match (sys_use_pll1_p, config.hse.is_some()) { | ||
| 662 | (true, _) => Sw::PLL1, | ||
| 663 | (false, true) => Sw::HSE, | ||
| 664 | _ => Sw::HSI, | ||
| 665 | }; | ||
| 666 | RCC.cfgr().modify(|w| w.set_sw(sw)); | ||
| 667 | while RCC.cfgr().read().sws() != sw.0 {} | ||
| 668 | |||
| 669 | // IO compensation cell - Requires CSI clock and SYSCFG | ||
| 670 | assert!(RCC.cr().read().csirdy()); | ||
| 671 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | ||
| 672 | |||
| 673 | // Enable the compensation cell, using back-bias voltage code | ||
| 674 | // provide by the cell. | ||
| 675 | critical_section::with(|_| { | ||
| 676 | SYSCFG.cccsr().modify(|w| { | ||
| 677 | w.set_en(true); | ||
| 678 | w.set_cs(false); | ||
| 679 | w.set_hslv(false); | ||
| 680 | }) | ||
| 681 | }); | ||
| 682 | while !SYSCFG.cccsr().read().ready() {} | ||
| 683 | |||
| 684 | let core_clocks = CoreClocks { | ||
| 685 | hclk: Hertz(rcc_hclk), | ||
| 686 | pclk1: Hertz(rcc_pclk1), | ||
| 687 | pclk2: Hertz(rcc_pclk2), | ||
| 688 | pclk3: Hertz(rcc_pclk3), | ||
| 689 | pclk4: Hertz(rcc_pclk4), | ||
| 690 | ppre1, | ||
| 691 | ppre2, | ||
| 692 | ppre3, | ||
| 693 | ppre4, | ||
| 694 | csi_ck: Some(CSI), | ||
| 695 | hsi_ck: Some(HSI), | ||
| 696 | hsi48_ck: Some(HSI48), | ||
| 697 | lsi_ck: Some(LSI), | ||
| 698 | per_ck: Some(per_ck), | ||
| 699 | hse_ck, | ||
| 700 | pll1_p_ck: pll1_p_ck.map(Hertz), | ||
| 701 | pll1_q_ck: pll1_q_ck.map(Hertz), | ||
| 702 | pll1_r_ck: pll1_r_ck.map(Hertz), | ||
| 703 | pll2_p_ck: pll2_p_ck.map(Hertz), | ||
| 704 | pll2_q_ck: pll2_q_ck.map(Hertz), | ||
| 705 | pll2_r_ck: pll2_r_ck.map(Hertz), | ||
| 706 | pll3_p_ck: pll3_p_ck.map(Hertz), | ||
| 707 | pll3_q_ck: pll3_q_ck.map(Hertz), | ||
| 708 | pll3_r_ck: pll3_r_ck.map(Hertz), | ||
| 709 | timx_ker_ck: rcc_timerx_ker_ck.map(Hertz), | ||
| 710 | timy_ker_ck: rcc_timery_ker_ck.map(Hertz), | ||
| 711 | sys_ck, | ||
| 712 | c_ck: Hertz(sys_d1cpre_ck), | ||
| 713 | }; | ||
| 714 | |||
| 715 | set_freqs(Clocks { | ||
| 716 | sys: core_clocks.c_ck, | ||
| 717 | ahb1: core_clocks.hclk, | ||
| 718 | ahb2: core_clocks.hclk, | ||
| 719 | ahb3: core_clocks.hclk, | ||
| 720 | ahb4: core_clocks.hclk, | ||
| 721 | apb1: core_clocks.pclk1, | ||
| 722 | apb2: core_clocks.pclk2, | ||
| 723 | apb4: core_clocks.pclk4, | ||
| 724 | apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1), | ||
| 725 | apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2), | ||
| 726 | }); | ||
| 727 | } | ||
| 728 | |||
| 729 | mod pll { | ||
| 730 | use super::{Hertz, RCC}; | ||
| 731 | |||
| 732 | const VCO_MIN: u32 = 150_000_000; | ||
| 733 | const VCO_MAX: u32 = 420_000_000; | ||
| 734 | |||
| 735 | #[derive(Default)] | ||
| 736 | pub struct PllConfig { | ||
| 737 | pub p_ck: Option<Hertz>, | ||
| 738 | pub q_ck: Option<Hertz>, | ||
| 739 | pub r_ck: Option<Hertz>, | ||
| 740 | } | ||
| 741 | |||
| 742 | pub(super) struct PllConfigResults { | ||
| 743 | pub ref_x_ck: u32, | ||
| 744 | pub pll_x_m: u32, | ||
| 745 | pub pll_x_p: u32, | ||
| 746 | pub vco_ck_target: u32, | ||
| 747 | } | ||
| 748 | |||
| 749 | fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) { | ||
| 750 | let pll_x_p = if plln == 0 { | ||
| 751 | if output > VCO_MAX / 2 { | ||
| 752 | 1 | ||
| 753 | } else { | ||
| 754 | ((VCO_MAX / output) | 1) - 1 // Must be even or unity | ||
| 755 | } | ||
| 756 | } else { | ||
| 757 | // Specific to PLL2/3, will subtract 1 later | ||
| 758 | if output > VCO_MAX / 2 { | ||
| 759 | 1 | ||
| 760 | } else { | ||
| 761 | VCO_MAX / output | ||
| 762 | } | ||
| 763 | }; | ||
| 764 | |||
| 765 | let vco_ck = output + pll_x_p; | ||
| 766 | |||
| 767 | assert!(pll_x_p < 128); | ||
| 768 | assert!(vco_ck >= VCO_MIN); | ||
| 769 | assert!(vco_ck <= VCO_MAX); | ||
| 770 | |||
| 771 | (vco_ck, pll_x_p) | ||
| 772 | } | ||
| 773 | |||
| 774 | /// # Safety | ||
| 775 | /// | ||
| 776 | /// Must have exclusive access to the RCC register block | ||
| 777 | unsafe fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults { | ||
| 778 | use crate::pac::rcc::vals::{Pllrge, Pllvcosel}; | ||
| 779 | |||
| 780 | let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln); | ||
| 781 | |||
| 782 | // Input divisor, resulting in a reference clock in the range | ||
| 783 | // 1 to 2 MHz. Choose the highest reference clock (lowest m) | ||
| 784 | let pll_x_m = (pll_src + 1_999_999) / 2_000_000; | ||
| 785 | assert!(pll_x_m < 64); | ||
| 786 | |||
| 787 | // Calculate resulting reference clock | ||
| 788 | let ref_x_ck = pll_src / pll_x_m; | ||
| 789 | assert!((1_000_000..=2_000_000).contains(&ref_x_ck)); | ||
| 790 | |||
| 791 | RCC.pllcfgr().modify(|w| { | ||
| 792 | w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO); | ||
| 793 | w.set_pllrge(plln, Pllrge::RANGE1); | ||
| 794 | }); | ||
| 795 | PllConfigResults { | ||
| 796 | ref_x_ck, | ||
| 797 | pll_x_m, | ||
| 798 | pll_x_p, | ||
| 799 | vco_ck_target, | ||
| 800 | } | ||
| 801 | } | ||
| 802 | |||
| 803 | /// # Safety | ||
| 804 | /// | ||
| 805 | /// Must have exclusive access to the RCC register block | ||
| 806 | pub(super) unsafe fn pll_setup( | ||
| 807 | pll_src: u32, | ||
| 808 | config: &PllConfig, | ||
| 809 | plln: usize, | ||
| 810 | ) -> (Option<u32>, Option<u32>, Option<u32>) { | ||
| 811 | use crate::pac::rcc::vals::Divp; | ||
| 812 | |||
| 813 | match config.p_ck { | ||
| 814 | Some(requested_output) => { | ||
| 815 | let config_results = vco_setup(pll_src, requested_output.0, plln); | ||
| 816 | let PllConfigResults { | ||
| 817 | ref_x_ck, | ||
| 818 | pll_x_m, | ||
| 819 | pll_x_p, | ||
| 820 | vco_ck_target, | ||
| 821 | } = config_results; | ||
| 822 | |||
| 823 | RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8)); | ||
| 824 | |||
| 825 | // Feedback divider. Integer only | ||
| 826 | let pll_x_n = vco_ck_target / ref_x_ck; | ||
| 827 | assert!(pll_x_n >= 4); | ||
| 828 | assert!(pll_x_n <= 512); | ||
| 829 | RCC.plldivr(plln) | ||
| 830 | .modify(|w| w.set_divn1((pll_x_n - 1) as u16)); | ||
| 831 | |||
| 832 | // No FRACN | ||
| 833 | RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false)); | ||
| 834 | let vco_ck = ref_x_ck * pll_x_n; | ||
| 835 | |||
| 836 | RCC.plldivr(plln) | ||
| 837 | .modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8))); | ||
| 838 | RCC.pllcfgr().modify(|w| w.set_divpen(plln, true)); | ||
| 839 | |||
| 840 | // Calulate additional output dividers | ||
| 841 | let q_ck = match config.q_ck { | ||
| 842 | Some(Hertz(ck)) if ck > 0 => { | ||
| 843 | let div = (vco_ck + ck - 1) / ck; | ||
| 844 | RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8)); | ||
| 845 | RCC.pllcfgr().modify(|w| w.set_divqen(plln, true)); | ||
| 846 | Some(vco_ck / div) | ||
| 847 | } | ||
| 848 | _ => None, | ||
| 849 | }; | ||
| 850 | let r_ck = match config.r_ck { | ||
| 851 | Some(Hertz(ck)) if ck > 0 => { | ||
| 852 | let div = (vco_ck + ck - 1) / ck; | ||
| 853 | RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8)); | ||
| 854 | RCC.pllcfgr().modify(|w| w.set_divren(plln, true)); | ||
| 855 | Some(vco_ck / div) | ||
| 856 | } | ||
| 857 | _ => None, | ||
| 858 | }; | ||
| 859 | |||
| 860 | (Some(vco_ck / pll_x_p), q_ck, r_ck) | ||
| 861 | } | ||
| 862 | None => { | ||
| 863 | assert!( | ||
| 864 | config.q_ck.is_none(), | ||
| 865 | "Must set PLL P clock for Q clock to take effect!" | ||
| 866 | ); | ||
| 867 | assert!( | ||
| 868 | config.r_ck.is_none(), | ||
| 869 | "Must set PLL P clock for R clock to take effect!" | ||
| 870 | ); | ||
| 871 | (None, None, None) | ||
| 872 | } | ||
| 873 | } | ||
| 874 | } | ||
| 875 | } | ||
diff --git a/embassy-stm32/src/rcc/h7/mod.rs b/embassy-stm32/src/rcc/h7/mod.rs deleted file mode 100644 index cd493a80a..000000000 --- a/embassy-stm32/src/rcc/h7/mod.rs +++ /dev/null | |||
| @@ -1,702 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy::util::Unborrow; | ||
| 4 | use embassy_hal_common::unborrow; | ||
| 5 | use stm32_metapac::rcc::vals::{Mco1, Mco2}; | ||
| 6 | |||
| 7 | use crate::gpio::sealed::Pin as __GpioPin; | ||
| 8 | use crate::gpio::Pin; | ||
| 9 | use crate::pac::rcc::vals::Timpre; | ||
| 10 | use crate::pac::{RCC, SYSCFG}; | ||
| 11 | use crate::peripherals; | ||
| 12 | use crate::pwr::{Power, VoltageScale}; | ||
| 13 | use crate::rcc::{set_freqs, Clocks}; | ||
| 14 | use crate::time::Hertz; | ||
| 15 | |||
| 16 | mod pll; | ||
| 17 | use pll::pll_setup; | ||
| 18 | pub use pll::PllConfig; | ||
| 19 | |||
| 20 | const HSI: Hertz = Hertz(64_000_000); | ||
| 21 | const CSI: Hertz = Hertz(4_000_000); | ||
| 22 | const HSI48: Hertz = Hertz(48_000_000); | ||
| 23 | const LSI: Hertz = Hertz(32_000); | ||
| 24 | |||
| 25 | /// Core clock frequencies | ||
| 26 | #[derive(Clone, Copy)] | ||
| 27 | pub struct CoreClocks { | ||
| 28 | pub hclk: Hertz, | ||
| 29 | pub pclk1: Hertz, | ||
| 30 | pub pclk2: Hertz, | ||
| 31 | pub pclk3: Hertz, | ||
| 32 | pub pclk4: Hertz, | ||
| 33 | pub ppre1: u8, | ||
| 34 | pub ppre2: u8, | ||
| 35 | pub ppre3: u8, | ||
| 36 | pub ppre4: u8, | ||
| 37 | pub csi_ck: Option<Hertz>, | ||
| 38 | pub hsi_ck: Option<Hertz>, | ||
| 39 | pub hsi48_ck: Option<Hertz>, | ||
| 40 | pub lsi_ck: Option<Hertz>, | ||
| 41 | pub per_ck: Option<Hertz>, | ||
| 42 | pub hse_ck: Option<Hertz>, | ||
| 43 | pub pll1_p_ck: Option<Hertz>, | ||
| 44 | pub pll1_q_ck: Option<Hertz>, | ||
| 45 | pub pll1_r_ck: Option<Hertz>, | ||
| 46 | pub pll2_p_ck: Option<Hertz>, | ||
| 47 | pub pll2_q_ck: Option<Hertz>, | ||
| 48 | pub pll2_r_ck: Option<Hertz>, | ||
| 49 | pub pll3_p_ck: Option<Hertz>, | ||
| 50 | pub pll3_q_ck: Option<Hertz>, | ||
| 51 | pub pll3_r_ck: Option<Hertz>, | ||
| 52 | pub timx_ker_ck: Option<Hertz>, | ||
| 53 | pub timy_ker_ck: Option<Hertz>, | ||
| 54 | pub sys_ck: Hertz, | ||
| 55 | pub c_ck: Hertz, | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Configuration of the core clocks | ||
| 59 | #[non_exhaustive] | ||
| 60 | #[derive(Default)] | ||
| 61 | pub struct Config { | ||
| 62 | pub hse: Option<Hertz>, | ||
| 63 | pub bypass_hse: bool, | ||
| 64 | pub sys_ck: Option<Hertz>, | ||
| 65 | pub per_ck: Option<Hertz>, | ||
| 66 | rcc_hclk: Option<Hertz>, | ||
| 67 | pub hclk: Option<Hertz>, | ||
| 68 | pub pclk1: Option<Hertz>, | ||
| 69 | pub pclk2: Option<Hertz>, | ||
| 70 | pub pclk3: Option<Hertz>, | ||
| 71 | pub pclk4: Option<Hertz>, | ||
| 72 | pub pll1: PllConfig, | ||
| 73 | pub pll2: PllConfig, | ||
| 74 | pub pll3: PllConfig, | ||
| 75 | } | ||
| 76 | |||
| 77 | pub struct Rcc<'d> { | ||
| 78 | inner: PhantomData<&'d ()>, | ||
| 79 | config: Config, | ||
| 80 | } | ||
| 81 | |||
| 82 | impl<'d> Rcc<'d> { | ||
| 83 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 84 | Self { | ||
| 85 | inner: PhantomData, | ||
| 86 | config, | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Freeze the core clocks, returning a Core Clocks Distribution | ||
| 91 | /// and Reset (CCDR) structure. The actual frequency of the clocks | ||
| 92 | /// configured is returned in the `clocks` member of the CCDR | ||
| 93 | /// structure. | ||
| 94 | /// | ||
| 95 | /// Note that `freeze` will never result in a clock _faster_ than | ||
| 96 | /// that specified. It may result in a clock that is a factor of [1, | ||
| 97 | /// 2) slower. | ||
| 98 | /// | ||
| 99 | /// `syscfg` is required to enable the I/O compensation cell. | ||
| 100 | /// | ||
| 101 | /// # Panics | ||
| 102 | /// | ||
| 103 | /// If a clock specification cannot be achieved within the | ||
| 104 | /// hardware specification then this function will panic. This | ||
| 105 | /// function may also panic if a clock specification can be | ||
| 106 | /// achieved, but the mechanism for doing so is not yet | ||
| 107 | /// implemented here. | ||
| 108 | pub fn freeze(mut self, pwr: &Power) -> CoreClocks { | ||
| 109 | use crate::pac::rcc::vals::{Ckpersel, Dppre, Hpre, Hsebyp, Hsidiv, Pllsrc, Sw}; | ||
| 110 | |||
| 111 | let srcclk = self.config.hse.unwrap_or(HSI); // Available clocks | ||
| 112 | let (sys_ck, sys_use_pll1_p) = self.sys_ck_setup(srcclk); | ||
| 113 | |||
| 114 | // Configure traceclk from PLL if needed | ||
| 115 | self.traceclk_setup(sys_use_pll1_p); | ||
| 116 | |||
| 117 | // NOTE(unsafe) We have exclusive access to the RCC | ||
| 118 | let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = | ||
| 119 | unsafe { pll_setup(srcclk.0, &self.config.pll1, 0) }; | ||
| 120 | let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = | ||
| 121 | unsafe { pll_setup(srcclk.0, &self.config.pll2, 1) }; | ||
| 122 | let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = | ||
| 123 | unsafe { pll_setup(srcclk.0, &self.config.pll3, 2) }; | ||
| 124 | |||
| 125 | let sys_ck = if sys_use_pll1_p { | ||
| 126 | Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup | ||
| 127 | } else { | ||
| 128 | sys_ck | ||
| 129 | }; | ||
| 130 | |||
| 131 | // NOTE(unsafe) We own the regblock | ||
| 132 | unsafe { | ||
| 133 | // This routine does not support HSIDIV != 1. To | ||
| 134 | // do so it would need to ensure all PLLxON bits are clear | ||
| 135 | // before changing the value of HSIDIV | ||
| 136 | let cr = RCC.cr().read(); | ||
| 137 | assert!(cr.hsion()); | ||
| 138 | assert!(cr.hsidiv() == Hsidiv::DIV1); | ||
| 139 | |||
| 140 | RCC.csr().modify(|w| w.set_lsion(true)); | ||
| 141 | while !RCC.csr().read().lsirdy() {} | ||
| 142 | } | ||
| 143 | |||
| 144 | // per_ck from HSI by default | ||
| 145 | let (per_ck, ckpersel) = match (self.config.per_ck == self.config.hse, self.config.per_ck) { | ||
| 146 | (true, Some(hse)) => (hse, Ckpersel::HSE), // HSE | ||
| 147 | (_, Some(CSI)) => (CSI, Ckpersel::CSI), // CSI | ||
| 148 | _ => (HSI, Ckpersel::HSI), // HSI | ||
| 149 | }; | ||
| 150 | |||
| 151 | // D1 Core Prescaler | ||
| 152 | // Set to 1 | ||
| 153 | let d1cpre_bits = 0; | ||
| 154 | let d1cpre_div = 1; | ||
| 155 | let sys_d1cpre_ck = sys_ck.0 / d1cpre_div; | ||
| 156 | |||
| 157 | // Refer to part datasheet "General operating conditions" | ||
| 158 | // table for (rev V). We do not assert checks for earlier | ||
| 159 | // revisions which may have lower limits. | ||
| 160 | let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match pwr.vos { | ||
| 161 | VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000), | ||
| 162 | VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000), | ||
| 163 | VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000), | ||
| 164 | _ => (200_000_000, 100_000_000, 50_000_000), | ||
| 165 | }; | ||
| 166 | assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max); | ||
| 167 | |||
| 168 | let rcc_hclk = self | ||
| 169 | .config | ||
| 170 | .rcc_hclk | ||
| 171 | .map(|v| v.0) | ||
| 172 | .unwrap_or(sys_d1cpre_ck / 2); | ||
| 173 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 174 | |||
| 175 | // Estimate divisor | ||
| 176 | let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk { | ||
| 177 | 0 => panic!(), | ||
| 178 | 1 => (Hpre::DIV1, 1), | ||
| 179 | 2 => (Hpre::DIV2, 2), | ||
| 180 | 3..=5 => (Hpre::DIV4, 4), | ||
| 181 | 6..=11 => (Hpre::DIV8, 8), | ||
| 182 | 12..=39 => (Hpre::DIV16, 16), | ||
| 183 | 40..=95 => (Hpre::DIV64, 64), | ||
| 184 | 96..=191 => (Hpre::DIV128, 128), | ||
| 185 | 192..=383 => (Hpre::DIV256, 256), | ||
| 186 | _ => (Hpre::DIV512, 512), | ||
| 187 | }; | ||
| 188 | // Calculate real AXI and AHB clock | ||
| 189 | let rcc_hclk = sys_d1cpre_ck / hpre_div; | ||
| 190 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 191 | let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7 | ||
| 192 | // Timer prescaler selection | ||
| 193 | let timpre = Timpre::DEFAULTX2; | ||
| 194 | |||
| 195 | let requested_pclk1 = self | ||
| 196 | .config | ||
| 197 | .pclk1 | ||
| 198 | .map(|v| v.0) | ||
| 199 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 200 | let (rcc_pclk1, ppre1_bits, ppre1, rcc_timerx_ker_ck) = | ||
| 201 | Self::ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre)); | ||
| 202 | |||
| 203 | let requested_pclk2 = self | ||
| 204 | .config | ||
| 205 | .pclk2 | ||
| 206 | .map(|v| v.0) | ||
| 207 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 208 | let (rcc_pclk2, ppre2_bits, ppre2, rcc_timery_ker_ck) = | ||
| 209 | Self::ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre)); | ||
| 210 | |||
| 211 | let requested_pclk3 = self | ||
| 212 | .config | ||
| 213 | .pclk3 | ||
| 214 | .map(|v| v.0) | ||
| 215 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 216 | let (rcc_pclk3, ppre3_bits, ppre3, _) = | ||
| 217 | Self::ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None); | ||
| 218 | |||
| 219 | let requested_pclk4 = self | ||
| 220 | .config | ||
| 221 | .pclk4 | ||
| 222 | .map(|v| v.0) | ||
| 223 | .unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); | ||
| 224 | let (rcc_pclk4, ppre4_bits, ppre4, _) = | ||
| 225 | Self::ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None); | ||
| 226 | |||
| 227 | Self::flash_setup(rcc_aclk, pwr.vos); | ||
| 228 | |||
| 229 | // Start switching clocks ------------------- | ||
| 230 | // NOTE(unsafe) We have the RCC singleton | ||
| 231 | unsafe { | ||
| 232 | // Ensure CSI is on and stable | ||
| 233 | RCC.cr().modify(|w| w.set_csion(true)); | ||
| 234 | while !RCC.cr().read().csirdy() {} | ||
| 235 | |||
| 236 | // Ensure HSI48 is on and stable | ||
| 237 | RCC.cr().modify(|w| w.set_hsi48on(true)); | ||
| 238 | while !RCC.cr().read().hsi48on() {} | ||
| 239 | |||
| 240 | // XXX: support MCO ? | ||
| 241 | |||
| 242 | let hse_ck = match self.config.hse { | ||
| 243 | Some(hse) => { | ||
| 244 | // Ensure HSE is on and stable | ||
| 245 | RCC.cr().modify(|w| { | ||
| 246 | w.set_hseon(true); | ||
| 247 | w.set_hsebyp(if self.config.bypass_hse { | ||
| 248 | Hsebyp::BYPASSED | ||
| 249 | } else { | ||
| 250 | Hsebyp::NOTBYPASSED | ||
| 251 | }); | ||
| 252 | }); | ||
| 253 | while !RCC.cr().read().hserdy() {} | ||
| 254 | Some(hse) | ||
| 255 | } | ||
| 256 | None => None, | ||
| 257 | }; | ||
| 258 | |||
| 259 | let pllsrc = if self.config.hse.is_some() { | ||
| 260 | Pllsrc::HSE | ||
| 261 | } else { | ||
| 262 | Pllsrc::HSI | ||
| 263 | }; | ||
| 264 | RCC.pllckselr().modify(|w| w.set_pllsrc(pllsrc)); | ||
| 265 | |||
| 266 | let enable_pll = |pll| { | ||
| 267 | RCC.cr().modify(|w| w.set_pllon(pll, true)); | ||
| 268 | while !RCC.cr().read().pllrdy(pll) {} | ||
| 269 | }; | ||
| 270 | |||
| 271 | if pll1_p_ck.is_some() { | ||
| 272 | enable_pll(0); | ||
| 273 | } | ||
| 274 | |||
| 275 | if pll2_p_ck.is_some() { | ||
| 276 | enable_pll(1); | ||
| 277 | } | ||
| 278 | |||
| 279 | if pll3_p_ck.is_some() { | ||
| 280 | enable_pll(2); | ||
| 281 | } | ||
| 282 | |||
| 283 | // Core Prescaler / AHB Prescaler / APB3 Prescaler | ||
| 284 | RCC.d1cfgr().modify(|w| { | ||
| 285 | w.set_d1cpre(Hpre(d1cpre_bits)); | ||
| 286 | w.set_d1ppre(Dppre(ppre3_bits)); | ||
| 287 | w.set_hpre(hpre_bits) | ||
| 288 | }); | ||
| 289 | // Ensure core prescaler value is valid before future lower | ||
| 290 | // core voltage | ||
| 291 | while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {} | ||
| 292 | |||
| 293 | // APB1 / APB2 Prescaler | ||
| 294 | RCC.d2cfgr().modify(|w| { | ||
| 295 | w.set_d2ppre1(Dppre(ppre1_bits)); | ||
| 296 | w.set_d2ppre2(Dppre(ppre2_bits)); | ||
| 297 | }); | ||
| 298 | |||
| 299 | // APB4 Prescaler | ||
| 300 | RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits))); | ||
| 301 | |||
| 302 | // Peripheral Clock (per_ck) | ||
| 303 | RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel)); | ||
| 304 | |||
| 305 | // Set timer clocks prescaler setting | ||
| 306 | RCC.cfgr().modify(|w| w.set_timpre(timpre)); | ||
| 307 | |||
| 308 | // Select system clock source | ||
| 309 | let sw = match (sys_use_pll1_p, self.config.hse.is_some()) { | ||
| 310 | (true, _) => Sw::PLL1, | ||
| 311 | (false, true) => Sw::HSE, | ||
| 312 | _ => Sw::HSI, | ||
| 313 | }; | ||
| 314 | RCC.cfgr().modify(|w| w.set_sw(sw)); | ||
| 315 | while RCC.cfgr().read().sws() != sw.0 {} | ||
| 316 | |||
| 317 | // IO compensation cell - Requires CSI clock and SYSCFG | ||
| 318 | assert!(RCC.cr().read().csirdy()); | ||
| 319 | RCC.apb4enr().modify(|w| w.set_syscfgen(true)); | ||
| 320 | |||
| 321 | // Enable the compensation cell, using back-bias voltage code | ||
| 322 | // provide by the cell. | ||
| 323 | critical_section::with(|_| { | ||
| 324 | SYSCFG.cccsr().modify(|w| { | ||
| 325 | w.set_en(true); | ||
| 326 | w.set_cs(false); | ||
| 327 | w.set_hslv(false); | ||
| 328 | }) | ||
| 329 | }); | ||
| 330 | while !SYSCFG.cccsr().read().ready() {} | ||
| 331 | |||
| 332 | CoreClocks { | ||
| 333 | hclk: Hertz(rcc_hclk), | ||
| 334 | pclk1: Hertz(rcc_pclk1), | ||
| 335 | pclk2: Hertz(rcc_pclk2), | ||
| 336 | pclk3: Hertz(rcc_pclk3), | ||
| 337 | pclk4: Hertz(rcc_pclk4), | ||
| 338 | ppre1, | ||
| 339 | ppre2, | ||
| 340 | ppre3, | ||
| 341 | ppre4, | ||
| 342 | csi_ck: Some(CSI), | ||
| 343 | hsi_ck: Some(HSI), | ||
| 344 | hsi48_ck: Some(HSI48), | ||
| 345 | lsi_ck: Some(LSI), | ||
| 346 | per_ck: Some(per_ck), | ||
| 347 | hse_ck, | ||
| 348 | pll1_p_ck: pll1_p_ck.map(Hertz), | ||
| 349 | pll1_q_ck: pll1_q_ck.map(Hertz), | ||
| 350 | pll1_r_ck: pll1_r_ck.map(Hertz), | ||
| 351 | pll2_p_ck: pll2_p_ck.map(Hertz), | ||
| 352 | pll2_q_ck: pll2_q_ck.map(Hertz), | ||
| 353 | pll2_r_ck: pll2_r_ck.map(Hertz), | ||
| 354 | pll3_p_ck: pll3_p_ck.map(Hertz), | ||
| 355 | pll3_q_ck: pll3_q_ck.map(Hertz), | ||
| 356 | pll3_r_ck: pll3_r_ck.map(Hertz), | ||
| 357 | timx_ker_ck: rcc_timerx_ker_ck.map(Hertz), | ||
| 358 | timy_ker_ck: rcc_timery_ker_ck.map(Hertz), | ||
| 359 | sys_ck, | ||
| 360 | c_ck: Hertz(sys_d1cpre_ck), | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | /// Setup traceclk | ||
| 366 | /// Returns a pll1_r_ck | ||
| 367 | fn traceclk_setup(&mut self, sys_use_pll1_p: bool) { | ||
| 368 | let pll1_r_ck = match (sys_use_pll1_p, self.config.pll1.r_ck) { | ||
| 369 | // pll1_p_ck selected as system clock but pll1_r_ck not | ||
| 370 | // set. The traceclk mux is synchronous with the system | ||
| 371 | // clock mux, but has pll1_r_ck as an input. In order to | ||
| 372 | // keep traceclk running, we force a pll1_r_ck. | ||
| 373 | (true, None) => Some(Hertz(unwrap!(self.config.pll1.p_ck).0 / 2)), | ||
| 374 | |||
| 375 | // Either pll1 not selected as system clock, free choice | ||
| 376 | // of pll1_r_ck. Or pll1 is selected, assume user has set | ||
| 377 | // a suitable pll1_r_ck frequency. | ||
| 378 | _ => self.config.pll1.r_ck, | ||
| 379 | }; | ||
| 380 | self.config.pll1.r_ck = pll1_r_ck; | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Divider calculator for pclk 1 - 4 | ||
| 384 | /// | ||
| 385 | /// Returns real pclk, bits, ppre and the timer kernel clock | ||
| 386 | fn ppre_calculate( | ||
| 387 | requested_pclk: u32, | ||
| 388 | hclk: u32, | ||
| 389 | max_pclk: u32, | ||
| 390 | tim_pre: Option<Timpre>, | ||
| 391 | ) -> (u32, u8, u8, Option<u32>) { | ||
| 392 | let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk { | ||
| 393 | 0 => panic!(), | ||
| 394 | 1 => (0b000, 1), | ||
| 395 | 2 => (0b100, 2), | ||
| 396 | 3..=5 => (0b101, 4), | ||
| 397 | 6..=11 => (0b110, 8), | ||
| 398 | _ => (0b111, 16), | ||
| 399 | }; | ||
| 400 | let real_pclk = hclk / u32::from(ppre); | ||
| 401 | assert!(real_pclk <= max_pclk); | ||
| 402 | |||
| 403 | let tim_ker_clk = if let Some(tim_pre) = tim_pre { | ||
| 404 | let clk = match (bits, tim_pre) { | ||
| 405 | (0b101, Timpre::DEFAULTX2) => hclk / 2, | ||
| 406 | (0b110, Timpre::DEFAULTX4) => hclk / 2, | ||
| 407 | (0b110, Timpre::DEFAULTX2) => hclk / 4, | ||
| 408 | (0b111, Timpre::DEFAULTX4) => hclk / 4, | ||
| 409 | (0b111, Timpre::DEFAULTX2) => hclk / 8, | ||
| 410 | _ => hclk, | ||
| 411 | }; | ||
| 412 | Some(clk) | ||
| 413 | } else { | ||
| 414 | None | ||
| 415 | }; | ||
| 416 | (real_pclk, bits, ppre, tim_ker_clk) | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Setup sys_ck | ||
| 420 | /// Returns sys_ck frequency, and a pll1_p_ck | ||
| 421 | fn sys_ck_setup(&mut self, srcclk: Hertz) -> (Hertz, bool) { | ||
| 422 | // Compare available with wanted clocks | ||
| 423 | let sys_ck = self.config.sys_ck.unwrap_or(srcclk); | ||
| 424 | |||
| 425 | if sys_ck != srcclk { | ||
| 426 | // The requested system clock is not the immediately available | ||
| 427 | // HSE/HSI clock. Perhaps there are other ways of obtaining | ||
| 428 | // the requested system clock (such as `HSIDIV`) but we will | ||
| 429 | // ignore those for now. | ||
| 430 | // | ||
| 431 | // Therefore we must use pll1_p_ck | ||
| 432 | let pll1_p_ck = match self.config.pll1.p_ck { | ||
| 433 | Some(p_ck) => { | ||
| 434 | assert!(p_ck == sys_ck, | ||
| 435 | "Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck"); | ||
| 436 | Some(p_ck) | ||
| 437 | } | ||
| 438 | None => Some(sys_ck), | ||
| 439 | }; | ||
| 440 | self.config.pll1.p_ck = pll1_p_ck; | ||
| 441 | |||
| 442 | (sys_ck, true) | ||
| 443 | } else { | ||
| 444 | // sys_ck is derived directly from a source clock | ||
| 445 | // (HSE/HSI). pll1_p_ck can be as requested | ||
| 446 | (sys_ck, false) | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { | ||
| 451 | use crate::pac::FLASH; | ||
| 452 | |||
| 453 | // ACLK in MHz, round down and subtract 1 from integers. eg. | ||
| 454 | // 61_999_999 -> 61MHz | ||
| 455 | // 62_000_000 -> 61MHz | ||
| 456 | // 62_000_001 -> 62MHz | ||
| 457 | let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000; | ||
| 458 | |||
| 459 | // See RM0433 Rev 7 Table 17. FLASH recommended number of wait | ||
| 460 | // states and programming delay | ||
| 461 | let (wait_states, progr_delay) = match vos { | ||
| 462 | // VOS 0 range VCORE 1.26V - 1.40V | ||
| 463 | VoltageScale::Scale0 => match rcc_aclk_mhz { | ||
| 464 | 0..=69 => (0, 0), | ||
| 465 | 70..=139 => (1, 1), | ||
| 466 | 140..=184 => (2, 1), | ||
| 467 | 185..=209 => (2, 2), | ||
| 468 | 210..=224 => (3, 2), | ||
| 469 | 225..=239 => (4, 2), | ||
| 470 | _ => (7, 3), | ||
| 471 | }, | ||
| 472 | // VOS 1 range VCORE 1.15V - 1.26V | ||
| 473 | VoltageScale::Scale1 => match rcc_aclk_mhz { | ||
| 474 | 0..=69 => (0, 0), | ||
| 475 | 70..=139 => (1, 1), | ||
| 476 | 140..=184 => (2, 1), | ||
| 477 | 185..=209 => (2, 2), | ||
| 478 | 210..=224 => (3, 2), | ||
| 479 | _ => (7, 3), | ||
| 480 | }, | ||
| 481 | // VOS 2 range VCORE 1.05V - 1.15V | ||
| 482 | VoltageScale::Scale2 => match rcc_aclk_mhz { | ||
| 483 | 0..=54 => (0, 0), | ||
| 484 | 55..=109 => (1, 1), | ||
| 485 | 110..=164 => (2, 1), | ||
| 486 | 165..=224 => (3, 2), | ||
| 487 | _ => (7, 3), | ||
| 488 | }, | ||
| 489 | // VOS 3 range VCORE 0.95V - 1.05V | ||
| 490 | VoltageScale::Scale3 => match rcc_aclk_mhz { | ||
| 491 | 0..=44 => (0, 0), | ||
| 492 | 45..=89 => (1, 1), | ||
| 493 | 90..=134 => (2, 1), | ||
| 494 | 135..=179 => (3, 2), | ||
| 495 | 180..=224 => (4, 2), | ||
| 496 | _ => (7, 3), | ||
| 497 | }, | ||
| 498 | }; | ||
| 499 | |||
| 500 | // NOTE(unsafe) Atomic write | ||
| 501 | unsafe { | ||
| 502 | FLASH.acr().write(|w| { | ||
| 503 | w.set_wrhighfreq(progr_delay); | ||
| 504 | w.set_latency(wait_states) | ||
| 505 | }); | ||
| 506 | while FLASH.acr().read().latency() != wait_states {} | ||
| 507 | } | ||
| 508 | } | ||
| 509 | } | ||
| 510 | pub enum McoClock { | ||
| 511 | Disabled, | ||
| 512 | Bypassed, | ||
| 513 | Divided(u8), | ||
| 514 | } | ||
| 515 | |||
| 516 | impl McoClock { | ||
| 517 | fn into_raw(&self) -> u8 { | ||
| 518 | match self { | ||
| 519 | McoClock::Disabled => 0, | ||
| 520 | McoClock::Bypassed => 1, | ||
| 521 | McoClock::Divided(divisor) => { | ||
| 522 | if *divisor > 15 { | ||
| 523 | panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.") | ||
| 524 | } | ||
| 525 | *divisor | ||
| 526 | } | ||
| 527 | } | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | #[derive(Copy, Clone)] | ||
| 532 | pub enum Mco1Source { | ||
| 533 | Hsi, | ||
| 534 | Lse, | ||
| 535 | Hse, | ||
| 536 | Pll1Q, | ||
| 537 | Hsi48, | ||
| 538 | } | ||
| 539 | |||
| 540 | impl Default for Mco1Source { | ||
| 541 | fn default() -> Self { | ||
| 542 | Self::Hsi | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 | pub trait McoSource { | ||
| 547 | type Raw; | ||
| 548 | |||
| 549 | fn into_raw(&self) -> Self::Raw; | ||
| 550 | } | ||
| 551 | |||
| 552 | impl McoSource for Mco1Source { | ||
| 553 | type Raw = Mco1; | ||
| 554 | fn into_raw(&self) -> Self::Raw { | ||
| 555 | match self { | ||
| 556 | Mco1Source::Hsi => Mco1::HSI, | ||
| 557 | Mco1Source::Lse => Mco1::LSE, | ||
| 558 | Mco1Source::Hse => Mco1::HSE, | ||
| 559 | Mco1Source::Pll1Q => Mco1::PLL1_Q, | ||
| 560 | Mco1Source::Hsi48 => Mco1::HSI48, | ||
| 561 | } | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | #[derive(Copy, Clone)] | ||
| 566 | pub enum Mco2Source { | ||
| 567 | SysClk, | ||
| 568 | Pll2Q, | ||
| 569 | Hse, | ||
| 570 | Pll1Q, | ||
| 571 | Csi, | ||
| 572 | Lsi, | ||
| 573 | } | ||
| 574 | |||
| 575 | impl Default for Mco2Source { | ||
| 576 | fn default() -> Self { | ||
| 577 | Self::SysClk | ||
| 578 | } | ||
| 579 | } | ||
| 580 | |||
| 581 | impl McoSource for Mco2Source { | ||
| 582 | type Raw = Mco2; | ||
| 583 | fn into_raw(&self) -> Self::Raw { | ||
| 584 | match self { | ||
| 585 | Mco2Source::SysClk => Mco2::SYSCLK, | ||
| 586 | Mco2Source::Pll2Q => Mco2::PLL2_P, | ||
| 587 | Mco2Source::Hse => Mco2::HSE, | ||
| 588 | Mco2Source::Pll1Q => Mco2::PLL1_P, | ||
| 589 | Mco2Source::Csi => Mco2::CSI, | ||
| 590 | Mco2Source::Lsi => Mco2::LSI, | ||
| 591 | } | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | pub(crate) mod sealed { | ||
| 596 | use super::*; | ||
| 597 | |||
| 598 | pub trait McoInstance { | ||
| 599 | type Source; | ||
| 600 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); | ||
| 601 | } | ||
| 602 | |||
| 603 | pub trait McoPin<T: McoInstance>: Pin { | ||
| 604 | fn configure(&mut self); | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 609 | |||
| 610 | pub trait McoPin<T: McoInstance>: sealed::McoPin<T> + 'static {} | ||
| 611 | |||
| 612 | macro_rules! impl_peri { | ||
| 613 | ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { | ||
| 614 | impl sealed::McoInstance for peripherals::$peri { | ||
| 615 | type Source = $source; | ||
| 616 | |||
| 617 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { | ||
| 618 | RCC.cfgr().modify(|w| { | ||
| 619 | w.$set_source(source); | ||
| 620 | w.$set_prescaler(prescaler); | ||
| 621 | }); | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | impl McoInstance for peripherals::$peri {} | ||
| 626 | }; | ||
| 627 | } | ||
| 628 | |||
| 629 | impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); | ||
| 630 | impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); | ||
| 631 | |||
| 632 | macro_rules! impl_pin { | ||
| 633 | ($peri:ident, $pin:ident, $af:expr) => { | ||
| 634 | impl McoPin<peripherals::$peri> for peripherals::$pin {} | ||
| 635 | |||
| 636 | impl sealed::McoPin<peripherals::$peri> for peripherals::$pin { | ||
| 637 | fn configure(&mut self) { | ||
| 638 | critical_section::with(|_| unsafe { | ||
| 639 | self.set_as_af($af, crate::gpio::sealed::AFType::OutputPushPull); | ||
| 640 | self.block().ospeedr().modify(|w| { | ||
| 641 | w.set_ospeedr( | ||
| 642 | self.pin() as usize, | ||
| 643 | crate::pac::gpio::vals::Ospeedr::VERYHIGHSPEED, | ||
| 644 | ) | ||
| 645 | }); | ||
| 646 | }) | ||
| 647 | } | ||
| 648 | } | ||
| 649 | }; | ||
| 650 | } | ||
| 651 | |||
| 652 | crate::pac::peripheral_pins!( | ||
| 653 | ($inst:ident, rcc, RCC, $pin:ident, MCO_1, $af:expr) => { | ||
| 654 | impl_pin!(MCO1, $pin, $af); | ||
| 655 | }; | ||
| 656 | ($inst:ident, rcc, RCC, $pin:ident, MCO_2, $af:expr) => { | ||
| 657 | impl_pin!(MCO2, $pin, $af); | ||
| 658 | }; | ||
| 659 | ); | ||
| 660 | |||
| 661 | pub struct Mco<'d, T: McoInstance> { | ||
| 662 | phantom: PhantomData<&'d mut T>, | ||
| 663 | } | ||
| 664 | |||
| 665 | impl<'d, T: McoInstance> Mco<'d, T> { | ||
| 666 | pub fn new( | ||
| 667 | _peri: impl Unborrow<Target = T> + 'd, | ||
| 668 | pin: impl Unborrow<Target = impl McoPin<T>> + 'd, | ||
| 669 | source: impl McoSource<Raw = T::Source>, | ||
| 670 | prescaler: McoClock, | ||
| 671 | ) -> Self { | ||
| 672 | unborrow!(pin); | ||
| 673 | |||
| 674 | unsafe { | ||
| 675 | T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); | ||
| 676 | } | ||
| 677 | |||
| 678 | pin.configure(); | ||
| 679 | |||
| 680 | Self { | ||
| 681 | phantom: PhantomData, | ||
| 682 | } | ||
| 683 | } | ||
| 684 | } | ||
| 685 | |||
| 686 | pub unsafe fn init(config: Config) { | ||
| 687 | let mut power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal(), false); | ||
| 688 | let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config); | ||
| 689 | let core_clocks = rcc.freeze(&mut power); | ||
| 690 | set_freqs(Clocks { | ||
| 691 | sys: core_clocks.c_ck, | ||
| 692 | ahb1: core_clocks.hclk, | ||
| 693 | ahb2: core_clocks.hclk, | ||
| 694 | ahb3: core_clocks.hclk, | ||
| 695 | ahb4: core_clocks.hclk, | ||
| 696 | apb1: core_clocks.pclk1, | ||
| 697 | apb2: core_clocks.pclk2, | ||
| 698 | apb4: core_clocks.pclk4, | ||
| 699 | apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1), | ||
| 700 | apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2), | ||
| 701 | }); | ||
| 702 | } | ||
diff --git a/embassy-stm32/src/rcc/h7/pll.rs b/embassy-stm32/src/rcc/h7/pll.rs deleted file mode 100644 index d3709378b..000000000 --- a/embassy-stm32/src/rcc/h7/pll.rs +++ /dev/null | |||
| @@ -1,145 +0,0 @@ | |||
| 1 | use super::{Hertz, RCC}; | ||
| 2 | |||
| 3 | const VCO_MIN: u32 = 150_000_000; | ||
| 4 | const VCO_MAX: u32 = 420_000_000; | ||
| 5 | |||
| 6 | #[derive(Default)] | ||
| 7 | pub struct PllConfig { | ||
| 8 | pub p_ck: Option<Hertz>, | ||
| 9 | pub q_ck: Option<Hertz>, | ||
| 10 | pub r_ck: Option<Hertz>, | ||
| 11 | } | ||
| 12 | |||
| 13 | pub(super) struct PllConfigResults { | ||
| 14 | pub ref_x_ck: u32, | ||
| 15 | pub pll_x_m: u32, | ||
| 16 | pub pll_x_p: u32, | ||
| 17 | pub vco_ck_target: u32, | ||
| 18 | } | ||
| 19 | |||
| 20 | fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) { | ||
| 21 | let pll_x_p = if plln == 0 { | ||
| 22 | if output > VCO_MAX / 2 { | ||
| 23 | 1 | ||
| 24 | } else { | ||
| 25 | ((VCO_MAX / output) | 1) - 1 // Must be even or unity | ||
| 26 | } | ||
| 27 | } else { | ||
| 28 | // Specific to PLL2/3, will subtract 1 later | ||
| 29 | if output > VCO_MAX / 2 { | ||
| 30 | 1 | ||
| 31 | } else { | ||
| 32 | VCO_MAX / output | ||
| 33 | } | ||
| 34 | }; | ||
| 35 | |||
| 36 | let vco_ck = output + pll_x_p; | ||
| 37 | |||
| 38 | assert!(pll_x_p < 128); | ||
| 39 | assert!(vco_ck >= VCO_MIN); | ||
| 40 | assert!(vco_ck <= VCO_MAX); | ||
| 41 | |||
| 42 | (vco_ck, pll_x_p) | ||
| 43 | } | ||
| 44 | |||
| 45 | /// # Safety | ||
| 46 | /// | ||
| 47 | /// Must have exclusive access to the RCC register block | ||
| 48 | unsafe fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults { | ||
| 49 | use crate::pac::rcc::vals::{Pllrge, Pllvcosel}; | ||
| 50 | |||
| 51 | let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln); | ||
| 52 | |||
| 53 | // Input divisor, resulting in a reference clock in the range | ||
| 54 | // 1 to 2 MHz. Choose the highest reference clock (lowest m) | ||
| 55 | let pll_x_m = (pll_src + 1_999_999) / 2_000_000; | ||
| 56 | assert!(pll_x_m < 64); | ||
| 57 | |||
| 58 | // Calculate resulting reference clock | ||
| 59 | let ref_x_ck = pll_src / pll_x_m; | ||
| 60 | assert!((1_000_000..=2_000_000).contains(&ref_x_ck)); | ||
| 61 | |||
| 62 | RCC.pllcfgr().modify(|w| { | ||
| 63 | w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO); | ||
| 64 | w.set_pllrge(plln, Pllrge::RANGE1); | ||
| 65 | }); | ||
| 66 | PllConfigResults { | ||
| 67 | ref_x_ck, | ||
| 68 | pll_x_m, | ||
| 69 | pll_x_p, | ||
| 70 | vco_ck_target, | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /// # Safety | ||
| 75 | /// | ||
| 76 | /// Must have exclusive access to the RCC register block | ||
| 77 | pub(super) unsafe fn pll_setup( | ||
| 78 | pll_src: u32, | ||
| 79 | config: &PllConfig, | ||
| 80 | plln: usize, | ||
| 81 | ) -> (Option<u32>, Option<u32>, Option<u32>) { | ||
| 82 | use crate::pac::rcc::vals::Divp; | ||
| 83 | |||
| 84 | match config.p_ck { | ||
| 85 | Some(requested_output) => { | ||
| 86 | let config_results = vco_setup(pll_src, requested_output.0, plln); | ||
| 87 | let PllConfigResults { | ||
| 88 | ref_x_ck, | ||
| 89 | pll_x_m, | ||
| 90 | pll_x_p, | ||
| 91 | vco_ck_target, | ||
| 92 | } = config_results; | ||
| 93 | |||
| 94 | RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8)); | ||
| 95 | |||
| 96 | // Feedback divider. Integer only | ||
| 97 | let pll_x_n = vco_ck_target / ref_x_ck; | ||
| 98 | assert!(pll_x_n >= 4); | ||
| 99 | assert!(pll_x_n <= 512); | ||
| 100 | RCC.plldivr(plln) | ||
| 101 | .modify(|w| w.set_divn1((pll_x_n - 1) as u16)); | ||
| 102 | |||
| 103 | // No FRACN | ||
| 104 | RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false)); | ||
| 105 | let vco_ck = ref_x_ck * pll_x_n; | ||
| 106 | |||
| 107 | RCC.plldivr(plln) | ||
| 108 | .modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8))); | ||
| 109 | RCC.pllcfgr().modify(|w| w.set_divpen(plln, true)); | ||
| 110 | |||
| 111 | // Calulate additional output dividers | ||
| 112 | let q_ck = match config.q_ck { | ||
| 113 | Some(Hertz(ck)) if ck > 0 => { | ||
| 114 | let div = (vco_ck + ck - 1) / ck; | ||
| 115 | RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8)); | ||
| 116 | RCC.pllcfgr().modify(|w| w.set_divqen(plln, true)); | ||
| 117 | Some(vco_ck / div) | ||
| 118 | } | ||
| 119 | _ => None, | ||
| 120 | }; | ||
| 121 | let r_ck = match config.r_ck { | ||
| 122 | Some(Hertz(ck)) if ck > 0 => { | ||
| 123 | let div = (vco_ck + ck - 1) / ck; | ||
| 124 | RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8)); | ||
| 125 | RCC.pllcfgr().modify(|w| w.set_divren(plln, true)); | ||
| 126 | Some(vco_ck / div) | ||
| 127 | } | ||
| 128 | _ => None, | ||
| 129 | }; | ||
| 130 | |||
| 131 | (Some(vco_ck / pll_x_p), q_ck, r_ck) | ||
| 132 | } | ||
| 133 | None => { | ||
| 134 | assert!( | ||
| 135 | config.q_ck.is_none(), | ||
| 136 | "Must set PLL P clock for Q clock to take effect!" | ||
| 137 | ); | ||
| 138 | assert!( | ||
| 139 | config.r_ck.is_none(), | ||
| 140 | "Must set PLL P clock for R clock to take effect!" | ||
| 141 | ); | ||
| 142 | (None, None, None) | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs new file mode 100644 index 000000000..25daeedf0 --- /dev/null +++ b/embassy-stm32/src/rcc/l0.rs | |||
| @@ -0,0 +1,362 @@ | |||
| 1 | use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; | ||
| 2 | use crate::pac::{CRS, RCC, SYSCFG}; | ||
| 3 | use crate::rcc::{set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | |||
| 7 | /// HSI16 speed | ||
| 8 | pub const HSI16_FREQ: u32 = 16_000_000; | ||
| 9 | |||
| 10 | /// System clock mux source | ||
| 11 | #[derive(Clone, Copy)] | ||
| 12 | pub enum ClockSrc { | ||
| 13 | MSI(MSIRange), | ||
| 14 | PLL(PLLSource, PLLMul, PLLDiv), | ||
| 15 | HSE(Hertz), | ||
| 16 | HSI16, | ||
| 17 | } | ||
| 18 | |||
| 19 | /// MSI Clock Range | ||
| 20 | /// | ||
| 21 | /// These ranges control the frequency of the MSI. Internally, these ranges map | ||
| 22 | /// to the `MSIRANGE` bits in the `RCC_ICSCR` register. | ||
| 23 | #[derive(Clone, Copy)] | ||
| 24 | pub enum MSIRange { | ||
| 25 | /// Around 65.536 kHz | ||
| 26 | Range0, | ||
| 27 | /// Around 131.072 kHz | ||
| 28 | Range1, | ||
| 29 | /// Around 262.144 kHz | ||
| 30 | Range2, | ||
| 31 | /// Around 524.288 kHz | ||
| 32 | Range3, | ||
| 33 | /// Around 1.048 MHz | ||
| 34 | Range4, | ||
| 35 | /// Around 2.097 MHz (reset value) | ||
| 36 | Range5, | ||
| 37 | /// Around 4.194 MHz | ||
| 38 | Range6, | ||
| 39 | } | ||
| 40 | |||
| 41 | impl Default for MSIRange { | ||
| 42 | fn default() -> MSIRange { | ||
| 43 | MSIRange::Range5 | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | /// PLL divider | ||
| 48 | #[derive(Clone, Copy)] | ||
| 49 | pub enum PLLDiv { | ||
| 50 | Div2, | ||
| 51 | Div3, | ||
| 52 | Div4, | ||
| 53 | } | ||
| 54 | |||
| 55 | /// PLL multiplier | ||
| 56 | #[derive(Clone, Copy)] | ||
| 57 | pub enum PLLMul { | ||
| 58 | Mul3, | ||
| 59 | Mul4, | ||
| 60 | Mul6, | ||
| 61 | Mul8, | ||
| 62 | Mul12, | ||
| 63 | Mul16, | ||
| 64 | Mul24, | ||
| 65 | Mul32, | ||
| 66 | Mul48, | ||
| 67 | } | ||
| 68 | |||
| 69 | /// AHB prescaler | ||
| 70 | #[derive(Clone, Copy, PartialEq)] | ||
| 71 | pub enum AHBPrescaler { | ||
| 72 | NotDivided, | ||
| 73 | Div2, | ||
| 74 | Div4, | ||
| 75 | Div8, | ||
| 76 | Div16, | ||
| 77 | Div64, | ||
| 78 | Div128, | ||
| 79 | Div256, | ||
| 80 | Div512, | ||
| 81 | } | ||
| 82 | |||
| 83 | /// APB prescaler | ||
| 84 | #[derive(Clone, Copy)] | ||
| 85 | pub enum APBPrescaler { | ||
| 86 | NotDivided, | ||
| 87 | Div2, | ||
| 88 | Div4, | ||
| 89 | Div8, | ||
| 90 | Div16, | ||
| 91 | } | ||
| 92 | |||
| 93 | /// PLL clock input source | ||
| 94 | #[derive(Clone, Copy)] | ||
| 95 | pub enum PLLSource { | ||
| 96 | HSI16, | ||
| 97 | HSE(Hertz), | ||
| 98 | } | ||
| 99 | |||
| 100 | impl From<PLLMul> for Pllmul { | ||
| 101 | fn from(val: PLLMul) -> Pllmul { | ||
| 102 | match val { | ||
| 103 | PLLMul::Mul3 => Pllmul::MUL3, | ||
| 104 | PLLMul::Mul4 => Pllmul::MUL4, | ||
| 105 | PLLMul::Mul6 => Pllmul::MUL6, | ||
| 106 | PLLMul::Mul8 => Pllmul::MUL8, | ||
| 107 | PLLMul::Mul12 => Pllmul::MUL12, | ||
| 108 | PLLMul::Mul16 => Pllmul::MUL16, | ||
| 109 | PLLMul::Mul24 => Pllmul::MUL24, | ||
| 110 | PLLMul::Mul32 => Pllmul::MUL32, | ||
| 111 | PLLMul::Mul48 => Pllmul::MUL48, | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | impl From<PLLDiv> for Plldiv { | ||
| 117 | fn from(val: PLLDiv) -> Plldiv { | ||
| 118 | match val { | ||
| 119 | PLLDiv::Div2 => Plldiv::DIV2, | ||
| 120 | PLLDiv::Div3 => Plldiv::DIV3, | ||
| 121 | PLLDiv::Div4 => Plldiv::DIV4, | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl From<PLLSource> for Pllsrc { | ||
| 127 | fn from(val: PLLSource) -> Pllsrc { | ||
| 128 | match val { | ||
| 129 | PLLSource::HSI16 => Pllsrc::HSI16, | ||
| 130 | PLLSource::HSE(_) => Pllsrc::HSE, | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | impl From<APBPrescaler> for Ppre { | ||
| 136 | fn from(val: APBPrescaler) -> Ppre { | ||
| 137 | match val { | ||
| 138 | APBPrescaler::NotDivided => Ppre::DIV1, | ||
| 139 | APBPrescaler::Div2 => Ppre::DIV2, | ||
| 140 | APBPrescaler::Div4 => Ppre::DIV4, | ||
| 141 | APBPrescaler::Div8 => Ppre::DIV8, | ||
| 142 | APBPrescaler::Div16 => Ppre::DIV16, | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | impl From<AHBPrescaler> for Hpre { | ||
| 148 | fn from(val: AHBPrescaler) -> Hpre { | ||
| 149 | match val { | ||
| 150 | AHBPrescaler::NotDivided => Hpre::DIV1, | ||
| 151 | AHBPrescaler::Div2 => Hpre::DIV2, | ||
| 152 | AHBPrescaler::Div4 => Hpre::DIV4, | ||
| 153 | AHBPrescaler::Div8 => Hpre::DIV8, | ||
| 154 | AHBPrescaler::Div16 => Hpre::DIV16, | ||
| 155 | AHBPrescaler::Div64 => Hpre::DIV64, | ||
| 156 | AHBPrescaler::Div128 => Hpre::DIV128, | ||
| 157 | AHBPrescaler::Div256 => Hpre::DIV256, | ||
| 158 | AHBPrescaler::Div512 => Hpre::DIV512, | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | impl From<MSIRange> for Msirange { | ||
| 164 | fn from(val: MSIRange) -> Msirange { | ||
| 165 | match val { | ||
| 166 | MSIRange::Range0 => Msirange::RANGE0, | ||
| 167 | MSIRange::Range1 => Msirange::RANGE1, | ||
| 168 | MSIRange::Range2 => Msirange::RANGE2, | ||
| 169 | MSIRange::Range3 => Msirange::RANGE3, | ||
| 170 | MSIRange::Range4 => Msirange::RANGE4, | ||
| 171 | MSIRange::Range5 => Msirange::RANGE5, | ||
| 172 | MSIRange::Range6 => Msirange::RANGE6, | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Clocks configutation | ||
| 178 | pub struct Config { | ||
| 179 | pub mux: ClockSrc, | ||
| 180 | pub ahb_pre: AHBPrescaler, | ||
| 181 | pub apb1_pre: APBPrescaler, | ||
| 182 | pub apb2_pre: APBPrescaler, | ||
| 183 | pub enable_hsi48: bool, | ||
| 184 | } | ||
| 185 | |||
| 186 | impl Default for Config { | ||
| 187 | #[inline] | ||
| 188 | fn default() -> Config { | ||
| 189 | Config { | ||
| 190 | mux: ClockSrc::MSI(MSIRange::default()), | ||
| 191 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 192 | apb1_pre: APBPrescaler::NotDivided, | ||
| 193 | apb2_pre: APBPrescaler::NotDivided, | ||
| 194 | enable_hsi48: false, | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | pub(crate) unsafe fn init(config: Config) { | ||
| 200 | let (sys_clk, sw) = match config.mux { | ||
| 201 | ClockSrc::MSI(range) => { | ||
| 202 | // Set MSI range | ||
| 203 | RCC.icscr().write(|w| w.set_msirange(range.into())); | ||
| 204 | |||
| 205 | // Enable MSI | ||
| 206 | RCC.cr().write(|w| w.set_msion(true)); | ||
| 207 | while !RCC.cr().read().msirdy() {} | ||
| 208 | |||
| 209 | let freq = 32_768 * (1 << (range as u8 + 1)); | ||
| 210 | (freq, Sw::MSI) | ||
| 211 | } | ||
| 212 | ClockSrc::HSI16 => { | ||
| 213 | // Enable HSI16 | ||
| 214 | RCC.cr().write(|w| w.set_hsi16on(true)); | ||
| 215 | while !RCC.cr().read().hsi16rdyf() {} | ||
| 216 | |||
| 217 | (HSI16_FREQ, Sw::HSI16) | ||
| 218 | } | ||
| 219 | ClockSrc::HSE(freq) => { | ||
| 220 | // Enable HSE | ||
| 221 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 222 | while !RCC.cr().read().hserdy() {} | ||
| 223 | |||
| 224 | (freq.0, Sw::HSE) | ||
| 225 | } | ||
| 226 | ClockSrc::PLL(src, mul, div) => { | ||
| 227 | let freq = match src { | ||
| 228 | PLLSource::HSE(freq) => { | ||
| 229 | // Enable HSE | ||
| 230 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 231 | while !RCC.cr().read().hserdy() {} | ||
| 232 | freq.0 | ||
| 233 | } | ||
| 234 | PLLSource::HSI16 => { | ||
| 235 | // Enable HSI | ||
| 236 | RCC.cr().write(|w| w.set_hsi16on(true)); | ||
| 237 | while !RCC.cr().read().hsi16rdyf() {} | ||
| 238 | HSI16_FREQ | ||
| 239 | } | ||
| 240 | }; | ||
| 241 | |||
| 242 | // Disable PLL | ||
| 243 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 244 | while RCC.cr().read().pllrdy() {} | ||
| 245 | |||
| 246 | let freq = match mul { | ||
| 247 | PLLMul::Mul3 => freq * 3, | ||
| 248 | PLLMul::Mul4 => freq * 4, | ||
| 249 | PLLMul::Mul6 => freq * 6, | ||
| 250 | PLLMul::Mul8 => freq * 8, | ||
| 251 | PLLMul::Mul12 => freq * 12, | ||
| 252 | PLLMul::Mul16 => freq * 16, | ||
| 253 | PLLMul::Mul24 => freq * 24, | ||
| 254 | PLLMul::Mul32 => freq * 32, | ||
| 255 | PLLMul::Mul48 => freq * 48, | ||
| 256 | }; | ||
| 257 | |||
| 258 | let freq = match div { | ||
| 259 | PLLDiv::Div2 => freq / 2, | ||
| 260 | PLLDiv::Div3 => freq / 3, | ||
| 261 | PLLDiv::Div4 => freq / 4, | ||
| 262 | }; | ||
| 263 | assert!(freq <= 32_u32.mhz().0); | ||
| 264 | |||
| 265 | RCC.cfgr().write(move |w| { | ||
| 266 | w.set_pllmul(mul.into()); | ||
| 267 | w.set_plldiv(div.into()); | ||
| 268 | w.set_pllsrc(src.into()); | ||
| 269 | }); | ||
| 270 | |||
| 271 | // Enable PLL | ||
| 272 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 273 | while !RCC.cr().read().pllrdy() {} | ||
| 274 | |||
| 275 | (freq, Sw::PLL) | ||
| 276 | } | ||
| 277 | }; | ||
| 278 | |||
| 279 | RCC.cfgr().modify(|w| { | ||
| 280 | w.set_sw(sw); | ||
| 281 | w.set_hpre(config.ahb_pre.into()); | ||
| 282 | w.set_ppre1(config.apb1_pre.into()); | ||
| 283 | w.set_ppre2(config.apb2_pre.into()); | ||
| 284 | }); | ||
| 285 | |||
| 286 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 287 | AHBPrescaler::NotDivided => sys_clk, | ||
| 288 | pre => { | ||
| 289 | let pre: Hpre = pre.into(); | ||
| 290 | let pre = 1 << (pre.0 as u32 - 7); | ||
| 291 | sys_clk / pre | ||
| 292 | } | ||
| 293 | }; | ||
| 294 | |||
| 295 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 296 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 297 | pre => { | ||
| 298 | let pre: Ppre = pre.into(); | ||
| 299 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 300 | let freq = ahb_freq / pre as u32; | ||
| 301 | (freq, freq * 2) | ||
| 302 | } | ||
| 303 | }; | ||
| 304 | |||
| 305 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 306 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 307 | pre => { | ||
| 308 | let pre: Ppre = pre.into(); | ||
| 309 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 310 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 311 | (freq, freq * 2) | ||
| 312 | } | ||
| 313 | }; | ||
| 314 | |||
| 315 | if config.enable_hsi48 { | ||
| 316 | // Reset SYSCFG peripheral | ||
| 317 | RCC.apb2rstr().modify(|w| w.set_syscfgrst(true)); | ||
| 318 | RCC.apb2rstr().modify(|w| w.set_syscfgrst(false)); | ||
| 319 | |||
| 320 | // Enable SYSCFG peripheral | ||
| 321 | RCC.apb2enr().modify(|w| w.set_syscfgen(true)); | ||
| 322 | |||
| 323 | // Reset CRS peripheral | ||
| 324 | RCC.apb1rstr().modify(|w| w.set_crsrst(true)); | ||
| 325 | RCC.apb1rstr().modify(|w| w.set_crsrst(false)); | ||
| 326 | |||
| 327 | // Enable CRS peripheral | ||
| 328 | RCC.apb1enr().modify(|w| w.set_crsen(true)); | ||
| 329 | |||
| 330 | // Initialize CRS | ||
| 331 | CRS.cfgr().write(|w| | ||
| 332 | |||
| 333 | // Select LSE as synchronization source | ||
| 334 | w.set_syncsrc(0b01)); | ||
| 335 | CRS.cr().modify(|w| { | ||
| 336 | w.set_autotrimen(true); | ||
| 337 | w.set_cen(true); | ||
| 338 | }); | ||
| 339 | |||
| 340 | // Enable VREFINT reference for HSI48 oscillator | ||
| 341 | SYSCFG.cfgr3().modify(|w| { | ||
| 342 | w.set_enref_hsi48(true); | ||
| 343 | w.set_en_vrefint(true); | ||
| 344 | }); | ||
| 345 | |||
| 346 | // Select HSI48 as USB clock | ||
| 347 | RCC.ccipr().modify(|w| w.set_hsi48msel(true)); | ||
| 348 | |||
| 349 | // Enable dedicated USB clock | ||
| 350 | RCC.crrcr().modify(|w| w.set_hsi48on(true)); | ||
| 351 | while !RCC.crrcr().read().hsi48rdy() {} | ||
| 352 | } | ||
| 353 | |||
| 354 | set_freqs(Clocks { | ||
| 355 | sys: sys_clk.hz(), | ||
| 356 | ahb: ahb_freq.hz(), | ||
| 357 | apb1: apb1_freq.hz(), | ||
| 358 | apb2: apb2_freq.hz(), | ||
| 359 | apb1_tim: apb1_tim_freq.hz(), | ||
| 360 | apb2_tim: apb2_tim_freq.hz(), | ||
| 361 | }); | ||
| 362 | } | ||
diff --git a/embassy-stm32/src/rcc/l0/mod.rs b/embassy-stm32/src/rcc/l0/mod.rs deleted file mode 100644 index fd84f09c5..000000000 --- a/embassy-stm32/src/rcc/l0/mod.rs +++ /dev/null | |||
| @@ -1,437 +0,0 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::{self, CRS, RCC, SYSCFG}; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use embassy::util::Unborrow; | ||
| 8 | use embassy_hal_common::unborrow; | ||
| 9 | use pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; | ||
| 10 | |||
| 11 | /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||
| 12 | /// and with the addition of the init function to configure a system clock. | ||
| 13 | |||
| 14 | /// HSI speed | ||
| 15 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 16 | |||
| 17 | /// System clock mux source | ||
| 18 | #[derive(Clone, Copy)] | ||
| 19 | pub enum ClockSrc { | ||
| 20 | MSI(MSIRange), | ||
| 21 | PLL(PLLSource, PLLMul, PLLDiv), | ||
| 22 | HSE(Hertz), | ||
| 23 | HSI16, | ||
| 24 | } | ||
| 25 | |||
| 26 | /// MSI Clock Range | ||
| 27 | /// | ||
| 28 | /// These ranges control the frequency of the MSI. Internally, these ranges map | ||
| 29 | /// to the `MSIRANGE` bits in the `RCC_ICSCR` register. | ||
| 30 | #[derive(Clone, Copy)] | ||
| 31 | pub enum MSIRange { | ||
| 32 | /// Around 65.536 kHz | ||
| 33 | Range0, | ||
| 34 | /// Around 131.072 kHz | ||
| 35 | Range1, | ||
| 36 | /// Around 262.144 kHz | ||
| 37 | Range2, | ||
| 38 | /// Around 524.288 kHz | ||
| 39 | Range3, | ||
| 40 | /// Around 1.048 MHz | ||
| 41 | Range4, | ||
| 42 | /// Around 2.097 MHz (reset value) | ||
| 43 | Range5, | ||
| 44 | /// Around 4.194 MHz | ||
| 45 | Range6, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl Default for MSIRange { | ||
| 49 | fn default() -> MSIRange { | ||
| 50 | MSIRange::Range5 | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// PLL divider | ||
| 55 | #[derive(Clone, Copy)] | ||
| 56 | pub enum PLLDiv { | ||
| 57 | Div2, | ||
| 58 | Div3, | ||
| 59 | Div4, | ||
| 60 | } | ||
| 61 | |||
| 62 | /// PLL multiplier | ||
| 63 | #[derive(Clone, Copy)] | ||
| 64 | pub enum PLLMul { | ||
| 65 | Mul3, | ||
| 66 | Mul4, | ||
| 67 | Mul6, | ||
| 68 | Mul8, | ||
| 69 | Mul12, | ||
| 70 | Mul16, | ||
| 71 | Mul24, | ||
| 72 | Mul32, | ||
| 73 | Mul48, | ||
| 74 | } | ||
| 75 | |||
| 76 | /// AHB prescaler | ||
| 77 | #[derive(Clone, Copy, PartialEq)] | ||
| 78 | pub enum AHBPrescaler { | ||
| 79 | NotDivided, | ||
| 80 | Div2, | ||
| 81 | Div4, | ||
| 82 | Div8, | ||
| 83 | Div16, | ||
| 84 | Div64, | ||
| 85 | Div128, | ||
| 86 | Div256, | ||
| 87 | Div512, | ||
| 88 | } | ||
| 89 | |||
| 90 | /// APB prescaler | ||
| 91 | #[derive(Clone, Copy)] | ||
| 92 | pub enum APBPrescaler { | ||
| 93 | NotDivided, | ||
| 94 | Div2, | ||
| 95 | Div4, | ||
| 96 | Div8, | ||
| 97 | Div16, | ||
| 98 | } | ||
| 99 | |||
| 100 | /// PLL clock input source | ||
| 101 | #[derive(Clone, Copy)] | ||
| 102 | pub enum PLLSource { | ||
| 103 | HSI16, | ||
| 104 | HSE(Hertz), | ||
| 105 | } | ||
| 106 | |||
| 107 | impl Into<Pllmul> for PLLMul { | ||
| 108 | fn into(self) -> Pllmul { | ||
| 109 | match self { | ||
| 110 | PLLMul::Mul3 => Pllmul::MUL3, | ||
| 111 | PLLMul::Mul4 => Pllmul::MUL4, | ||
| 112 | PLLMul::Mul6 => Pllmul::MUL6, | ||
| 113 | PLLMul::Mul8 => Pllmul::MUL8, | ||
| 114 | PLLMul::Mul12 => Pllmul::MUL12, | ||
| 115 | PLLMul::Mul16 => Pllmul::MUL16, | ||
| 116 | PLLMul::Mul24 => Pllmul::MUL24, | ||
| 117 | PLLMul::Mul32 => Pllmul::MUL32, | ||
| 118 | PLLMul::Mul48 => Pllmul::MUL48, | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | impl Into<Plldiv> for PLLDiv { | ||
| 124 | fn into(self) -> Plldiv { | ||
| 125 | match self { | ||
| 126 | PLLDiv::Div2 => Plldiv::DIV2, | ||
| 127 | PLLDiv::Div3 => Plldiv::DIV3, | ||
| 128 | PLLDiv::Div4 => Plldiv::DIV4, | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | impl Into<Pllsrc> for PLLSource { | ||
| 134 | fn into(self) -> Pllsrc { | ||
| 135 | match self { | ||
| 136 | PLLSource::HSI16 => Pllsrc::HSI16, | ||
| 137 | PLLSource::HSE(_) => Pllsrc::HSE, | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | impl Into<Ppre> for APBPrescaler { | ||
| 143 | fn into(self) -> Ppre { | ||
| 144 | match self { | ||
| 145 | APBPrescaler::NotDivided => Ppre::DIV1, | ||
| 146 | APBPrescaler::Div2 => Ppre::DIV2, | ||
| 147 | APBPrescaler::Div4 => Ppre::DIV4, | ||
| 148 | APBPrescaler::Div8 => Ppre::DIV8, | ||
| 149 | APBPrescaler::Div16 => Ppre::DIV16, | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | impl Into<Hpre> for AHBPrescaler { | ||
| 155 | fn into(self) -> Hpre { | ||
| 156 | match self { | ||
| 157 | AHBPrescaler::NotDivided => Hpre::DIV1, | ||
| 158 | AHBPrescaler::Div2 => Hpre::DIV2, | ||
| 159 | AHBPrescaler::Div4 => Hpre::DIV4, | ||
| 160 | AHBPrescaler::Div8 => Hpre::DIV8, | ||
| 161 | AHBPrescaler::Div16 => Hpre::DIV16, | ||
| 162 | AHBPrescaler::Div64 => Hpre::DIV64, | ||
| 163 | AHBPrescaler::Div128 => Hpre::DIV128, | ||
| 164 | AHBPrescaler::Div256 => Hpre::DIV256, | ||
| 165 | AHBPrescaler::Div512 => Hpre::DIV512, | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | impl Into<Msirange> for MSIRange { | ||
| 171 | fn into(self) -> Msirange { | ||
| 172 | match self { | ||
| 173 | MSIRange::Range0 => Msirange::RANGE0, | ||
| 174 | MSIRange::Range1 => Msirange::RANGE1, | ||
| 175 | MSIRange::Range2 => Msirange::RANGE2, | ||
| 176 | MSIRange::Range3 => Msirange::RANGE3, | ||
| 177 | MSIRange::Range4 => Msirange::RANGE4, | ||
| 178 | MSIRange::Range5 => Msirange::RANGE5, | ||
| 179 | MSIRange::Range6 => Msirange::RANGE6, | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Clocks configutation | ||
| 185 | pub struct Config { | ||
| 186 | pub mux: ClockSrc, | ||
| 187 | pub ahb_pre: AHBPrescaler, | ||
| 188 | pub apb1_pre: APBPrescaler, | ||
| 189 | pub apb2_pre: APBPrescaler, | ||
| 190 | } | ||
| 191 | |||
| 192 | impl Default for Config { | ||
| 193 | #[inline] | ||
| 194 | fn default() -> Config { | ||
| 195 | Config { | ||
| 196 | mux: ClockSrc::MSI(MSIRange::default()), | ||
| 197 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 198 | apb1_pre: APBPrescaler::NotDivided, | ||
| 199 | apb2_pre: APBPrescaler::NotDivided, | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | /// RCC peripheral | ||
| 205 | pub struct Rcc<'d> { | ||
| 206 | _rb: peripherals::RCC, | ||
| 207 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<'d> Rcc<'d> { | ||
| 211 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | ||
| 212 | unborrow!(rcc); | ||
| 213 | Self { | ||
| 214 | _rb: rcc, | ||
| 215 | phantom: PhantomData, | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | // Safety: RCC init must have been called | ||
| 220 | pub fn clocks(&self) -> &'static Clocks { | ||
| 221 | unsafe { get_freqs() } | ||
| 222 | } | ||
| 223 | |||
| 224 | pub fn enable_hsi48(&mut self, _syscfg: &mut SYSCFG, _crs: CRS) -> HSI48 { | ||
| 225 | let rcc = pac::RCC; | ||
| 226 | unsafe { | ||
| 227 | // Reset SYSCFG peripheral | ||
| 228 | rcc.apb2rstr().modify(|w| w.set_syscfgrst(true)); | ||
| 229 | rcc.apb2rstr().modify(|w| w.set_syscfgrst(false)); | ||
| 230 | |||
| 231 | // Enable SYSCFG peripheral | ||
| 232 | rcc.apb2enr().modify(|w| w.set_syscfgen(true)); | ||
| 233 | |||
| 234 | // Reset CRS peripheral | ||
| 235 | rcc.apb1rstr().modify(|w| w.set_crsrst(true)); | ||
| 236 | rcc.apb1rstr().modify(|w| w.set_crsrst(false)); | ||
| 237 | |||
| 238 | // Enable CRS peripheral | ||
| 239 | rcc.apb1enr().modify(|w| w.set_crsen(true)); | ||
| 240 | |||
| 241 | // Initialize CRS | ||
| 242 | let crs = pac::CRS; | ||
| 243 | crs.cfgr().write(|w| | ||
| 244 | |||
| 245 | // Select LSE as synchronization source | ||
| 246 | w.set_syncsrc(0b01)); | ||
| 247 | crs.cr().modify(|w| { | ||
| 248 | w.set_autotrimen(true); | ||
| 249 | w.set_cen(true); | ||
| 250 | }); | ||
| 251 | |||
| 252 | // Enable VREFINT reference for HSI48 oscillator | ||
| 253 | let syscfg = pac::SYSCFG; | ||
| 254 | syscfg.cfgr3().modify(|w| { | ||
| 255 | w.set_enref_hsi48(true); | ||
| 256 | w.set_en_vrefint(true); | ||
| 257 | }); | ||
| 258 | |||
| 259 | // Select HSI48 as USB clock | ||
| 260 | rcc.ccipr().modify(|w| w.set_hsi48msel(true)); | ||
| 261 | |||
| 262 | // Enable dedicated USB clock | ||
| 263 | rcc.crrcr().modify(|w| w.set_hsi48on(true)); | ||
| 264 | while !rcc.crrcr().read().hsi48rdy() {} | ||
| 265 | } | ||
| 266 | |||
| 267 | HSI48(()) | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | ||
| 272 | pub trait RccExt { | ||
| 273 | fn freeze(self, config: Config) -> Clocks; | ||
| 274 | } | ||
| 275 | |||
| 276 | impl RccExt for RCC { | ||
| 277 | // `cfgr` is almost always a constant, so make sure it can be constant-propagated properly by | ||
| 278 | // marking this function and all `Config` constructors and setters as `#[inline]`. | ||
| 279 | // This saves ~900 Bytes for the `pwr.rs` example. | ||
| 280 | #[inline] | ||
| 281 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 282 | let rcc = pac::RCC; | ||
| 283 | let (sys_clk, sw) = match cfgr.mux { | ||
| 284 | ClockSrc::MSI(range) => { | ||
| 285 | // Set MSI range | ||
| 286 | unsafe { | ||
| 287 | rcc.icscr().write(|w| w.set_msirange(range.into())); | ||
| 288 | } | ||
| 289 | |||
| 290 | // Enable MSI | ||
| 291 | unsafe { | ||
| 292 | rcc.cr().write(|w| w.set_msion(true)); | ||
| 293 | while !rcc.cr().read().msirdy() {} | ||
| 294 | } | ||
| 295 | |||
| 296 | let freq = 32_768 * (1 << (range as u8 + 1)); | ||
| 297 | (freq, Sw::MSI) | ||
| 298 | } | ||
| 299 | ClockSrc::HSI16 => { | ||
| 300 | // Enable HSI16 | ||
| 301 | unsafe { | ||
| 302 | rcc.cr().write(|w| w.set_hsi16on(true)); | ||
| 303 | while !rcc.cr().read().hsi16rdyf() {} | ||
| 304 | } | ||
| 305 | |||
| 306 | (HSI_FREQ, Sw::HSI16) | ||
| 307 | } | ||
| 308 | ClockSrc::HSE(freq) => { | ||
| 309 | // Enable HSE | ||
| 310 | unsafe { | ||
| 311 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 312 | while !rcc.cr().read().hserdy() {} | ||
| 313 | } | ||
| 314 | |||
| 315 | (freq.0, Sw::HSE) | ||
| 316 | } | ||
| 317 | ClockSrc::PLL(src, mul, div) => { | ||
| 318 | let freq = match src { | ||
| 319 | PLLSource::HSE(freq) => { | ||
| 320 | // Enable HSE | ||
| 321 | unsafe { | ||
| 322 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 323 | while !rcc.cr().read().hserdy() {} | ||
| 324 | } | ||
| 325 | freq.0 | ||
| 326 | } | ||
| 327 | PLLSource::HSI16 => { | ||
| 328 | // Enable HSI | ||
| 329 | unsafe { | ||
| 330 | rcc.cr().write(|w| w.set_hsi16on(true)); | ||
| 331 | while !rcc.cr().read().hsi16rdyf() {} | ||
| 332 | } | ||
| 333 | HSI_FREQ | ||
| 334 | } | ||
| 335 | }; | ||
| 336 | |||
| 337 | // Disable PLL | ||
| 338 | unsafe { | ||
| 339 | rcc.cr().modify(|w| w.set_pllon(false)); | ||
| 340 | while rcc.cr().read().pllrdy() {} | ||
| 341 | } | ||
| 342 | |||
| 343 | let freq = match mul { | ||
| 344 | PLLMul::Mul3 => freq * 3, | ||
| 345 | PLLMul::Mul4 => freq * 4, | ||
| 346 | PLLMul::Mul6 => freq * 6, | ||
| 347 | PLLMul::Mul8 => freq * 8, | ||
| 348 | PLLMul::Mul12 => freq * 12, | ||
| 349 | PLLMul::Mul16 => freq * 16, | ||
| 350 | PLLMul::Mul24 => freq * 24, | ||
| 351 | PLLMul::Mul32 => freq * 32, | ||
| 352 | PLLMul::Mul48 => freq * 48, | ||
| 353 | }; | ||
| 354 | |||
| 355 | let freq = match div { | ||
| 356 | PLLDiv::Div2 => freq / 2, | ||
| 357 | PLLDiv::Div3 => freq / 3, | ||
| 358 | PLLDiv::Div4 => freq / 4, | ||
| 359 | }; | ||
| 360 | assert!(freq <= 32_u32.mhz().0); | ||
| 361 | |||
| 362 | unsafe { | ||
| 363 | rcc.cfgr().write(move |w| { | ||
| 364 | w.set_pllmul(mul.into()); | ||
| 365 | w.set_plldiv(div.into()); | ||
| 366 | w.set_pllsrc(src.into()); | ||
| 367 | }); | ||
| 368 | |||
| 369 | // Enable PLL | ||
| 370 | rcc.cr().modify(|w| w.set_pllon(true)); | ||
| 371 | while !rcc.cr().read().pllrdy() {} | ||
| 372 | } | ||
| 373 | |||
| 374 | (freq, Sw::PLL) | ||
| 375 | } | ||
| 376 | }; | ||
| 377 | |||
| 378 | unsafe { | ||
| 379 | rcc.cfgr().modify(|w| { | ||
| 380 | w.set_sw(sw.into()); | ||
| 381 | w.set_hpre(cfgr.ahb_pre.into()); | ||
| 382 | w.set_ppre1(cfgr.apb1_pre.into()); | ||
| 383 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 384 | }); | ||
| 385 | } | ||
| 386 | |||
| 387 | let ahb_freq: u32 = match cfgr.ahb_pre { | ||
| 388 | AHBPrescaler::NotDivided => sys_clk, | ||
| 389 | pre => { | ||
| 390 | let pre: Hpre = pre.into(); | ||
| 391 | let pre = 1 << (pre.0 as u32 - 7); | ||
| 392 | sys_clk / pre | ||
| 393 | } | ||
| 394 | }; | ||
| 395 | |||
| 396 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | ||
| 397 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 398 | pre => { | ||
| 399 | let pre: Ppre = pre.into(); | ||
| 400 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 401 | let freq = ahb_freq / pre as u32; | ||
| 402 | (freq, freq * 2) | ||
| 403 | } | ||
| 404 | }; | ||
| 405 | |||
| 406 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | ||
| 407 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 408 | pre => { | ||
| 409 | let pre: Ppre = pre.into(); | ||
| 410 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 411 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 412 | (freq, freq * 2) | ||
| 413 | } | ||
| 414 | }; | ||
| 415 | |||
| 416 | Clocks { | ||
| 417 | sys: sys_clk.hz(), | ||
| 418 | ahb: ahb_freq.hz(), | ||
| 419 | apb1: apb1_freq.hz(), | ||
| 420 | apb2: apb2_freq.hz(), | ||
| 421 | apb1_tim: apb1_tim_freq.hz(), | ||
| 422 | apb2_tim: apb2_tim_freq.hz(), | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | /// Token that exists only, if the HSI48 clock has been enabled | ||
| 428 | /// | ||
| 429 | /// You can get an instance of this struct by calling [`Rcc::enable_hsi48`]. | ||
| 430 | #[derive(Clone, Copy)] | ||
| 431 | pub struct HSI48(()); | ||
| 432 | |||
| 433 | pub unsafe fn init(config: Config) { | ||
| 434 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 435 | let clocks = r.freeze(config); | ||
| 436 | set_freqs(clocks); | ||
| 437 | } | ||
diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs new file mode 100644 index 000000000..517869ca4 --- /dev/null +++ b/embassy-stm32/src/rcc/l1.rs | |||
| @@ -0,0 +1,321 @@ | |||
| 1 | use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; | ||
| 2 | use crate::pac::RCC; | ||
| 3 | use crate::rcc::{set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | |||
| 7 | /// HSI speed | ||
| 8 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 9 | |||
| 10 | /// System clock mux source | ||
| 11 | #[derive(Clone, Copy)] | ||
| 12 | pub enum ClockSrc { | ||
| 13 | MSI(MSIRange), | ||
| 14 | PLL(PLLSource, PLLMul, PLLDiv), | ||
| 15 | HSE(Hertz), | ||
| 16 | HSI, | ||
| 17 | } | ||
| 18 | |||
| 19 | /// MSI Clock Range | ||
| 20 | /// | ||
| 21 | /// These ranges control the frequency of the MSI. Internally, these ranges map | ||
| 22 | /// to the `MSIRANGE` bits in the `RCC_ICSCR` register. | ||
| 23 | #[derive(Clone, Copy)] | ||
| 24 | pub enum MSIRange { | ||
| 25 | /// Around 65.536 kHz | ||
| 26 | Range0, | ||
| 27 | /// Around 131.072 kHz | ||
| 28 | Range1, | ||
| 29 | /// Around 262.144 kHz | ||
| 30 | Range2, | ||
| 31 | /// Around 524.288 kHz | ||
| 32 | Range3, | ||
| 33 | /// Around 1.048 MHz | ||
| 34 | Range4, | ||
| 35 | /// Around 2.097 MHz (reset value) | ||
| 36 | Range5, | ||
| 37 | /// Around 4.194 MHz | ||
| 38 | Range6, | ||
| 39 | } | ||
| 40 | |||
| 41 | impl Default for MSIRange { | ||
| 42 | fn default() -> MSIRange { | ||
| 43 | MSIRange::Range5 | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | /// PLL divider | ||
| 48 | #[derive(Clone, Copy)] | ||
| 49 | pub enum PLLDiv { | ||
| 50 | Div2, | ||
| 51 | Div3, | ||
| 52 | Div4, | ||
| 53 | } | ||
| 54 | |||
| 55 | /// PLL multiplier | ||
| 56 | #[derive(Clone, Copy)] | ||
| 57 | pub enum PLLMul { | ||
| 58 | Mul3, | ||
| 59 | Mul4, | ||
| 60 | Mul6, | ||
| 61 | Mul8, | ||
| 62 | Mul12, | ||
| 63 | Mul16, | ||
| 64 | Mul24, | ||
| 65 | Mul32, | ||
| 66 | Mul48, | ||
| 67 | } | ||
| 68 | |||
| 69 | /// AHB prescaler | ||
| 70 | #[derive(Clone, Copy, PartialEq)] | ||
| 71 | pub enum AHBPrescaler { | ||
| 72 | NotDivided, | ||
| 73 | Div2, | ||
| 74 | Div4, | ||
| 75 | Div8, | ||
| 76 | Div16, | ||
| 77 | Div64, | ||
| 78 | Div128, | ||
| 79 | Div256, | ||
| 80 | Div512, | ||
| 81 | } | ||
| 82 | |||
| 83 | /// APB prescaler | ||
| 84 | #[derive(Clone, Copy)] | ||
| 85 | pub enum APBPrescaler { | ||
| 86 | NotDivided, | ||
| 87 | Div2, | ||
| 88 | Div4, | ||
| 89 | Div8, | ||
| 90 | Div16, | ||
| 91 | } | ||
| 92 | |||
| 93 | /// PLL clock input source | ||
| 94 | #[derive(Clone, Copy)] | ||
| 95 | pub enum PLLSource { | ||
| 96 | HSI, | ||
| 97 | HSE(Hertz), | ||
| 98 | } | ||
| 99 | |||
| 100 | impl From<PLLMul> for Pllmul { | ||
| 101 | fn from(val: PLLMul) -> Pllmul { | ||
| 102 | match val { | ||
| 103 | PLLMul::Mul3 => Pllmul::MUL3, | ||
| 104 | PLLMul::Mul4 => Pllmul::MUL4, | ||
| 105 | PLLMul::Mul6 => Pllmul::MUL6, | ||
| 106 | PLLMul::Mul8 => Pllmul::MUL8, | ||
| 107 | PLLMul::Mul12 => Pllmul::MUL12, | ||
| 108 | PLLMul::Mul16 => Pllmul::MUL16, | ||
| 109 | PLLMul::Mul24 => Pllmul::MUL24, | ||
| 110 | PLLMul::Mul32 => Pllmul::MUL32, | ||
| 111 | PLLMul::Mul48 => Pllmul::MUL48, | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | impl From<PLLDiv> for Plldiv { | ||
| 117 | fn from(val: PLLDiv) -> Plldiv { | ||
| 118 | match val { | ||
| 119 | PLLDiv::Div2 => Plldiv::DIV2, | ||
| 120 | PLLDiv::Div3 => Plldiv::DIV3, | ||
| 121 | PLLDiv::Div4 => Plldiv::DIV4, | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl From<PLLSource> for Pllsrc { | ||
| 127 | fn from(val: PLLSource) -> Pllsrc { | ||
| 128 | match val { | ||
| 129 | PLLSource::HSI => Pllsrc::HSI, | ||
| 130 | PLLSource::HSE(_) => Pllsrc::HSE, | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | impl From<APBPrescaler> for Ppre { | ||
| 136 | fn from(val: APBPrescaler) -> Ppre { | ||
| 137 | match val { | ||
| 138 | APBPrescaler::NotDivided => Ppre::DIV1, | ||
| 139 | APBPrescaler::Div2 => Ppre::DIV2, | ||
| 140 | APBPrescaler::Div4 => Ppre::DIV4, | ||
| 141 | APBPrescaler::Div8 => Ppre::DIV8, | ||
| 142 | APBPrescaler::Div16 => Ppre::DIV16, | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | impl From<AHBPrescaler> for Hpre { | ||
| 148 | fn from(val: AHBPrescaler) -> Hpre { | ||
| 149 | match val { | ||
| 150 | AHBPrescaler::NotDivided => Hpre::DIV1, | ||
| 151 | AHBPrescaler::Div2 => Hpre::DIV2, | ||
| 152 | AHBPrescaler::Div4 => Hpre::DIV4, | ||
| 153 | AHBPrescaler::Div8 => Hpre::DIV8, | ||
| 154 | AHBPrescaler::Div16 => Hpre::DIV16, | ||
| 155 | AHBPrescaler::Div64 => Hpre::DIV64, | ||
| 156 | AHBPrescaler::Div128 => Hpre::DIV128, | ||
| 157 | AHBPrescaler::Div256 => Hpre::DIV256, | ||
| 158 | AHBPrescaler::Div512 => Hpre::DIV512, | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | impl From<MSIRange> for Msirange { | ||
| 164 | fn from(val: MSIRange) -> Msirange { | ||
| 165 | match val { | ||
| 166 | MSIRange::Range0 => Msirange::RANGE0, | ||
| 167 | MSIRange::Range1 => Msirange::RANGE1, | ||
| 168 | MSIRange::Range2 => Msirange::RANGE2, | ||
| 169 | MSIRange::Range3 => Msirange::RANGE3, | ||
| 170 | MSIRange::Range4 => Msirange::RANGE4, | ||
| 171 | MSIRange::Range5 => Msirange::RANGE5, | ||
| 172 | MSIRange::Range6 => Msirange::RANGE6, | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Clocks configutation | ||
| 178 | pub struct Config { | ||
| 179 | pub mux: ClockSrc, | ||
| 180 | pub ahb_pre: AHBPrescaler, | ||
| 181 | pub apb1_pre: APBPrescaler, | ||
| 182 | pub apb2_pre: APBPrescaler, | ||
| 183 | } | ||
| 184 | |||
| 185 | impl Default for Config { | ||
| 186 | #[inline] | ||
| 187 | fn default() -> Config { | ||
| 188 | Config { | ||
| 189 | mux: ClockSrc::MSI(MSIRange::default()), | ||
| 190 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 191 | apb1_pre: APBPrescaler::NotDivided, | ||
| 192 | apb2_pre: APBPrescaler::NotDivided, | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | pub(crate) unsafe fn init(config: Config) { | ||
| 198 | let (sys_clk, sw) = match config.mux { | ||
| 199 | ClockSrc::MSI(range) => { | ||
| 200 | // Set MSI range | ||
| 201 | RCC.icscr().write(|w| w.set_msirange(range.into())); | ||
| 202 | |||
| 203 | // Enable MSI | ||
| 204 | RCC.cr().write(|w| w.set_msion(true)); | ||
| 205 | while !RCC.cr().read().msirdy() {} | ||
| 206 | |||
| 207 | let freq = 32_768 * (1 << (range as u8 + 1)); | ||
| 208 | (freq, Sw::MSI) | ||
| 209 | } | ||
| 210 | ClockSrc::HSI => { | ||
| 211 | // Enable HSI | ||
| 212 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 213 | while !RCC.cr().read().hsirdy() {} | ||
| 214 | |||
| 215 | (HSI_FREQ, Sw::HSI) | ||
| 216 | } | ||
| 217 | ClockSrc::HSE(freq) => { | ||
| 218 | // Enable HSE | ||
| 219 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 220 | while !RCC.cr().read().hserdy() {} | ||
| 221 | |||
| 222 | (freq.0, Sw::HSE) | ||
| 223 | } | ||
| 224 | ClockSrc::PLL(src, mul, div) => { | ||
| 225 | let freq = match src { | ||
| 226 | PLLSource::HSE(freq) => { | ||
| 227 | // Enable HSE | ||
| 228 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 229 | while !RCC.cr().read().hserdy() {} | ||
| 230 | freq.0 | ||
| 231 | } | ||
| 232 | PLLSource::HSI => { | ||
| 233 | // Enable HSI | ||
| 234 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 235 | while !RCC.cr().read().hsirdy() {} | ||
| 236 | HSI_FREQ | ||
| 237 | } | ||
| 238 | }; | ||
| 239 | |||
| 240 | // Disable PLL | ||
| 241 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 242 | while RCC.cr().read().pllrdy() {} | ||
| 243 | |||
| 244 | let freq = match mul { | ||
| 245 | PLLMul::Mul3 => freq * 3, | ||
| 246 | PLLMul::Mul4 => freq * 4, | ||
| 247 | PLLMul::Mul6 => freq * 6, | ||
| 248 | PLLMul::Mul8 => freq * 8, | ||
| 249 | PLLMul::Mul12 => freq * 12, | ||
| 250 | PLLMul::Mul16 => freq * 16, | ||
| 251 | PLLMul::Mul24 => freq * 24, | ||
| 252 | PLLMul::Mul32 => freq * 32, | ||
| 253 | PLLMul::Mul48 => freq * 48, | ||
| 254 | }; | ||
| 255 | |||
| 256 | let freq = match div { | ||
| 257 | PLLDiv::Div2 => freq / 2, | ||
| 258 | PLLDiv::Div3 => freq / 3, | ||
| 259 | PLLDiv::Div4 => freq / 4, | ||
| 260 | }; | ||
| 261 | assert!(freq <= 32_u32.mhz().0); | ||
| 262 | |||
| 263 | RCC.cfgr().write(move |w| { | ||
| 264 | w.set_pllmul(mul.into()); | ||
| 265 | w.set_plldiv(div.into()); | ||
| 266 | w.set_pllsrc(src.into()); | ||
| 267 | }); | ||
| 268 | |||
| 269 | // Enable PLL | ||
| 270 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 271 | while !RCC.cr().read().pllrdy() {} | ||
| 272 | |||
| 273 | (freq, Sw::PLL) | ||
| 274 | } | ||
| 275 | }; | ||
| 276 | |||
| 277 | RCC.cfgr().modify(|w| { | ||
| 278 | w.set_sw(sw); | ||
| 279 | w.set_hpre(config.ahb_pre.into()); | ||
| 280 | w.set_ppre1(config.apb1_pre.into()); | ||
| 281 | w.set_ppre2(config.apb2_pre.into()); | ||
| 282 | }); | ||
| 283 | |||
| 284 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 285 | AHBPrescaler::NotDivided => sys_clk, | ||
| 286 | pre => { | ||
| 287 | let pre: Hpre = pre.into(); | ||
| 288 | let pre = 1 << (pre.0 as u32 - 7); | ||
| 289 | sys_clk / pre | ||
| 290 | } | ||
| 291 | }; | ||
| 292 | |||
| 293 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 294 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 295 | pre => { | ||
| 296 | let pre: Ppre = pre.into(); | ||
| 297 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 298 | let freq = ahb_freq / pre as u32; | ||
| 299 | (freq, freq * 2) | ||
| 300 | } | ||
| 301 | }; | ||
| 302 | |||
| 303 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 304 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 305 | pre => { | ||
| 306 | let pre: Ppre = pre.into(); | ||
| 307 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 308 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 309 | (freq, freq * 2) | ||
| 310 | } | ||
| 311 | }; | ||
| 312 | |||
| 313 | set_freqs(Clocks { | ||
| 314 | sys: sys_clk.hz(), | ||
| 315 | ahb: ahb_freq.hz(), | ||
| 316 | apb1: apb1_freq.hz(), | ||
| 317 | apb2: apb2_freq.hz(), | ||
| 318 | apb1_tim: apb1_tim_freq.hz(), | ||
| 319 | apb2_tim: apb2_tim_freq.hz(), | ||
| 320 | }); | ||
| 321 | } | ||
diff --git a/embassy-stm32/src/rcc/l1/mod.rs b/embassy-stm32/src/rcc/l1/mod.rs deleted file mode 100644 index e46bee323..000000000 --- a/embassy-stm32/src/rcc/l1/mod.rs +++ /dev/null | |||
| @@ -1,261 +0,0 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::{self, RCC}; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use embassy::util::Unborrow; | ||
| 8 | use embassy_hal_common::unborrow; | ||
| 9 | |||
| 10 | /// Most of clock setup is copied from rcc/l0 | ||
| 11 | |||
| 12 | /// HSI speed | ||
| 13 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 14 | |||
| 15 | /// System clock mux source | ||
| 16 | #[derive(Clone, Copy)] | ||
| 17 | pub enum ClockSrc { | ||
| 18 | MSI(MSIRange), | ||
| 19 | HSE(Hertz), | ||
| 20 | HSI, | ||
| 21 | } | ||
| 22 | |||
| 23 | /// MSI Clock Range | ||
| 24 | /// | ||
| 25 | /// These ranges control the frequency of the MSI. Internally, these ranges map | ||
| 26 | /// to the `MSIRANGE` bits in the `RCC_ICSCR` register. | ||
| 27 | #[derive(Clone, Copy)] | ||
| 28 | pub enum MSIRange { | ||
| 29 | /// Around 65.536 kHz | ||
| 30 | Range0, | ||
| 31 | /// Around 131.072 kHz | ||
| 32 | Range1, | ||
| 33 | /// Around 262.144 kHz | ||
| 34 | Range2, | ||
| 35 | /// Around 524.288 kHz | ||
| 36 | Range3, | ||
| 37 | /// Around 1.048 MHz | ||
| 38 | Range4, | ||
| 39 | /// Around 2.097 MHz (reset value) | ||
| 40 | Range5, | ||
| 41 | /// Around 4.194 MHz | ||
| 42 | Range6, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl Default for MSIRange { | ||
| 46 | fn default() -> MSIRange { | ||
| 47 | MSIRange::Range5 | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | /// AHB prescaler | ||
| 52 | #[derive(Clone, Copy, PartialEq)] | ||
| 53 | pub enum AHBPrescaler { | ||
| 54 | NotDivided, | ||
| 55 | Div2, | ||
| 56 | Div4, | ||
| 57 | Div8, | ||
| 58 | Div16, | ||
| 59 | Div64, | ||
| 60 | Div128, | ||
| 61 | Div256, | ||
| 62 | Div512, | ||
| 63 | } | ||
| 64 | |||
| 65 | /// APB prescaler | ||
| 66 | #[derive(Clone, Copy)] | ||
| 67 | pub enum APBPrescaler { | ||
| 68 | NotDivided, | ||
| 69 | Div2, | ||
| 70 | Div4, | ||
| 71 | Div8, | ||
| 72 | Div16, | ||
| 73 | } | ||
| 74 | |||
| 75 | type Ppre = u8; | ||
| 76 | impl Into<Ppre> for APBPrescaler { | ||
| 77 | fn into(self) -> Ppre { | ||
| 78 | match self { | ||
| 79 | APBPrescaler::NotDivided => 0b000, | ||
| 80 | APBPrescaler::Div2 => 0b100, | ||
| 81 | APBPrescaler::Div4 => 0b101, | ||
| 82 | APBPrescaler::Div8 => 0b110, | ||
| 83 | APBPrescaler::Div16 => 0b111, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | type Hpre = u8; | ||
| 89 | impl Into<Hpre> for AHBPrescaler { | ||
| 90 | fn into(self) -> Hpre { | ||
| 91 | match self { | ||
| 92 | AHBPrescaler::NotDivided => 0b0000, | ||
| 93 | AHBPrescaler::Div2 => 0b1000, | ||
| 94 | AHBPrescaler::Div4 => 0b1001, | ||
| 95 | AHBPrescaler::Div8 => 0b1010, | ||
| 96 | AHBPrescaler::Div16 => 0b1011, | ||
| 97 | AHBPrescaler::Div64 => 0b1100, | ||
| 98 | AHBPrescaler::Div128 => 0b1101, | ||
| 99 | AHBPrescaler::Div256 => 0b1110, | ||
| 100 | AHBPrescaler::Div512 => 0b1111, | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | impl Into<u8> for MSIRange { | ||
| 106 | fn into(self) -> u8 { | ||
| 107 | match self { | ||
| 108 | MSIRange::Range0 => 0b000, | ||
| 109 | MSIRange::Range1 => 0b001, | ||
| 110 | MSIRange::Range2 => 0b010, | ||
| 111 | MSIRange::Range3 => 0b011, | ||
| 112 | MSIRange::Range4 => 0b100, | ||
| 113 | MSIRange::Range5 => 0b101, | ||
| 114 | MSIRange::Range6 => 0b110, | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Clocks configutation | ||
| 120 | pub struct Config { | ||
| 121 | pub mux: ClockSrc, | ||
| 122 | pub ahb_pre: AHBPrescaler, | ||
| 123 | pub apb1_pre: APBPrescaler, | ||
| 124 | pub apb2_pre: APBPrescaler, | ||
| 125 | } | ||
| 126 | |||
| 127 | impl Default for Config { | ||
| 128 | #[inline] | ||
| 129 | fn default() -> Config { | ||
| 130 | Config { | ||
| 131 | mux: ClockSrc::MSI(MSIRange::default()), | ||
| 132 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 133 | apb1_pre: APBPrescaler::NotDivided, | ||
| 134 | apb2_pre: APBPrescaler::NotDivided, | ||
| 135 | } | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | /// RCC peripheral | ||
| 140 | pub struct Rcc<'d> { | ||
| 141 | _rb: peripherals::RCC, | ||
| 142 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 143 | } | ||
| 144 | |||
| 145 | impl<'d> Rcc<'d> { | ||
| 146 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | ||
| 147 | unborrow!(rcc); | ||
| 148 | Self { | ||
| 149 | _rb: rcc, | ||
| 150 | phantom: PhantomData, | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | // Safety: RCC init must have been called | ||
| 155 | pub fn clocks(&self) -> &'static Clocks { | ||
| 156 | unsafe { get_freqs() } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | ||
| 161 | pub trait RccExt { | ||
| 162 | fn freeze(self, config: Config) -> Clocks; | ||
| 163 | } | ||
| 164 | |||
| 165 | impl RccExt for RCC { | ||
| 166 | // `cfgr` is almost always a constant, so make sure it can be constant-propagated properly by | ||
| 167 | // marking this function and all `Config` constructors and setters as `#[inline]`. | ||
| 168 | // This saves ~900 Bytes for the `pwr.rs` example. | ||
| 169 | #[inline] | ||
| 170 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 171 | let rcc = pac::RCC; | ||
| 172 | let (sys_clk, sw) = match cfgr.mux { | ||
| 173 | ClockSrc::MSI(range) => { | ||
| 174 | // Set MSI range | ||
| 175 | unsafe { | ||
| 176 | rcc.icscr().write(|w| w.set_msirange(range.into())); | ||
| 177 | } | ||
| 178 | |||
| 179 | // Enable MSI | ||
| 180 | unsafe { | ||
| 181 | rcc.cr().write(|w| w.set_msion(true)); | ||
| 182 | while !rcc.cr().read().msirdy() {} | ||
| 183 | } | ||
| 184 | |||
| 185 | let freq = 32_768 * (1 << (range as u8 + 1)); | ||
| 186 | (freq, 0b00) | ||
| 187 | } | ||
| 188 | ClockSrc::HSI => { | ||
| 189 | // Enable HSI | ||
| 190 | unsafe { | ||
| 191 | rcc.cr().write(|w| w.set_hsion(true)); | ||
| 192 | while !rcc.cr().read().hsirdy() {} | ||
| 193 | } | ||
| 194 | |||
| 195 | (HSI_FREQ, 0b01) | ||
| 196 | } | ||
| 197 | ClockSrc::HSE(freq) => { | ||
| 198 | // Enable HSE | ||
| 199 | unsafe { | ||
| 200 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 201 | while !rcc.cr().read().hserdy() {} | ||
| 202 | } | ||
| 203 | |||
| 204 | (freq.0, 0b10) | ||
| 205 | } | ||
| 206 | }; | ||
| 207 | |||
| 208 | unsafe { | ||
| 209 | rcc.cfgr().modify(|w| { | ||
| 210 | w.set_sw(sw.into()); | ||
| 211 | w.set_hpre(cfgr.ahb_pre.into()); | ||
| 212 | w.set_ppre1(cfgr.apb1_pre.into()); | ||
| 213 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 214 | }); | ||
| 215 | } | ||
| 216 | |||
| 217 | let ahb_freq: u32 = match cfgr.ahb_pre { | ||
| 218 | AHBPrescaler::NotDivided => sys_clk, | ||
| 219 | pre => { | ||
| 220 | let pre: Hpre = pre.into(); | ||
| 221 | let pre = 1 << (pre as u32 - 7); | ||
| 222 | sys_clk / pre | ||
| 223 | } | ||
| 224 | }; | ||
| 225 | |||
| 226 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | ||
| 227 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 228 | pre => { | ||
| 229 | let pre: Ppre = pre.into(); | ||
| 230 | let pre: u8 = 1 << (pre - 3); | ||
| 231 | let freq = ahb_freq / pre as u32; | ||
| 232 | (freq, freq * 2) | ||
| 233 | } | ||
| 234 | }; | ||
| 235 | |||
| 236 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | ||
| 237 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 238 | pre => { | ||
| 239 | let pre: Ppre = pre.into(); | ||
| 240 | let pre: u8 = 1 << (pre - 3); | ||
| 241 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 242 | (freq, freq * 2) | ||
| 243 | } | ||
| 244 | }; | ||
| 245 | |||
| 246 | Clocks { | ||
| 247 | sys: sys_clk.hz(), | ||
| 248 | ahb: ahb_freq.hz(), | ||
| 249 | apb1: apb1_freq.hz(), | ||
| 250 | apb2: apb2_freq.hz(), | ||
| 251 | apb1_tim: apb1_tim_freq.hz(), | ||
| 252 | apb2_tim: apb2_tim_freq.hz(), | ||
| 253 | } | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | pub unsafe fn init(config: Config) { | ||
| 258 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 259 | let clocks = r.freeze(config); | ||
| 260 | set_freqs(clocks); | ||
| 261 | } | ||
diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs new file mode 100644 index 000000000..68b960d7c --- /dev/null +++ b/embassy-stm32/src/rcc/l4.rs | |||
| @@ -0,0 +1,429 @@ | |||
| 1 | use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; | ||
| 2 | use crate::pac::{FLASH, RCC}; | ||
| 3 | use crate::rcc::{set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | |||
| 7 | /// HSI16 speed | ||
| 8 | pub const HSI16_FREQ: u32 = 16_000_000; | ||
| 9 | |||
| 10 | /// System clock mux source | ||
| 11 | #[derive(Clone, Copy)] | ||
| 12 | pub enum ClockSrc { | ||
| 13 | MSI(MSIRange), | ||
| 14 | PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>), | ||
| 15 | HSE(Hertz), | ||
| 16 | HSI16, | ||
| 17 | } | ||
| 18 | |||
| 19 | /// MSI Clock Range | ||
| 20 | /// | ||
| 21 | /// These ranges control the frequency of the MSI. Internally, these ranges map | ||
| 22 | /// to the `MSIRANGE` bits in the `RCC_ICSCR` register. | ||
| 23 | #[derive(Clone, Copy)] | ||
| 24 | pub enum MSIRange { | ||
| 25 | /// Around 100 kHz | ||
| 26 | Range0, | ||
| 27 | /// Around 200 kHz | ||
| 28 | Range1, | ||
| 29 | /// Around 400 kHz | ||
| 30 | Range2, | ||
| 31 | /// Around 800 kHz | ||
| 32 | Range3, | ||
| 33 | /// Around 1 MHz | ||
| 34 | Range4, | ||
| 35 | /// Around 2 MHz | ||
| 36 | Range5, | ||
| 37 | /// Around 4 MHz (reset value) | ||
| 38 | Range6, | ||
| 39 | /// Around 8 MHz | ||
| 40 | Range7, | ||
| 41 | /// Around 16 MHz | ||
| 42 | Range8, | ||
| 43 | /// Around 24 MHz | ||
| 44 | Range9, | ||
| 45 | /// Around 32 MHz | ||
| 46 | Range10, | ||
| 47 | /// Around 48 MHz | ||
| 48 | Range11, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl Default for MSIRange { | ||
| 52 | fn default() -> MSIRange { | ||
| 53 | MSIRange::Range6 | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | pub type PLL48Div = PLLClkDiv; | ||
| 58 | |||
| 59 | /// PLL divider | ||
| 60 | #[derive(Clone, Copy)] | ||
| 61 | pub enum PLLDiv { | ||
| 62 | Div2, | ||
| 63 | Div3, | ||
| 64 | Div4, | ||
| 65 | } | ||
| 66 | |||
| 67 | /// AHB prescaler | ||
| 68 | #[derive(Clone, Copy, PartialEq)] | ||
| 69 | pub enum AHBPrescaler { | ||
| 70 | NotDivided, | ||
| 71 | Div2, | ||
| 72 | Div4, | ||
| 73 | Div8, | ||
| 74 | Div16, | ||
| 75 | Div64, | ||
| 76 | Div128, | ||
| 77 | Div256, | ||
| 78 | Div512, | ||
| 79 | } | ||
| 80 | |||
| 81 | /// APB prescaler | ||
| 82 | #[derive(Clone, Copy)] | ||
| 83 | pub enum APBPrescaler { | ||
| 84 | NotDivided, | ||
| 85 | Div2, | ||
| 86 | Div4, | ||
| 87 | Div8, | ||
| 88 | Div16, | ||
| 89 | } | ||
| 90 | |||
| 91 | /// PLL clock input source | ||
| 92 | #[derive(Clone, Copy)] | ||
| 93 | pub enum PLLSource { | ||
| 94 | HSI16, | ||
| 95 | HSE(Hertz), | ||
| 96 | } | ||
| 97 | |||
| 98 | seq_macro::seq!(N in 8..=86 { | ||
| 99 | #[derive(Clone, Copy)] | ||
| 100 | pub enum PLLMul { | ||
| 101 | #( | ||
| 102 | Mul#N, | ||
| 103 | )* | ||
| 104 | } | ||
| 105 | |||
| 106 | impl From<PLLMul> for u8 { | ||
| 107 | fn from(val: PLLMul) -> u8 { | ||
| 108 | match val { | ||
| 109 | #( | ||
| 110 | PLLMul::Mul#N => N, | ||
| 111 | )* | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | impl PLLMul { | ||
| 117 | pub fn to_mul(self) -> u32 { | ||
| 118 | match self { | ||
| 119 | #( | ||
| 120 | PLLMul::Mul#N => N, | ||
| 121 | )* | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | }); | ||
| 126 | |||
| 127 | #[derive(Clone, Copy)] | ||
| 128 | pub enum PLLClkDiv { | ||
| 129 | Div2, | ||
| 130 | Div4, | ||
| 131 | Div6, | ||
| 132 | Div8, | ||
| 133 | } | ||
| 134 | |||
| 135 | impl PLLClkDiv { | ||
| 136 | pub fn to_div(self) -> u32 { | ||
| 137 | let val: u8 = self.into(); | ||
| 138 | (val as u32 + 1) * 2 | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | impl From<PLLClkDiv> for u8 { | ||
| 143 | fn from(val: PLLClkDiv) -> u8 { | ||
| 144 | match val { | ||
| 145 | PLLClkDiv::Div2 => 0b00, | ||
| 146 | PLLClkDiv::Div4 => 0b01, | ||
| 147 | PLLClkDiv::Div6 => 0b10, | ||
| 148 | PLLClkDiv::Div8 => 0b11, | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | #[derive(Clone, Copy)] | ||
| 154 | pub enum PLLSrcDiv { | ||
| 155 | Div1, | ||
| 156 | Div2, | ||
| 157 | Div3, | ||
| 158 | Div4, | ||
| 159 | Div5, | ||
| 160 | Div6, | ||
| 161 | Div7, | ||
| 162 | Div8, | ||
| 163 | } | ||
| 164 | |||
| 165 | impl PLLSrcDiv { | ||
| 166 | pub fn to_div(self) -> u32 { | ||
| 167 | let val: u8 = self.into(); | ||
| 168 | val as u32 + 1 | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | impl From<PLLSrcDiv> for u8 { | ||
| 173 | fn from(val: PLLSrcDiv) -> u8 { | ||
| 174 | match val { | ||
| 175 | PLLSrcDiv::Div1 => 0b000, | ||
| 176 | PLLSrcDiv::Div2 => 0b001, | ||
| 177 | PLLSrcDiv::Div3 => 0b010, | ||
| 178 | PLLSrcDiv::Div4 => 0b011, | ||
| 179 | PLLSrcDiv::Div5 => 0b100, | ||
| 180 | PLLSrcDiv::Div6 => 0b101, | ||
| 181 | PLLSrcDiv::Div7 => 0b110, | ||
| 182 | PLLSrcDiv::Div8 => 0b111, | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | impl From<PLLSource> for Pllsrc { | ||
| 188 | fn from(val: PLLSource) -> Pllsrc { | ||
| 189 | match val { | ||
| 190 | PLLSource::HSI16 => Pllsrc::HSI16, | ||
| 191 | PLLSource::HSE(_) => Pllsrc::HSE, | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | impl From<APBPrescaler> for Ppre { | ||
| 197 | fn from(val: APBPrescaler) -> Ppre { | ||
| 198 | match val { | ||
| 199 | APBPrescaler::NotDivided => Ppre::DIV1, | ||
| 200 | APBPrescaler::Div2 => Ppre::DIV2, | ||
| 201 | APBPrescaler::Div4 => Ppre::DIV4, | ||
| 202 | APBPrescaler::Div8 => Ppre::DIV8, | ||
| 203 | APBPrescaler::Div16 => Ppre::DIV16, | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | impl From<AHBPrescaler> for Hpre { | ||
| 209 | fn from(val: AHBPrescaler) -> Hpre { | ||
| 210 | match val { | ||
| 211 | AHBPrescaler::NotDivided => Hpre::DIV1, | ||
| 212 | AHBPrescaler::Div2 => Hpre::DIV2, | ||
| 213 | AHBPrescaler::Div4 => Hpre::DIV4, | ||
| 214 | AHBPrescaler::Div8 => Hpre::DIV8, | ||
| 215 | AHBPrescaler::Div16 => Hpre::DIV16, | ||
| 216 | AHBPrescaler::Div64 => Hpre::DIV64, | ||
| 217 | AHBPrescaler::Div128 => Hpre::DIV128, | ||
| 218 | AHBPrescaler::Div256 => Hpre::DIV256, | ||
| 219 | AHBPrescaler::Div512 => Hpre::DIV512, | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | impl From<MSIRange> for Msirange { | ||
| 225 | fn from(val: MSIRange) -> Msirange { | ||
| 226 | match val { | ||
| 227 | MSIRange::Range0 => Msirange::RANGE100K, | ||
| 228 | MSIRange::Range1 => Msirange::RANGE200K, | ||
| 229 | MSIRange::Range2 => Msirange::RANGE400K, | ||
| 230 | MSIRange::Range3 => Msirange::RANGE800K, | ||
| 231 | MSIRange::Range4 => Msirange::RANGE1M, | ||
| 232 | MSIRange::Range5 => Msirange::RANGE2M, | ||
| 233 | MSIRange::Range6 => Msirange::RANGE4M, | ||
| 234 | MSIRange::Range7 => Msirange::RANGE8M, | ||
| 235 | MSIRange::Range8 => Msirange::RANGE16M, | ||
| 236 | MSIRange::Range9 => Msirange::RANGE24M, | ||
| 237 | MSIRange::Range10 => Msirange::RANGE32M, | ||
| 238 | MSIRange::Range11 => Msirange::RANGE48M, | ||
| 239 | } | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | impl From<MSIRange> for u32 { | ||
| 244 | fn from(val: MSIRange) -> u32 { | ||
| 245 | match val { | ||
| 246 | MSIRange::Range0 => 100_000, | ||
| 247 | MSIRange::Range1 => 200_000, | ||
| 248 | MSIRange::Range2 => 400_000, | ||
| 249 | MSIRange::Range3 => 800_000, | ||
| 250 | MSIRange::Range4 => 1_000_000, | ||
| 251 | MSIRange::Range5 => 2_000_000, | ||
| 252 | MSIRange::Range6 => 4_000_000, | ||
| 253 | MSIRange::Range7 => 8_000_000, | ||
| 254 | MSIRange::Range8 => 16_000_000, | ||
| 255 | MSIRange::Range9 => 24_000_000, | ||
| 256 | MSIRange::Range10 => 32_000_000, | ||
| 257 | MSIRange::Range11 => 48_000_000, | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Clocks configutation | ||
| 263 | pub struct Config { | ||
| 264 | pub mux: ClockSrc, | ||
| 265 | pub ahb_pre: AHBPrescaler, | ||
| 266 | pub apb1_pre: APBPrescaler, | ||
| 267 | pub apb2_pre: APBPrescaler, | ||
| 268 | } | ||
| 269 | |||
| 270 | impl Default for Config { | ||
| 271 | #[inline] | ||
| 272 | fn default() -> Config { | ||
| 273 | Config { | ||
| 274 | mux: ClockSrc::MSI(MSIRange::Range6), | ||
| 275 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 276 | apb1_pre: APBPrescaler::NotDivided, | ||
| 277 | apb2_pre: APBPrescaler::NotDivided, | ||
| 278 | } | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | pub(crate) unsafe fn init(config: Config) { | ||
| 283 | let (sys_clk, sw) = match config.mux { | ||
| 284 | ClockSrc::MSI(range) => { | ||
| 285 | // Enable MSI | ||
| 286 | RCC.cr().write(|w| { | ||
| 287 | let bits: Msirange = range.into(); | ||
| 288 | w.set_msirange(bits); | ||
| 289 | w.set_msipllen(false); | ||
| 290 | w.set_msirgsel(true); | ||
| 291 | w.set_msion(true); | ||
| 292 | }); | ||
| 293 | while !RCC.cr().read().msirdy() {} | ||
| 294 | |||
| 295 | // Enable as clock source for USB, RNG if running at 48 MHz | ||
| 296 | if let MSIRange::Range11 = range { | ||
| 297 | RCC.ccipr().modify(|w| { | ||
| 298 | w.set_clk48sel(0b11); | ||
| 299 | }); | ||
| 300 | } | ||
| 301 | (range.into(), Sw::MSI) | ||
| 302 | } | ||
| 303 | ClockSrc::HSI16 => { | ||
| 304 | // Enable HSI16 | ||
| 305 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 306 | while !RCC.cr().read().hsirdy() {} | ||
| 307 | |||
| 308 | (HSI16_FREQ, Sw::HSI16) | ||
| 309 | } | ||
| 310 | ClockSrc::HSE(freq) => { | ||
| 311 | // Enable HSE | ||
| 312 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 313 | while !RCC.cr().read().hserdy() {} | ||
| 314 | |||
| 315 | (freq.0, Sw::HSE) | ||
| 316 | } | ||
| 317 | ClockSrc::PLL(src, div, prediv, mul, pll48div) => { | ||
| 318 | let freq = match src { | ||
| 319 | PLLSource::HSE(freq) => { | ||
| 320 | // Enable HSE | ||
| 321 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 322 | while !RCC.cr().read().hserdy() {} | ||
| 323 | freq.0 | ||
| 324 | } | ||
| 325 | PLLSource::HSI16 => { | ||
| 326 | // Enable HSI | ||
| 327 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 328 | while !RCC.cr().read().hsirdy() {} | ||
| 329 | HSI16_FREQ | ||
| 330 | } | ||
| 331 | }; | ||
| 332 | |||
| 333 | // Disable PLL | ||
| 334 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 335 | while RCC.cr().read().pllrdy() {} | ||
| 336 | |||
| 337 | let freq = (freq / prediv.to_div() * mul.to_mul()) / div.to_div(); | ||
| 338 | |||
| 339 | assert!(freq <= 80_000_000); | ||
| 340 | |||
| 341 | RCC.pllcfgr().write(move |w| { | ||
| 342 | w.set_plln(mul.into()); | ||
| 343 | w.set_pllm(prediv.into()); | ||
| 344 | w.set_pllr(div.into()); | ||
| 345 | if let Some(pll48div) = pll48div { | ||
| 346 | w.set_pllq(pll48div.into()); | ||
| 347 | w.set_pllqen(true); | ||
| 348 | } | ||
| 349 | w.set_pllsrc(src.into()); | ||
| 350 | }); | ||
| 351 | |||
| 352 | // Enable as clock source for USB, RNG if PLL48 divisor is provided | ||
| 353 | if pll48div.is_some() { | ||
| 354 | RCC.ccipr().modify(|w| { | ||
| 355 | w.set_clk48sel(0b10); | ||
| 356 | }); | ||
| 357 | } | ||
| 358 | |||
| 359 | // Enable PLL | ||
| 360 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 361 | while !RCC.cr().read().pllrdy() {} | ||
| 362 | RCC.pllcfgr().modify(|w| w.set_pllren(true)); | ||
| 363 | |||
| 364 | (freq, Sw::PLL) | ||
| 365 | } | ||
| 366 | }; | ||
| 367 | |||
| 368 | // Set flash wait states | ||
| 369 | FLASH.acr().modify(|w| { | ||
| 370 | w.set_latency(if sys_clk <= 16_000_000 { | ||
| 371 | 0b000 | ||
| 372 | } else if sys_clk <= 32_000_000 { | ||
| 373 | 0b001 | ||
| 374 | } else if sys_clk <= 48_000_000 { | ||
| 375 | 0b010 | ||
| 376 | } else if sys_clk <= 64_000_000 { | ||
| 377 | 0b011 | ||
| 378 | } else { | ||
| 379 | 0b100 | ||
| 380 | }); | ||
| 381 | }); | ||
| 382 | |||
| 383 | RCC.cfgr().modify(|w| { | ||
| 384 | w.set_sw(sw); | ||
| 385 | w.set_hpre(config.ahb_pre.into()); | ||
| 386 | w.set_ppre1(config.apb1_pre.into()); | ||
| 387 | w.set_ppre2(config.apb2_pre.into()); | ||
| 388 | }); | ||
| 389 | |||
| 390 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 391 | AHBPrescaler::NotDivided => sys_clk, | ||
| 392 | pre => { | ||
| 393 | let pre: Hpre = pre.into(); | ||
| 394 | let pre = 1 << (pre.0 as u32 - 7); | ||
| 395 | sys_clk / pre | ||
| 396 | } | ||
| 397 | }; | ||
| 398 | |||
| 399 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 400 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 401 | pre => { | ||
| 402 | let pre: Ppre = pre.into(); | ||
| 403 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 404 | let freq = ahb_freq / pre as u32; | ||
| 405 | (freq, freq * 2) | ||
| 406 | } | ||
| 407 | }; | ||
| 408 | |||
| 409 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 410 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 411 | pre => { | ||
| 412 | let pre: Ppre = pre.into(); | ||
| 413 | let pre: u8 = 1 << (pre.0 - 3); | ||
| 414 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 415 | (freq, freq * 2) | ||
| 416 | } | ||
| 417 | }; | ||
| 418 | |||
| 419 | set_freqs(Clocks { | ||
| 420 | sys: sys_clk.hz(), | ||
| 421 | ahb1: ahb_freq.hz(), | ||
| 422 | ahb2: ahb_freq.hz(), | ||
| 423 | ahb3: ahb_freq.hz(), | ||
| 424 | apb1: apb1_freq.hz(), | ||
| 425 | apb2: apb2_freq.hz(), | ||
| 426 | apb1_tim: apb1_tim_freq.hz(), | ||
| 427 | apb2_tim: apb2_tim_freq.hz(), | ||
| 428 | }); | ||
| 429 | } | ||
diff --git a/embassy-stm32/src/rcc/l4/mod.rs b/embassy-stm32/src/rcc/l4/mod.rs deleted file mode 100644 index a0eedf0b6..000000000 --- a/embassy-stm32/src/rcc/l4/mod.rs +++ /dev/null | |||
| @@ -1,489 +0,0 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::{self, RCC}; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use embassy::util::Unborrow; | ||
| 8 | use embassy_hal_common::unborrow; | ||
| 9 | use stm32_metapac::rcc::vals::Msirange; | ||
| 10 | |||
| 11 | /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||
| 12 | /// and with the addition of the init function to configure a system clock. | ||
| 13 | |||
| 14 | /// Only the basic setup using the HSE and HSI clocks are supported as of now. | ||
| 15 | |||
| 16 | /// HSI16 speed | ||
| 17 | pub const HSI16_FREQ: u32 = 16_000_000; | ||
| 18 | |||
| 19 | /// System clock mux source | ||
| 20 | #[derive(Clone, Copy)] | ||
| 21 | pub enum ClockSrc { | ||
| 22 | PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>), | ||
| 23 | MSI(MSIRange), | ||
| 24 | HSE(Hertz), | ||
| 25 | HSI16, | ||
| 26 | } | ||
| 27 | |||
| 28 | /// MSI Clock Range | ||
| 29 | /// | ||
| 30 | /// These ranges control the frequency of the MSI. Internally, these ranges map | ||
| 31 | /// to the `MSIRANGE` bits in the `RCC_ICSCR` register. | ||
| 32 | #[derive(Clone, Copy)] | ||
| 33 | pub enum MSIRange { | ||
| 34 | /// Around 100 kHz | ||
| 35 | Range0, | ||
| 36 | /// Around 200 kHz | ||
| 37 | Range1, | ||
| 38 | /// Around 400 kHz | ||
| 39 | Range2, | ||
| 40 | /// Around 800 kHz | ||
| 41 | Range3, | ||
| 42 | /// Around 1 MHz | ||
| 43 | Range4, | ||
| 44 | /// Around 2 MHz | ||
| 45 | Range5, | ||
| 46 | /// Around 4 MHz (reset value) | ||
| 47 | Range6, | ||
| 48 | /// Around 8 MHz | ||
| 49 | Range7, | ||
| 50 | /// Around 16 MHz | ||
| 51 | Range8, | ||
| 52 | /// Around 24 MHz | ||
| 53 | Range9, | ||
| 54 | /// Around 32 MHz | ||
| 55 | Range10, | ||
| 56 | /// Around 48 MHz | ||
| 57 | Range11, | ||
| 58 | } | ||
| 59 | |||
| 60 | impl Into<u32> for MSIRange { | ||
| 61 | fn into(self) -> u32 { | ||
| 62 | match self { | ||
| 63 | MSIRange::Range0 => 100_000, | ||
| 64 | MSIRange::Range1 => 200_000, | ||
| 65 | MSIRange::Range2 => 400_000, | ||
| 66 | MSIRange::Range3 => 800_000, | ||
| 67 | MSIRange::Range4 => 1_000_000, | ||
| 68 | MSIRange::Range5 => 2_000_000, | ||
| 69 | MSIRange::Range6 => 4_000_000, | ||
| 70 | MSIRange::Range7 => 8_000_000, | ||
| 71 | MSIRange::Range8 => 16_000_000, | ||
| 72 | MSIRange::Range9 => 24_000_000, | ||
| 73 | MSIRange::Range10 => 32_000_000, | ||
| 74 | MSIRange::Range11 => 48_000_000, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | impl Default for MSIRange { | ||
| 80 | fn default() -> MSIRange { | ||
| 81 | MSIRange::Range6 | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | pub type PLL48Div = PLLClkDiv; | ||
| 86 | |||
| 87 | /// PLL divider | ||
| 88 | #[derive(Clone, Copy)] | ||
| 89 | pub enum PLLDiv { | ||
| 90 | Div2, | ||
| 91 | Div3, | ||
| 92 | Div4, | ||
| 93 | } | ||
| 94 | |||
| 95 | /// AHB prescaler | ||
| 96 | #[derive(Clone, Copy, PartialEq)] | ||
| 97 | pub enum AHBPrescaler { | ||
| 98 | NotDivided, | ||
| 99 | Div2, | ||
| 100 | Div4, | ||
| 101 | Div8, | ||
| 102 | Div16, | ||
| 103 | Div64, | ||
| 104 | Div128, | ||
| 105 | Div256, | ||
| 106 | Div512, | ||
| 107 | } | ||
| 108 | |||
| 109 | /// APB prescaler | ||
| 110 | #[derive(Clone, Copy)] | ||
| 111 | pub enum APBPrescaler { | ||
| 112 | NotDivided, | ||
| 113 | Div2, | ||
| 114 | Div4, | ||
| 115 | Div8, | ||
| 116 | Div16, | ||
| 117 | } | ||
| 118 | |||
| 119 | /// PLL clock input source | ||
| 120 | #[derive(Clone, Copy)] | ||
| 121 | pub enum PLLSource { | ||
| 122 | HSI16, | ||
| 123 | HSE(Hertz), | ||
| 124 | } | ||
| 125 | |||
| 126 | seq_macro::seq!(N in 8..=86 { | ||
| 127 | #[derive(Clone, Copy)] | ||
| 128 | pub enum PLLMul { | ||
| 129 | #( | ||
| 130 | Mul#N, | ||
| 131 | )* | ||
| 132 | } | ||
| 133 | |||
| 134 | impl Into<u8> for PLLMul { | ||
| 135 | fn into(self) -> u8 { | ||
| 136 | match self { | ||
| 137 | #( | ||
| 138 | PLLMul::Mul#N => N, | ||
| 139 | )* | ||
| 140 | } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | impl PLLMul { | ||
| 145 | pub fn to_mul(self) -> u32 { | ||
| 146 | match self { | ||
| 147 | #( | ||
| 148 | PLLMul::Mul#N => N, | ||
| 149 | )* | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | }); | ||
| 154 | |||
| 155 | #[derive(Clone, Copy)] | ||
| 156 | pub enum PLLClkDiv { | ||
| 157 | Div2, | ||
| 158 | Div4, | ||
| 159 | Div6, | ||
| 160 | Div8, | ||
| 161 | } | ||
| 162 | |||
| 163 | impl PLLClkDiv { | ||
| 164 | pub fn to_div(self) -> u32 { | ||
| 165 | let val: u8 = self.into(); | ||
| 166 | val as u32 + 1 * 2 | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | impl Into<u8> for PLLClkDiv { | ||
| 171 | fn into(self) -> u8 { | ||
| 172 | match self { | ||
| 173 | PLLClkDiv::Div2 => 0b00, | ||
| 174 | PLLClkDiv::Div4 => 0b01, | ||
| 175 | PLLClkDiv::Div6 => 0b10, | ||
| 176 | PLLClkDiv::Div8 => 0b11, | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | #[derive(Clone, Copy)] | ||
| 182 | pub enum PLLSrcDiv { | ||
| 183 | Div1, | ||
| 184 | Div2, | ||
| 185 | Div3, | ||
| 186 | Div4, | ||
| 187 | Div5, | ||
| 188 | Div6, | ||
| 189 | Div7, | ||
| 190 | Div8, | ||
| 191 | } | ||
| 192 | |||
| 193 | impl PLLSrcDiv { | ||
| 194 | pub fn to_div(self) -> u32 { | ||
| 195 | let val: u8 = self.into(); | ||
| 196 | val as u32 + 1 | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | impl Into<u8> for PLLSrcDiv { | ||
| 201 | fn into(self) -> u8 { | ||
| 202 | match self { | ||
| 203 | PLLSrcDiv::Div1 => 0b000, | ||
| 204 | PLLSrcDiv::Div2 => 0b001, | ||
| 205 | PLLSrcDiv::Div3 => 0b010, | ||
| 206 | PLLSrcDiv::Div4 => 0b011, | ||
| 207 | PLLSrcDiv::Div5 => 0b100, | ||
| 208 | PLLSrcDiv::Div6 => 0b101, | ||
| 209 | PLLSrcDiv::Div7 => 0b110, | ||
| 210 | PLLSrcDiv::Div8 => 0b111, | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | impl Into<u8> for PLLSource { | ||
| 216 | fn into(self) -> u8 { | ||
| 217 | match self { | ||
| 218 | PLLSource::HSI16 => 0b10, | ||
| 219 | PLLSource::HSE(_) => 0b11, | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | impl Into<Msirange> for MSIRange { | ||
| 225 | fn into(self) -> Msirange { | ||
| 226 | match self { | ||
| 227 | MSIRange::Range0 => Msirange::RANGE100K, | ||
| 228 | MSIRange::Range1 => Msirange::RANGE200K, | ||
| 229 | MSIRange::Range2 => Msirange::RANGE400K, | ||
| 230 | MSIRange::Range3 => Msirange::RANGE800K, | ||
| 231 | MSIRange::Range4 => Msirange::RANGE1M, | ||
| 232 | MSIRange::Range5 => Msirange::RANGE2M, | ||
| 233 | MSIRange::Range6 => Msirange::RANGE4M, | ||
| 234 | MSIRange::Range7 => Msirange::RANGE8M, | ||
| 235 | MSIRange::Range8 => Msirange::RANGE16M, | ||
| 236 | MSIRange::Range9 => Msirange::RANGE24M, | ||
| 237 | MSIRange::Range10 => Msirange::RANGE32M, | ||
| 238 | MSIRange::Range11 => Msirange::RANGE48M, | ||
| 239 | } | ||
| 240 | } | ||
| 241 | } | ||
| 242 | impl Into<u8> for APBPrescaler { | ||
| 243 | fn into(self) -> u8 { | ||
| 244 | match self { | ||
| 245 | APBPrescaler::NotDivided => 1, | ||
| 246 | APBPrescaler::Div2 => 0x04, | ||
| 247 | APBPrescaler::Div4 => 0x05, | ||
| 248 | APBPrescaler::Div8 => 0x06, | ||
| 249 | APBPrescaler::Div16 => 0x07, | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | impl Into<u8> for AHBPrescaler { | ||
| 255 | fn into(self) -> u8 { | ||
| 256 | match self { | ||
| 257 | AHBPrescaler::NotDivided => 1, | ||
| 258 | AHBPrescaler::Div2 => 0x08, | ||
| 259 | AHBPrescaler::Div4 => 0x09, | ||
| 260 | AHBPrescaler::Div8 => 0x0a, | ||
| 261 | AHBPrescaler::Div16 => 0x0b, | ||
| 262 | AHBPrescaler::Div64 => 0x0c, | ||
| 263 | AHBPrescaler::Div128 => 0x0d, | ||
| 264 | AHBPrescaler::Div256 => 0x0e, | ||
| 265 | AHBPrescaler::Div512 => 0x0f, | ||
| 266 | } | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Clocks configutation | ||
| 271 | pub struct Config { | ||
| 272 | pub mux: ClockSrc, | ||
| 273 | pub ahb_pre: AHBPrescaler, | ||
| 274 | pub apb1_pre: APBPrescaler, | ||
| 275 | pub apb2_pre: APBPrescaler, | ||
| 276 | } | ||
| 277 | |||
| 278 | impl Default for Config { | ||
| 279 | #[inline] | ||
| 280 | fn default() -> Config { | ||
| 281 | Config { | ||
| 282 | mux: ClockSrc::MSI(MSIRange::Range6), | ||
| 283 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 284 | apb1_pre: APBPrescaler::NotDivided, | ||
| 285 | apb2_pre: APBPrescaler::NotDivided, | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | /// RCC peripheral | ||
| 291 | pub struct Rcc<'d> { | ||
| 292 | _rb: peripherals::RCC, | ||
| 293 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 294 | } | ||
| 295 | |||
| 296 | impl<'d> Rcc<'d> { | ||
| 297 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | ||
| 298 | unborrow!(rcc); | ||
| 299 | Self { | ||
| 300 | _rb: rcc, | ||
| 301 | phantom: PhantomData, | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | // Safety: RCC init must have been called | ||
| 306 | pub fn clocks(&self) -> &'static Clocks { | ||
| 307 | unsafe { get_freqs() } | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | ||
| 312 | pub trait RccExt { | ||
| 313 | fn freeze(self, config: Config) -> Clocks; | ||
| 314 | } | ||
| 315 | |||
| 316 | impl RccExt for RCC { | ||
| 317 | #[inline] | ||
| 318 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 319 | let rcc = pac::RCC; | ||
| 320 | let (sys_clk, sw) = match cfgr.mux { | ||
| 321 | ClockSrc::HSI16 => { | ||
| 322 | // Enable HSI16 | ||
| 323 | unsafe { | ||
| 324 | rcc.cr().write(|w| w.set_hsion(true)); | ||
| 325 | while !rcc.cr().read().hsirdy() {} | ||
| 326 | } | ||
| 327 | |||
| 328 | (HSI16_FREQ, 0b01) | ||
| 329 | } | ||
| 330 | ClockSrc::HSE(freq) => { | ||
| 331 | // Enable HSE | ||
| 332 | unsafe { | ||
| 333 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 334 | while !rcc.cr().read().hserdy() {} | ||
| 335 | } | ||
| 336 | |||
| 337 | (freq.0, 0b10) | ||
| 338 | } | ||
| 339 | ClockSrc::MSI(range) => { | ||
| 340 | // Enable MSI | ||
| 341 | unsafe { | ||
| 342 | rcc.cr().write(|w| { | ||
| 343 | let bits: Msirange = range.into(); | ||
| 344 | w.set_msirange(bits); | ||
| 345 | w.set_msipllen(false); | ||
| 346 | w.set_msirgsel(true); | ||
| 347 | w.set_msion(true); | ||
| 348 | }); | ||
| 349 | while !rcc.cr().read().msirdy() {} | ||
| 350 | |||
| 351 | // Enable as clock source for USB, RNG if running at 48 MHz | ||
| 352 | if let MSIRange::Range11 = range { | ||
| 353 | rcc.ccipr().modify(|w| { | ||
| 354 | w.set_clk48sel(0b11); | ||
| 355 | }); | ||
| 356 | } | ||
| 357 | } | ||
| 358 | (range.into(), 0b00) | ||
| 359 | } | ||
| 360 | ClockSrc::PLL(src, div, prediv, mul, pll48div) => { | ||
| 361 | let freq = match src { | ||
| 362 | PLLSource::HSE(freq) => { | ||
| 363 | // Enable HSE | ||
| 364 | unsafe { | ||
| 365 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 366 | while !rcc.cr().read().hserdy() {} | ||
| 367 | } | ||
| 368 | freq.0 | ||
| 369 | } | ||
| 370 | PLLSource::HSI16 => { | ||
| 371 | // Enable HSI | ||
| 372 | unsafe { | ||
| 373 | rcc.cr().write(|w| w.set_hsion(true)); | ||
| 374 | while !rcc.cr().read().hsirdy() {} | ||
| 375 | } | ||
| 376 | HSI16_FREQ | ||
| 377 | } | ||
| 378 | }; | ||
| 379 | |||
| 380 | // Disable PLL | ||
| 381 | unsafe { | ||
| 382 | rcc.cr().modify(|w| w.set_pllon(false)); | ||
| 383 | while rcc.cr().read().pllrdy() {} | ||
| 384 | } | ||
| 385 | |||
| 386 | let freq = (freq / prediv.to_div() * mul.to_mul()) / div.to_div(); | ||
| 387 | |||
| 388 | assert!(freq <= 80_000_000); | ||
| 389 | |||
| 390 | unsafe { | ||
| 391 | rcc.pllcfgr().write(move |w| { | ||
| 392 | w.set_plln(mul.into()); | ||
| 393 | w.set_pllm(prediv.into()); | ||
| 394 | w.set_pllr(div.into()); | ||
| 395 | if let Some(pll48div) = pll48div { | ||
| 396 | w.set_pllq(pll48div.into()); | ||
| 397 | w.set_pllqen(true); | ||
| 398 | } | ||
| 399 | w.set_pllsrc(src.into()); | ||
| 400 | }); | ||
| 401 | |||
| 402 | // Enable as clock source for USB, RNG if PLL48 divisor is provided | ||
| 403 | if pll48div.is_some() { | ||
| 404 | rcc.ccipr().modify(|w| { | ||
| 405 | w.set_clk48sel(0b10); | ||
| 406 | }); | ||
| 407 | } | ||
| 408 | |||
| 409 | // Enable PLL | ||
| 410 | rcc.cr().modify(|w| w.set_pllon(true)); | ||
| 411 | while !rcc.cr().read().pllrdy() {} | ||
| 412 | rcc.pllcfgr().modify(|w| w.set_pllren(true)); | ||
| 413 | } | ||
| 414 | (freq, 0b11) | ||
| 415 | } | ||
| 416 | }; | ||
| 417 | |||
| 418 | unsafe { | ||
| 419 | // Set flash wait states | ||
| 420 | pac::FLASH.acr().modify(|w| { | ||
| 421 | w.set_latency(if sys_clk <= 16_000_000 { | ||
| 422 | 0b000 | ||
| 423 | } else if sys_clk <= 32_000_000 { | ||
| 424 | 0b001 | ||
| 425 | } else if sys_clk <= 48_000_000 { | ||
| 426 | 0b010 | ||
| 427 | } else if sys_clk <= 64_000_000 { | ||
| 428 | 0b011 | ||
| 429 | } else { | ||
| 430 | 0b100 | ||
| 431 | }); | ||
| 432 | }); | ||
| 433 | |||
| 434 | // Switch active clocks to new clock source | ||
| 435 | rcc.cfgr().modify(|w| { | ||
| 436 | w.set_sw(sw.into()); | ||
| 437 | w.set_hpre(cfgr.ahb_pre.into()); | ||
| 438 | w.set_ppre1(cfgr.apb1_pre.into()); | ||
| 439 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 440 | }); | ||
| 441 | } | ||
| 442 | |||
| 443 | let ahb_freq: u32 = match cfgr.ahb_pre { | ||
| 444 | AHBPrescaler::NotDivided => sys_clk, | ||
| 445 | pre => { | ||
| 446 | let pre: u8 = pre.into(); | ||
| 447 | let pre = 1 << (pre as u32 - 7); | ||
| 448 | sys_clk / pre | ||
| 449 | } | ||
| 450 | }; | ||
| 451 | |||
| 452 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | ||
| 453 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 454 | pre => { | ||
| 455 | let pre: u8 = pre.into(); | ||
| 456 | let pre: u8 = 1 << (pre - 3); | ||
| 457 | let freq = ahb_freq / pre as u32; | ||
| 458 | (freq, freq * 2) | ||
| 459 | } | ||
| 460 | }; | ||
| 461 | |||
| 462 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | ||
| 463 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 464 | pre => { | ||
| 465 | let pre: u8 = pre.into(); | ||
| 466 | let pre: u8 = 1 << (pre - 3); | ||
| 467 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 468 | (freq, freq * 2) | ||
| 469 | } | ||
| 470 | }; | ||
| 471 | |||
| 472 | Clocks { | ||
| 473 | sys: sys_clk.hz(), | ||
| 474 | ahb1: ahb_freq.hz(), | ||
| 475 | ahb2: ahb_freq.hz(), | ||
| 476 | ahb3: ahb_freq.hz(), | ||
| 477 | apb1: apb1_freq.hz(), | ||
| 478 | apb2: apb2_freq.hz(), | ||
| 479 | apb1_tim: apb1_tim_freq.hz(), | ||
| 480 | apb2_tim: apb2_tim_freq.hz(), | ||
| 481 | } | ||
| 482 | } | ||
| 483 | } | ||
| 484 | |||
| 485 | pub unsafe fn init(config: Config) { | ||
| 486 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 487 | let clocks = r.freeze(config); | ||
| 488 | set_freqs(clocks); | ||
| 489 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 0a7edb37a..d0b6e5a18 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -4,6 +4,23 @@ use crate::peripherals; | |||
| 4 | use crate::time::Hertz; | 4 | use crate::time::Hertz; |
| 5 | use core::mem::MaybeUninit; | 5 | use core::mem::MaybeUninit; |
| 6 | 6 | ||
| 7 | #[cfg_attr(any(rcc_f0, rcc_f0x0), path = "f0.rs")] | ||
| 8 | #[cfg_attr(rcc_f1, path = "f1.rs")] | ||
| 9 | #[cfg_attr(rcc_f3, path = "f3.rs")] | ||
| 10 | #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] | ||
| 11 | #[cfg_attr(rcc_f7, path = "f7.rs")] | ||
| 12 | #[cfg_attr(rcc_g0, path = "g0.rs")] | ||
| 13 | #[cfg_attr(rcc_g4, path = "g4.rs")] | ||
| 14 | #[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")] | ||
| 15 | #[cfg_attr(rcc_l0, path = "l0.rs")] | ||
| 16 | #[cfg_attr(rcc_l1, path = "l1.rs")] | ||
| 17 | #[cfg_attr(rcc_l4, path = "l4.rs")] | ||
| 18 | #[cfg_attr(rcc_u5, path = "u5.rs")] | ||
| 19 | #[cfg_attr(rcc_wb, path = "wb.rs")] | ||
| 20 | #[cfg_attr(rcc_wl5, path = "wl5.rs")] | ||
| 21 | mod _version; | ||
| 22 | pub use _version::*; | ||
| 23 | |||
| 7 | #[derive(Clone, Copy)] | 24 | #[derive(Clone, Copy)] |
| 8 | pub struct Clocks { | 25 | pub struct Clocks { |
| 9 | pub sys: Hertz, | 26 | pub sys: Hertz, |
| @@ -44,7 +61,7 @@ pub struct Clocks { | |||
| 44 | #[cfg(any(rcc_h7))] | 61 | #[cfg(any(rcc_h7))] |
| 45 | pub apb4: Hertz, | 62 | pub apb4: Hertz, |
| 46 | 63 | ||
| 47 | #[cfg(rcc_f4)] | 64 | #[cfg(any(rcc_f4, rcc_f7))] |
| 48 | pub pll48: Option<Hertz>, | 65 | pub pll48: Option<Hertz>, |
| 49 | 66 | ||
| 50 | #[cfg(rcc_f1)] | 67 | #[cfg(rcc_f1)] |
| @@ -59,61 +76,15 @@ static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit(); | |||
| 59 | /// Sets the clock frequencies | 76 | /// Sets the clock frequencies |
| 60 | /// | 77 | /// |
| 61 | /// Safety: Sets a mutable global. | 78 | /// Safety: Sets a mutable global. |
| 62 | pub unsafe fn set_freqs(freqs: Clocks) { | 79 | pub(crate) unsafe fn set_freqs(freqs: Clocks) { |
| 63 | CLOCK_FREQS.as_mut_ptr().write(freqs); | 80 | CLOCK_FREQS.as_mut_ptr().write(freqs); |
| 64 | } | 81 | } |
| 65 | 82 | ||
| 66 | /// Safety: Reads a mutable global. | 83 | /// Safety: Reads a mutable global. |
| 67 | pub unsafe fn get_freqs() -> &'static Clocks { | 84 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { |
| 68 | &*CLOCK_FREQS.as_ptr() | 85 | &*CLOCK_FREQS.as_ptr() |
| 69 | } | 86 | } |
| 70 | 87 | ||
| 71 | cfg_if::cfg_if! { | ||
| 72 | if #[cfg(rcc_h7)] { | ||
| 73 | mod h7; | ||
| 74 | pub use h7::*; | ||
| 75 | } else if #[cfg(rcc_l0)] { | ||
| 76 | mod l0; | ||
| 77 | pub use l0::*; | ||
| 78 | } else if #[cfg(rcc_l1)] { | ||
| 79 | mod l1; | ||
| 80 | pub use l1::*; | ||
| 81 | } else if #[cfg(rcc_l4)] { | ||
| 82 | mod l4; | ||
| 83 | pub use l4::*; | ||
| 84 | } else if #[cfg(rcc_f1)] { | ||
| 85 | mod f1; | ||
| 86 | pub use f1::*; | ||
| 87 | } else if #[cfg(rcc_f3)] { | ||
| 88 | mod f3; | ||
| 89 | pub use f3::*; | ||
| 90 | } else if #[cfg(rcc_f4)] { | ||
| 91 | mod f4; | ||
| 92 | pub use f4::*; | ||
| 93 | } else if #[cfg(rcc_f7)] { | ||
| 94 | mod f7; | ||
| 95 | pub use f7::*; | ||
| 96 | } else if #[cfg(rcc_wb)] { | ||
| 97 | mod wb; | ||
| 98 | pub use wb::*; | ||
| 99 | } else if #[cfg(rcc_wl5)] { | ||
| 100 | mod wl5x; | ||
| 101 | pub use wl5x::*; | ||
| 102 | } else if #[cfg(any(rcc_f0, rcc_f0x0))] { | ||
| 103 | mod f0; | ||
| 104 | pub use f0::*; | ||
| 105 | } else if #[cfg(any(rcc_g0))] { | ||
| 106 | mod g0; | ||
| 107 | pub use g0::*; | ||
| 108 | } else if #[cfg(any(rcc_g4))] { | ||
| 109 | mod g4; | ||
| 110 | pub use g4::*; | ||
| 111 | } else if #[cfg(any(rcc_u5))] { | ||
| 112 | mod u5; | ||
| 113 | pub use u5::*; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | pub(crate) mod sealed { | 88 | pub(crate) mod sealed { |
| 118 | pub trait RccPeripheral { | 89 | pub trait RccPeripheral { |
| 119 | fn frequency() -> crate::time::Hertz; | 90 | fn frequency() -> crate::time::Hertz; |
diff --git a/embassy-stm32/src/rcc/u5/mod.rs b/embassy-stm32/src/rcc/u5.rs index a3df3a02d..ac1cd06c8 100644 --- a/embassy-stm32/src/rcc/u5/mod.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -1,6 +1,4 @@ | |||
| 1 | use crate::pac; | 1 | use crate::pac::{FLASH, RCC}; |
| 2 | use crate::peripherals::{self, RCC}; | ||
| 3 | use crate::pwr::{Power, VoltageScale}; | ||
| 4 | use crate::rcc::{set_freqs, Clocks}; | 2 | use crate::rcc::{set_freqs, Clocks}; |
| 5 | use crate::time::{Hertz, U32Ext}; | 3 | use crate::time::{Hertz, U32Ext}; |
| 6 | use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw}; | 4 | use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw}; |
| @@ -8,6 +6,20 @@ use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw} | |||
| 8 | /// HSI16 speed | 6 | /// HSI16 speed |
| 9 | pub const HSI16_FREQ: u32 = 16_000_000; | 7 | pub const HSI16_FREQ: u32 = 16_000_000; |
| 10 | 8 | ||
| 9 | /// Voltage Scale | ||
| 10 | /// | ||
| 11 | /// Represents the voltage range feeding the CPU core. The maximum core | ||
| 12 | /// clock frequency depends on this value. | ||
| 13 | #[derive(Copy, Clone, PartialEq)] | ||
| 14 | pub enum VoltageScale { | ||
| 15 | // Highest frequency | ||
| 16 | Range1, | ||
| 17 | Range2, | ||
| 18 | Range3, | ||
| 19 | // Lowest power | ||
| 20 | Range4, | ||
| 21 | } | ||
| 22 | |||
| 11 | #[derive(Copy, Clone)] | 23 | #[derive(Copy, Clone)] |
| 12 | pub enum ClockSrc { | 24 | pub enum ClockSrc { |
| 13 | MSI(MSIRange), | 25 | MSI(MSIRange), |
| @@ -293,218 +305,188 @@ impl Default for Config { | |||
| 293 | } | 305 | } |
| 294 | } | 306 | } |
| 295 | 307 | ||
| 296 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | 308 | pub(crate) unsafe fn init(config: Config) { |
| 297 | pub trait RccExt { | 309 | let sys_clk = match config.mux { |
| 298 | fn freeze(self, config: Config, power: &Power) -> Clocks; | 310 | ClockSrc::MSI(range) => { |
| 299 | } | 311 | RCC.icscr1().modify(|w| { |
| 300 | 312 | let bits: Msirange = range.into(); | |
| 301 | impl RccExt for RCC { | 313 | w.set_msisrange(bits); |
| 302 | #[inline] | 314 | w.set_msirgsel(Msirgsel::RCC_ICSCR1); |
| 303 | fn freeze(self, cfgr: Config, power: &Power) -> Clocks { | 315 | }); |
| 304 | let rcc = pac::RCC; | 316 | RCC.cr().write(|w| { |
| 305 | 317 | w.set_msipllen(false); | |
| 306 | let sys_clk = match cfgr.mux { | 318 | w.set_msison(true); |
| 307 | ClockSrc::MSI(range) => { | 319 | w.set_msison(true); |
| 308 | unsafe { | 320 | }); |
| 309 | rcc.icscr1().modify(|w| { | 321 | while !RCC.cr().read().msisrdy() {} |
| 310 | let bits: Msirange = range.into(); | ||
| 311 | w.set_msisrange(bits); | ||
| 312 | w.set_msirgsel(Msirgsel::RCC_ICSCR1); | ||
| 313 | }); | ||
| 314 | rcc.cr().write(|w| { | ||
| 315 | w.set_msipllen(false); | ||
| 316 | w.set_msison(true); | ||
| 317 | w.set_msison(true); | ||
| 318 | }); | ||
| 319 | while !rcc.cr().read().msisrdy() {} | ||
| 320 | } | ||
| 321 | |||
| 322 | range.into() | ||
| 323 | } | ||
| 324 | ClockSrc::HSE(freq) => { | ||
| 325 | unsafe { | ||
| 326 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 327 | while !rcc.cr().read().hserdy() {} | ||
| 328 | } | ||
| 329 | |||
| 330 | freq.0 | ||
| 331 | } | ||
| 332 | ClockSrc::HSI16 => { | ||
| 333 | unsafe { | ||
| 334 | rcc.cr().write(|w| w.set_hsion(true)); | ||
| 335 | while !rcc.cr().read().hsirdy() {} | ||
| 336 | } | ||
| 337 | 322 | ||
| 338 | HSI16_FREQ | 323 | range.into() |
| 339 | } | 324 | } |
| 340 | ClockSrc::PLL1R(src, m, n, div) => { | 325 | ClockSrc::HSE(freq) => { |
| 341 | let freq = match src { | 326 | RCC.cr().write(|w| w.set_hseon(true)); |
| 342 | PllSrc::MSI(_) => MSIRange::default().into(), | 327 | while !RCC.cr().read().hserdy() {} |
| 343 | PllSrc::HSE(hertz) => hertz.0, | ||
| 344 | PllSrc::HSI16 => HSI16_FREQ, | ||
| 345 | }; | ||
| 346 | |||
| 347 | // disable | ||
| 348 | unsafe { | ||
| 349 | rcc.cr().modify(|w| w.set_pllon(0, false)); | ||
| 350 | while rcc.cr().read().pllrdy(0) {} | ||
| 351 | } | ||
| 352 | |||
| 353 | let vco = freq * n as u8 as u32; | ||
| 354 | let pll_ck = vco / (div as u8 as u32 + 1); | ||
| 355 | |||
| 356 | unsafe { | ||
| 357 | rcc.pll1cfgr().write(|w| { | ||
| 358 | w.set_pllm(m.into()); | ||
| 359 | w.set_pllsrc(src.into()); | ||
| 360 | }); | ||
| 361 | |||
| 362 | rcc.pll1divr().modify(|w| { | ||
| 363 | w.set_pllr(div.to_div()); | ||
| 364 | w.set_plln(n.to_mul()); | ||
| 365 | }); | ||
| 366 | |||
| 367 | // Enable PLL | ||
| 368 | rcc.cr().modify(|w| w.set_pllon(0, true)); | ||
| 369 | while !rcc.cr().read().pllrdy(0) {} | ||
| 370 | rcc.pll1cfgr().modify(|w| w.set_pllren(true)); | ||
| 371 | } | ||
| 372 | |||
| 373 | unsafe { | ||
| 374 | rcc.cr().write(|w| w.set_pllon(0, true)); | ||
| 375 | while !rcc.cr().read().pllrdy(0) {} | ||
| 376 | } | ||
| 377 | |||
| 378 | pll_ck | ||
| 379 | } | ||
| 380 | }; | ||
| 381 | |||
| 382 | // states and programming delay | ||
| 383 | let wait_states = match power.vos { | ||
| 384 | // VOS 0 range VCORE 1.26V - 1.40V | ||
| 385 | VoltageScale::Range1 => { | ||
| 386 | if sys_clk < 32_000_000 { | ||
| 387 | 0 | ||
| 388 | } else if sys_clk < 64_000_000 { | ||
| 389 | 1 | ||
| 390 | } else if sys_clk < 96_000_000 { | ||
| 391 | 2 | ||
| 392 | } else if sys_clk < 128_000_000 { | ||
| 393 | 3 | ||
| 394 | } else { | ||
| 395 | 4 | ||
| 396 | } | ||
| 397 | } | ||
| 398 | // VOS 1 range VCORE 1.15V - 1.26V | ||
| 399 | VoltageScale::Range2 => { | ||
| 400 | if sys_clk < 30_000_000 { | ||
| 401 | 0 | ||
| 402 | } else if sys_clk < 60_000_000 { | ||
| 403 | 1 | ||
| 404 | } else if sys_clk < 90_000_000 { | ||
| 405 | 2 | ||
| 406 | } else { | ||
| 407 | 3 | ||
| 408 | } | ||
| 409 | } | ||
| 410 | // VOS 2 range VCORE 1.05V - 1.15V | ||
| 411 | VoltageScale::Range3 => { | ||
| 412 | if sys_clk < 24_000_000 { | ||
| 413 | 0 | ||
| 414 | } else if sys_clk < 48_000_000 { | ||
| 415 | 1 | ||
| 416 | } else { | ||
| 417 | 2 | ||
| 418 | } | ||
| 419 | } | ||
| 420 | // VOS 3 range VCORE 0.95V - 1.05V | ||
| 421 | VoltageScale::Range4 => { | ||
| 422 | if sys_clk < 12_000_000 { | ||
| 423 | 0 | ||
| 424 | } else { | ||
| 425 | 1 | ||
| 426 | } | ||
| 427 | } | ||
| 428 | }; | ||
| 429 | 328 | ||
| 430 | unsafe { | 329 | freq.0 |
| 431 | pac::FLASH.acr().modify(|w| { | ||
| 432 | w.set_latency(wait_states); | ||
| 433 | }) | ||
| 434 | } | 330 | } |
| 331 | ClockSrc::HSI16 => { | ||
| 332 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 333 | while !RCC.cr().read().hsirdy() {} | ||
| 435 | 334 | ||
| 436 | unsafe { | 335 | HSI16_FREQ |
| 437 | rcc.cfgr1().modify(|w| { | 336 | } |
| 438 | w.set_sw(cfgr.mux.into()); | 337 | ClockSrc::PLL1R(src, m, n, div) => { |
| 338 | let freq = match src { | ||
| 339 | PllSrc::MSI(_) => MSIRange::default().into(), | ||
| 340 | PllSrc::HSE(hertz) => hertz.0, | ||
| 341 | PllSrc::HSI16 => HSI16_FREQ, | ||
| 342 | }; | ||
| 343 | |||
| 344 | // disable | ||
| 345 | RCC.cr().modify(|w| w.set_pllon(0, false)); | ||
| 346 | while RCC.cr().read().pllrdy(0) {} | ||
| 347 | |||
| 348 | let vco = freq * n as u8 as u32; | ||
| 349 | let pll_ck = vco / (div as u8 as u32 + 1); | ||
| 350 | |||
| 351 | RCC.pll1cfgr().write(|w| { | ||
| 352 | w.set_pllm(m.into()); | ||
| 353 | w.set_pllsrc(src.into()); | ||
| 439 | }); | 354 | }); |
| 440 | 355 | ||
| 441 | rcc.cfgr2().modify(|w| { | 356 | RCC.pll1divr().modify(|w| { |
| 442 | w.set_hpre(cfgr.ahb_pre.into()); | 357 | w.set_pllr(div.to_div()); |
| 443 | w.set_ppre1(cfgr.apb1_pre.into()); | 358 | w.set_plln(n.to_mul()); |
| 444 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 445 | }); | 359 | }); |
| 446 | 360 | ||
| 447 | rcc.cfgr3().modify(|w| { | 361 | // Enable PLL |
| 448 | w.set_ppre3(cfgr.apb3_pre.into()); | 362 | RCC.cr().modify(|w| w.set_pllon(0, true)); |
| 449 | }); | 363 | while !RCC.cr().read().pllrdy(0) {} |
| 450 | } | 364 | RCC.pll1cfgr().modify(|w| w.set_pllren(true)); |
| 365 | |||
| 366 | RCC.cr().write(|w| w.set_pllon(0, true)); | ||
| 367 | while !RCC.cr().read().pllrdy(0) {} | ||
| 451 | 368 | ||
| 452 | let ahb_freq: u32 = match cfgr.ahb_pre { | 369 | pll_ck |
| 453 | AHBPrescaler::NotDivided => sys_clk, | 370 | } |
| 454 | pre => { | 371 | }; |
| 455 | let pre: u8 = pre.into(); | 372 | |
| 456 | let pre = 1 << (pre as u32 - 7); | 373 | // TODO make configurable |
| 457 | sys_clk / pre | 374 | let power_vos = VoltageScale::Range4; |
| 375 | |||
| 376 | // states and programming delay | ||
| 377 | let wait_states = match power_vos { | ||
| 378 | // VOS 0 range VCORE 1.26V - 1.40V | ||
| 379 | VoltageScale::Range1 => { | ||
| 380 | if sys_clk < 32_000_000 { | ||
| 381 | 0 | ||
| 382 | } else if sys_clk < 64_000_000 { | ||
| 383 | 1 | ||
| 384 | } else if sys_clk < 96_000_000 { | ||
| 385 | 2 | ||
| 386 | } else if sys_clk < 128_000_000 { | ||
| 387 | 3 | ||
| 388 | } else { | ||
| 389 | 4 | ||
| 458 | } | 390 | } |
| 459 | }; | 391 | } |
| 460 | 392 | // VOS 1 range VCORE 1.15V - 1.26V | |
| 461 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | 393 | VoltageScale::Range2 => { |
| 462 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 394 | if sys_clk < 30_000_000 { |
| 463 | pre => { | 395 | 0 |
| 464 | let pre: u8 = pre.into(); | 396 | } else if sys_clk < 60_000_000 { |
| 465 | let pre: u8 = 1 << (pre - 3); | 397 | 1 |
| 466 | let freq = ahb_freq / pre as u32; | 398 | } else if sys_clk < 90_000_000 { |
| 467 | (freq, freq * 2) | 399 | 2 |
| 400 | } else { | ||
| 401 | 3 | ||
| 468 | } | 402 | } |
| 469 | }; | 403 | } |
| 470 | 404 | // VOS 2 range VCORE 1.05V - 1.15V | |
| 471 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | 405 | VoltageScale::Range3 => { |
| 472 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 406 | if sys_clk < 24_000_000 { |
| 473 | pre => { | 407 | 0 |
| 474 | let pre: u8 = pre.into(); | 408 | } else if sys_clk < 48_000_000 { |
| 475 | let pre: u8 = 1 << (pre - 3); | 409 | 1 |
| 476 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | 410 | } else { |
| 477 | (freq, freq * 2) | 411 | 2 |
| 478 | } | 412 | } |
| 479 | }; | 413 | } |
| 480 | 414 | // VOS 3 range VCORE 0.95V - 1.05V | |
| 481 | let (apb3_freq, _apb3_tim_freq) = match cfgr.apb3_pre { | 415 | VoltageScale::Range4 => { |
| 482 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 416 | if sys_clk < 12_000_000 { |
| 483 | pre => { | 417 | 0 |
| 484 | let pre: u8 = pre.into(); | 418 | } else { |
| 485 | let pre: u8 = 1 << (pre - 3); | 419 | 1 |
| 486 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 487 | (freq, freq * 2) | ||
| 488 | } | 420 | } |
| 489 | }; | ||
| 490 | |||
| 491 | Clocks { | ||
| 492 | sys: sys_clk.hz(), | ||
| 493 | ahb1: ahb_freq.hz(), | ||
| 494 | ahb2: ahb_freq.hz(), | ||
| 495 | ahb3: ahb_freq.hz(), | ||
| 496 | apb1: apb1_freq.hz(), | ||
| 497 | apb2: apb2_freq.hz(), | ||
| 498 | apb3: apb3_freq.hz(), | ||
| 499 | apb1_tim: apb1_tim_freq.hz(), | ||
| 500 | apb2_tim: apb2_tim_freq.hz(), | ||
| 501 | } | 421 | } |
| 502 | } | 422 | }; |
| 503 | } | 423 | |
| 504 | 424 | FLASH.acr().modify(|w| { | |
| 505 | pub unsafe fn init(config: Config) { | 425 | w.set_latency(wait_states); |
| 506 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | 426 | }); |
| 507 | let power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal()); | 427 | |
| 508 | let clocks = r.freeze(config, &power); | 428 | RCC.cfgr1().modify(|w| { |
| 509 | set_freqs(clocks); | 429 | w.set_sw(config.mux.into()); |
| 430 | }); | ||
| 431 | |||
| 432 | RCC.cfgr2().modify(|w| { | ||
| 433 | w.set_hpre(config.ahb_pre.into()); | ||
| 434 | w.set_ppre1(config.apb1_pre.into()); | ||
| 435 | w.set_ppre2(config.apb2_pre.into()); | ||
| 436 | }); | ||
| 437 | |||
| 438 | RCC.cfgr3().modify(|w| { | ||
| 439 | w.set_ppre3(config.apb3_pre.into()); | ||
| 440 | }); | ||
| 441 | |||
| 442 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 443 | AHBPrescaler::NotDivided => sys_clk, | ||
| 444 | pre => { | ||
| 445 | let pre: u8 = pre.into(); | ||
| 446 | let pre = 1 << (pre as u32 - 7); | ||
| 447 | sys_clk / pre | ||
| 448 | } | ||
| 449 | }; | ||
| 450 | |||
| 451 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 452 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 453 | pre => { | ||
| 454 | let pre: u8 = pre.into(); | ||
| 455 | let pre: u8 = 1 << (pre - 3); | ||
| 456 | let freq = ahb_freq / pre as u32; | ||
| 457 | (freq, freq * 2) | ||
| 458 | } | ||
| 459 | }; | ||
| 460 | |||
| 461 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 462 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 463 | pre => { | ||
| 464 | let pre: u8 = pre.into(); | ||
| 465 | let pre: u8 = 1 << (pre - 3); | ||
| 466 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 467 | (freq, freq * 2) | ||
| 468 | } | ||
| 469 | }; | ||
| 470 | |||
| 471 | let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre { | ||
| 472 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 473 | pre => { | ||
| 474 | let pre: u8 = pre.into(); | ||
| 475 | let pre: u8 = 1 << (pre - 3); | ||
| 476 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 477 | (freq, freq * 2) | ||
| 478 | } | ||
| 479 | }; | ||
| 480 | |||
| 481 | set_freqs(Clocks { | ||
| 482 | sys: sys_clk.hz(), | ||
| 483 | ahb1: ahb_freq.hz(), | ||
| 484 | ahb2: ahb_freq.hz(), | ||
| 485 | ahb3: ahb_freq.hz(), | ||
| 486 | apb1: apb1_freq.hz(), | ||
| 487 | apb2: apb2_freq.hz(), | ||
| 488 | apb3: apb3_freq.hz(), | ||
| 489 | apb1_tim: apb1_tim_freq.hz(), | ||
| 490 | apb2_tim: apb2_tim_freq.hz(), | ||
| 491 | }); | ||
| 510 | } | 492 | } |
diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs new file mode 100644 index 000000000..eae7c9464 --- /dev/null +++ b/embassy-stm32/src/rcc/wb.rs | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | use crate::pac::RCC; | ||
| 2 | use crate::rcc::{set_freqs, Clocks}; | ||
| 3 | use crate::time::Hertz; | ||
| 4 | use crate::time::U32Ext; | ||
| 5 | |||
| 6 | /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||
| 7 | /// and with the addition of the init function to configure a system clock. | ||
| 8 | |||
| 9 | /// Only the basic setup using the HSE and HSI clocks are supported as of now. | ||
| 10 | |||
| 11 | /// HSI speed | ||
| 12 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 13 | |||
| 14 | /// System clock mux source | ||
| 15 | #[derive(Clone, Copy)] | ||
| 16 | pub enum ClockSrc { | ||
| 17 | HSE(Hertz), | ||
| 18 | HSI16, | ||
| 19 | } | ||
| 20 | |||
| 21 | /// AHB prescaler | ||
| 22 | #[derive(Clone, Copy, PartialEq)] | ||
| 23 | pub enum AHBPrescaler { | ||
| 24 | NotDivided, | ||
| 25 | Div2, | ||
| 26 | Div3, | ||
| 27 | Div4, | ||
| 28 | Div5, | ||
| 29 | Div6, | ||
| 30 | Div8, | ||
| 31 | Div10, | ||
| 32 | Div16, | ||
| 33 | Div32, | ||
| 34 | Div64, | ||
| 35 | Div128, | ||
| 36 | Div256, | ||
| 37 | Div512, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// APB prescaler | ||
| 41 | #[derive(Clone, Copy)] | ||
| 42 | pub enum APBPrescaler { | ||
| 43 | NotDivided, | ||
| 44 | Div2, | ||
| 45 | Div4, | ||
| 46 | Div8, | ||
| 47 | Div16, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl Into<u8> for APBPrescaler { | ||
| 51 | fn into(self) -> u8 { | ||
| 52 | match self { | ||
| 53 | APBPrescaler::NotDivided => 1, | ||
| 54 | APBPrescaler::Div2 => 0x04, | ||
| 55 | APBPrescaler::Div4 => 0x05, | ||
| 56 | APBPrescaler::Div8 => 0x06, | ||
| 57 | APBPrescaler::Div16 => 0x07, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl Into<u8> for AHBPrescaler { | ||
| 63 | fn into(self) -> u8 { | ||
| 64 | match self { | ||
| 65 | AHBPrescaler::NotDivided => 1, | ||
| 66 | AHBPrescaler::Div2 => 0x08, | ||
| 67 | AHBPrescaler::Div3 => 0x01, | ||
| 68 | AHBPrescaler::Div4 => 0x09, | ||
| 69 | AHBPrescaler::Div5 => 0x02, | ||
| 70 | AHBPrescaler::Div6 => 0x05, | ||
| 71 | AHBPrescaler::Div8 => 0x0a, | ||
| 72 | AHBPrescaler::Div10 => 0x06, | ||
| 73 | AHBPrescaler::Div16 => 0x0b, | ||
| 74 | AHBPrescaler::Div32 => 0x07, | ||
| 75 | AHBPrescaler::Div64 => 0x0c, | ||
| 76 | AHBPrescaler::Div128 => 0x0d, | ||
| 77 | AHBPrescaler::Div256 => 0x0e, | ||
| 78 | AHBPrescaler::Div512 => 0x0f, | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | /// Clocks configutation | ||
| 84 | pub struct Config { | ||
| 85 | pub mux: ClockSrc, | ||
| 86 | pub ahb_pre: AHBPrescaler, | ||
| 87 | pub apb1_pre: APBPrescaler, | ||
| 88 | pub apb2_pre: APBPrescaler, | ||
| 89 | } | ||
| 90 | |||
| 91 | impl Default for Config { | ||
| 92 | #[inline] | ||
| 93 | fn default() -> Config { | ||
| 94 | Config { | ||
| 95 | mux: ClockSrc::HSI16, | ||
| 96 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 97 | apb1_pre: APBPrescaler::NotDivided, | ||
| 98 | apb2_pre: APBPrescaler::NotDivided, | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | pub(crate) unsafe fn init(config: Config) { | ||
| 104 | let (sys_clk, sw) = match config.mux { | ||
| 105 | ClockSrc::HSI16 => { | ||
| 106 | // Enable HSI16 | ||
| 107 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 108 | while !RCC.cr().read().hsirdy() {} | ||
| 109 | |||
| 110 | (HSI_FREQ, 0x01) | ||
| 111 | } | ||
| 112 | ClockSrc::HSE(freq) => { | ||
| 113 | // Enable HSE | ||
| 114 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 115 | while !RCC.cr().read().hserdy() {} | ||
| 116 | |||
| 117 | (freq.0, 0x02) | ||
| 118 | } | ||
| 119 | }; | ||
| 120 | |||
| 121 | RCC.cfgr().modify(|w| { | ||
| 122 | w.set_sw(sw.into()); | ||
| 123 | w.set_hpre(config.ahb_pre.into()); | ||
| 124 | w.set_ppre1(config.apb1_pre.into()); | ||
| 125 | w.set_ppre2(config.apb2_pre.into()); | ||
| 126 | }); | ||
| 127 | |||
| 128 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 129 | AHBPrescaler::NotDivided => sys_clk, | ||
| 130 | pre => { | ||
| 131 | let pre: u8 = pre.into(); | ||
| 132 | let pre = 1 << (pre as u32 - 7); | ||
| 133 | sys_clk / pre | ||
| 134 | } | ||
| 135 | }; | ||
| 136 | |||
| 137 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 138 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 139 | pre => { | ||
| 140 | let pre: u8 = pre.into(); | ||
| 141 | let pre: u8 = 1 << (pre - 3); | ||
| 142 | let freq = ahb_freq / pre as u32; | ||
| 143 | (freq, freq * 2) | ||
| 144 | } | ||
| 145 | }; | ||
| 146 | |||
| 147 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 148 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 149 | pre => { | ||
| 150 | let pre: u8 = pre.into(); | ||
| 151 | let pre: u8 = 1 << (pre - 3); | ||
| 152 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 153 | (freq, freq * 2) | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | set_freqs(Clocks { | ||
| 158 | sys: sys_clk.hz(), | ||
| 159 | ahb1: ahb_freq.hz(), | ||
| 160 | ahb2: ahb_freq.hz(), | ||
| 161 | ahb3: ahb_freq.hz(), | ||
| 162 | apb1: apb1_freq.hz(), | ||
| 163 | apb2: apb2_freq.hz(), | ||
| 164 | apb1_tim: apb1_tim_freq.hz(), | ||
| 165 | apb2_tim: apb2_tim_freq.hz(), | ||
| 166 | }); | ||
| 167 | } | ||
diff --git a/embassy-stm32/src/rcc/wb/mod.rs b/embassy-stm32/src/rcc/wb/mod.rs deleted file mode 100644 index 73835cacd..000000000 --- a/embassy-stm32/src/rcc/wb/mod.rs +++ /dev/null | |||
| @@ -1,213 +0,0 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::{self, RCC}; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::Hertz; | ||
| 5 | use crate::time::U32Ext; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | use embassy::util::Unborrow; | ||
| 8 | use embassy_hal_common::unborrow; | ||
| 9 | |||
| 10 | /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||
| 11 | /// and with the addition of the init function to configure a system clock. | ||
| 12 | |||
| 13 | /// Only the basic setup using the HSE and HSI clocks are supported as of now. | ||
| 14 | |||
| 15 | /// HSI speed | ||
| 16 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 17 | |||
| 18 | /// System clock mux source | ||
| 19 | #[derive(Clone, Copy)] | ||
| 20 | pub enum ClockSrc { | ||
| 21 | HSE(Hertz), | ||
| 22 | HSI16, | ||
| 23 | } | ||
| 24 | |||
| 25 | /// AHB prescaler | ||
| 26 | #[derive(Clone, Copy, PartialEq)] | ||
| 27 | pub enum AHBPrescaler { | ||
| 28 | NotDivided, | ||
| 29 | Div2, | ||
| 30 | Div3, | ||
| 31 | Div4, | ||
| 32 | Div5, | ||
| 33 | Div6, | ||
| 34 | Div8, | ||
| 35 | Div10, | ||
| 36 | Div16, | ||
| 37 | Div32, | ||
| 38 | Div64, | ||
| 39 | Div128, | ||
| 40 | Div256, | ||
| 41 | Div512, | ||
| 42 | } | ||
| 43 | |||
| 44 | /// APB prescaler | ||
| 45 | #[derive(Clone, Copy)] | ||
| 46 | pub enum APBPrescaler { | ||
| 47 | NotDivided, | ||
| 48 | Div2, | ||
| 49 | Div4, | ||
| 50 | Div8, | ||
| 51 | Div16, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl Into<u8> for APBPrescaler { | ||
| 55 | fn into(self) -> u8 { | ||
| 56 | match self { | ||
| 57 | APBPrescaler::NotDivided => 1, | ||
| 58 | APBPrescaler::Div2 => 0x04, | ||
| 59 | APBPrescaler::Div4 => 0x05, | ||
| 60 | APBPrescaler::Div8 => 0x06, | ||
| 61 | APBPrescaler::Div16 => 0x07, | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | impl Into<u8> for AHBPrescaler { | ||
| 67 | fn into(self) -> u8 { | ||
| 68 | match self { | ||
| 69 | AHBPrescaler::NotDivided => 1, | ||
| 70 | AHBPrescaler::Div2 => 0x08, | ||
| 71 | AHBPrescaler::Div3 => 0x01, | ||
| 72 | AHBPrescaler::Div4 => 0x09, | ||
| 73 | AHBPrescaler::Div5 => 0x02, | ||
| 74 | AHBPrescaler::Div6 => 0x05, | ||
| 75 | AHBPrescaler::Div8 => 0x0a, | ||
| 76 | AHBPrescaler::Div10 => 0x06, | ||
| 77 | AHBPrescaler::Div16 => 0x0b, | ||
| 78 | AHBPrescaler::Div32 => 0x07, | ||
| 79 | AHBPrescaler::Div64 => 0x0c, | ||
| 80 | AHBPrescaler::Div128 => 0x0d, | ||
| 81 | AHBPrescaler::Div256 => 0x0e, | ||
| 82 | AHBPrescaler::Div512 => 0x0f, | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Clocks configutation | ||
| 88 | pub struct Config { | ||
| 89 | pub mux: ClockSrc, | ||
| 90 | pub ahb_pre: AHBPrescaler, | ||
| 91 | pub apb1_pre: APBPrescaler, | ||
| 92 | pub apb2_pre: APBPrescaler, | ||
| 93 | } | ||
| 94 | |||
| 95 | impl Default for Config { | ||
| 96 | #[inline] | ||
| 97 | fn default() -> Config { | ||
| 98 | Config { | ||
| 99 | mux: ClockSrc::HSI16, | ||
| 100 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 101 | apb1_pre: APBPrescaler::NotDivided, | ||
| 102 | apb2_pre: APBPrescaler::NotDivided, | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | /// RCC peripheral | ||
| 108 | pub struct Rcc<'d> { | ||
| 109 | _rb: peripherals::RCC, | ||
| 110 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 111 | } | ||
| 112 | |||
| 113 | impl<'d> Rcc<'d> { | ||
| 114 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | ||
| 115 | unborrow!(rcc); | ||
| 116 | Self { | ||
| 117 | _rb: rcc, | ||
| 118 | phantom: PhantomData, | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | // Safety: RCC init must have been called | ||
| 123 | pub fn clocks(&self) -> &'static Clocks { | ||
| 124 | unsafe { get_freqs() } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | ||
| 129 | pub trait RccExt { | ||
| 130 | fn freeze(self, config: Config) -> Clocks; | ||
| 131 | } | ||
| 132 | |||
| 133 | impl RccExt for RCC { | ||
| 134 | #[inline] | ||
| 135 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 136 | let rcc = pac::RCC; | ||
| 137 | let (sys_clk, sw) = match cfgr.mux { | ||
| 138 | ClockSrc::HSI16 => { | ||
| 139 | // Enable HSI16 | ||
| 140 | unsafe { | ||
| 141 | rcc.cr().write(|w| w.set_hsion(true)); | ||
| 142 | while !rcc.cr().read().hsirdy() {} | ||
| 143 | } | ||
| 144 | |||
| 145 | (HSI_FREQ, 0x01) | ||
| 146 | } | ||
| 147 | ClockSrc::HSE(freq) => { | ||
| 148 | // Enable HSE | ||
| 149 | unsafe { | ||
| 150 | rcc.cr().write(|w| w.set_hseon(true)); | ||
| 151 | while !rcc.cr().read().hserdy() {} | ||
| 152 | } | ||
| 153 | |||
| 154 | (freq.0, 0x02) | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | |||
| 158 | unsafe { | ||
| 159 | rcc.cfgr().modify(|w| { | ||
| 160 | w.set_sw(sw.into()); | ||
| 161 | w.set_hpre(cfgr.ahb_pre.into()); | ||
| 162 | w.set_ppre1(cfgr.apb1_pre.into()); | ||
| 163 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 164 | }); | ||
| 165 | } | ||
| 166 | |||
| 167 | let ahb_freq: u32 = match cfgr.ahb_pre { | ||
| 168 | AHBPrescaler::NotDivided => sys_clk, | ||
| 169 | pre => { | ||
| 170 | let pre: u8 = pre.into(); | ||
| 171 | let pre = 1 << (pre as u32 - 7); | ||
| 172 | sys_clk / pre | ||
| 173 | } | ||
| 174 | }; | ||
| 175 | |||
| 176 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | ||
| 177 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 178 | pre => { | ||
| 179 | let pre: u8 = pre.into(); | ||
| 180 | let pre: u8 = 1 << (pre - 3); | ||
| 181 | let freq = ahb_freq / pre as u32; | ||
| 182 | (freq, freq * 2) | ||
| 183 | } | ||
| 184 | }; | ||
| 185 | |||
| 186 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | ||
| 187 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 188 | pre => { | ||
| 189 | let pre: u8 = pre.into(); | ||
| 190 | let pre: u8 = 1 << (pre - 3); | ||
| 191 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 192 | (freq, freq * 2) | ||
| 193 | } | ||
| 194 | }; | ||
| 195 | |||
| 196 | Clocks { | ||
| 197 | sys: sys_clk.hz(), | ||
| 198 | ahb1: ahb_freq.hz(), | ||
| 199 | ahb2: ahb_freq.hz(), | ||
| 200 | ahb3: ahb_freq.hz(), | ||
| 201 | apb1: apb1_freq.hz(), | ||
| 202 | apb2: apb2_freq.hz(), | ||
| 203 | apb1_tim: apb1_tim_freq.hz(), | ||
| 204 | apb2_tim: apb2_tim_freq.hz(), | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | pub unsafe fn init(config: Config) { | ||
| 210 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 211 | let clocks = r.freeze(config); | ||
| 212 | set_freqs(clocks); | ||
| 213 | } | ||
diff --git a/embassy-stm32/src/rcc/wl5.rs b/embassy-stm32/src/rcc/wl5.rs new file mode 100644 index 000000000..fb2dd9986 --- /dev/null +++ b/embassy-stm32/src/rcc/wl5.rs | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | use crate::pac::RCC; | ||
| 2 | use crate::rcc::{set_freqs, Clocks}; | ||
| 3 | use crate::time::U32Ext; | ||
| 4 | |||
| 5 | /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||
| 6 | /// and with the addition of the init function to configure a system clock. | ||
| 7 | |||
| 8 | /// Only the basic setup using the HSE and HSI clocks are supported as of now. | ||
| 9 | |||
| 10 | /// HSI speed | ||
| 11 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 12 | |||
| 13 | pub const HSE32_FREQ: u32 = 32_000_000; | ||
| 14 | |||
| 15 | /// System clock mux source | ||
| 16 | #[derive(Clone, Copy)] | ||
| 17 | pub enum ClockSrc { | ||
| 18 | HSE32, | ||
| 19 | HSI16, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// AHB prescaler | ||
| 23 | #[derive(Clone, Copy, PartialEq)] | ||
| 24 | pub enum AHBPrescaler { | ||
| 25 | NotDivided, | ||
| 26 | Div2, | ||
| 27 | Div3, | ||
| 28 | Div4, | ||
| 29 | Div5, | ||
| 30 | Div6, | ||
| 31 | Div8, | ||
| 32 | Div10, | ||
| 33 | Div16, | ||
| 34 | Div32, | ||
| 35 | Div64, | ||
| 36 | Div128, | ||
| 37 | Div256, | ||
| 38 | Div512, | ||
| 39 | } | ||
| 40 | |||
| 41 | /// APB prescaler | ||
| 42 | #[derive(Clone, Copy)] | ||
| 43 | pub enum APBPrescaler { | ||
| 44 | NotDivided, | ||
| 45 | Div2, | ||
| 46 | Div4, | ||
| 47 | Div8, | ||
| 48 | Div16, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl Into<u8> for APBPrescaler { | ||
| 52 | fn into(self) -> u8 { | ||
| 53 | match self { | ||
| 54 | APBPrescaler::NotDivided => 1, | ||
| 55 | APBPrescaler::Div2 => 0x04, | ||
| 56 | APBPrescaler::Div4 => 0x05, | ||
| 57 | APBPrescaler::Div8 => 0x06, | ||
| 58 | APBPrescaler::Div16 => 0x07, | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | impl Into<u8> for AHBPrescaler { | ||
| 64 | fn into(self) -> u8 { | ||
| 65 | match self { | ||
| 66 | AHBPrescaler::NotDivided => 1, | ||
| 67 | AHBPrescaler::Div2 => 0x08, | ||
| 68 | AHBPrescaler::Div3 => 0x01, | ||
| 69 | AHBPrescaler::Div4 => 0x09, | ||
| 70 | AHBPrescaler::Div5 => 0x02, | ||
| 71 | AHBPrescaler::Div6 => 0x05, | ||
| 72 | AHBPrescaler::Div8 => 0x0a, | ||
| 73 | AHBPrescaler::Div10 => 0x06, | ||
| 74 | AHBPrescaler::Div16 => 0x0b, | ||
| 75 | AHBPrescaler::Div32 => 0x07, | ||
| 76 | AHBPrescaler::Div64 => 0x0c, | ||
| 77 | AHBPrescaler::Div128 => 0x0d, | ||
| 78 | AHBPrescaler::Div256 => 0x0e, | ||
| 79 | AHBPrescaler::Div512 => 0x0f, | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Clocks configutation | ||
| 85 | pub struct Config { | ||
| 86 | pub mux: ClockSrc, | ||
| 87 | pub ahb_pre: AHBPrescaler, | ||
| 88 | pub apb1_pre: APBPrescaler, | ||
| 89 | pub apb2_pre: APBPrescaler, | ||
| 90 | pub enable_lsi: bool, | ||
| 91 | } | ||
| 92 | |||
| 93 | impl Default for Config { | ||
| 94 | #[inline] | ||
| 95 | fn default() -> Config { | ||
| 96 | Config { | ||
| 97 | mux: ClockSrc::HSI16, | ||
| 98 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 99 | apb1_pre: APBPrescaler::NotDivided, | ||
| 100 | apb2_pre: APBPrescaler::NotDivided, | ||
| 101 | enable_lsi: false, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | pub(crate) unsafe fn init(config: Config) { | ||
| 107 | let (sys_clk, sw) = match config.mux { | ||
| 108 | ClockSrc::HSI16 => { | ||
| 109 | // Enable HSI16 | ||
| 110 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 111 | while !RCC.cr().read().hsirdy() {} | ||
| 112 | |||
| 113 | (HSI_FREQ, 0x01) | ||
| 114 | } | ||
| 115 | ClockSrc::HSE32 => { | ||
| 116 | // Enable HSE32 | ||
| 117 | RCC.cr().write(|w| { | ||
| 118 | w.set_hsebyppwr(true); | ||
| 119 | w.set_hseon(true); | ||
| 120 | }); | ||
| 121 | while !RCC.cr().read().hserdy() {} | ||
| 122 | |||
| 123 | (HSE32_FREQ, 0x02) | ||
| 124 | } | ||
| 125 | }; | ||
| 126 | |||
| 127 | RCC.cfgr().modify(|w| { | ||
| 128 | w.set_sw(sw.into()); | ||
| 129 | if config.ahb_pre == AHBPrescaler::NotDivided { | ||
| 130 | w.set_hpre(0); | ||
| 131 | } else { | ||
| 132 | w.set_hpre(config.ahb_pre.into()); | ||
| 133 | } | ||
| 134 | w.set_ppre1(config.apb1_pre.into()); | ||
| 135 | w.set_ppre2(config.apb2_pre.into()); | ||
| 136 | }); | ||
| 137 | |||
| 138 | let ahb_freq: u32 = match config.ahb_pre { | ||
| 139 | AHBPrescaler::NotDivided => sys_clk, | ||
| 140 | pre => { | ||
| 141 | let pre: u8 = pre.into(); | ||
| 142 | let pre = 1 << (pre as u32 - 7); | ||
| 143 | sys_clk / pre | ||
| 144 | } | ||
| 145 | }; | ||
| 146 | |||
| 147 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 148 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 149 | pre => { | ||
| 150 | let pre: u8 = pre.into(); | ||
| 151 | let pre: u8 = 1 << (pre - 3); | ||
| 152 | let freq = ahb_freq / pre as u32; | ||
| 153 | (freq, freq * 2) | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 158 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 159 | pre => { | ||
| 160 | let pre: u8 = pre.into(); | ||
| 161 | let pre: u8 = 1 << (pre - 3); | ||
| 162 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 163 | (freq, freq * 2) | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | |||
| 167 | // TODO: completely untested | ||
| 168 | let apb3_freq = ahb_freq; | ||
| 169 | |||
| 170 | if config.enable_lsi { | ||
| 171 | let csr = RCC.csr().read(); | ||
| 172 | if !csr.lsion() { | ||
| 173 | RCC.csr().modify(|w| w.set_lsion(true)); | ||
| 174 | while !RCC.csr().read().lsirdy() {} | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | set_freqs(Clocks { | ||
| 179 | sys: sys_clk.hz(), | ||
| 180 | ahb1: ahb_freq.hz(), | ||
| 181 | ahb2: ahb_freq.hz(), | ||
| 182 | ahb3: ahb_freq.hz(), | ||
| 183 | apb1: apb1_freq.hz(), | ||
| 184 | apb2: apb2_freq.hz(), | ||
| 185 | apb3: apb3_freq.hz(), | ||
| 186 | apb1_tim: apb1_tim_freq.hz(), | ||
| 187 | apb2_tim: apb2_tim_freq.hz(), | ||
| 188 | }); | ||
| 189 | } | ||
diff --git a/embassy-stm32/src/rcc/wl5x/mod.rs b/embassy-stm32/src/rcc/wl5x/mod.rs deleted file mode 100644 index edf603ee6..000000000 --- a/embassy-stm32/src/rcc/wl5x/mod.rs +++ /dev/null | |||
| @@ -1,236 +0,0 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::{self, RCC}; | ||
| 3 | use crate::rcc::{get_freqs, set_freqs, Clocks}; | ||
| 4 | use crate::time::U32Ext; | ||
| 5 | use core::marker::PhantomData; | ||
| 6 | use embassy::util::Unborrow; | ||
| 7 | use embassy_hal_common::unborrow; | ||
| 8 | |||
| 9 | /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, | ||
| 10 | /// and with the addition of the init function to configure a system clock. | ||
| 11 | |||
| 12 | /// Only the basic setup using the HSE and HSI clocks are supported as of now. | ||
| 13 | |||
| 14 | /// HSI speed | ||
| 15 | pub const HSI_FREQ: u32 = 16_000_000; | ||
| 16 | |||
| 17 | pub const HSE32_FREQ: u32 = 32_000_000; | ||
| 18 | |||
| 19 | /// System clock mux source | ||
| 20 | #[derive(Clone, Copy)] | ||
| 21 | pub enum ClockSrc { | ||
| 22 | HSE32, | ||
| 23 | HSI16, | ||
| 24 | } | ||
| 25 | |||
| 26 | /// AHB prescaler | ||
| 27 | #[derive(Clone, Copy, PartialEq)] | ||
| 28 | pub enum AHBPrescaler { | ||
| 29 | NotDivided, | ||
| 30 | Div2, | ||
| 31 | Div3, | ||
| 32 | Div4, | ||
| 33 | Div5, | ||
| 34 | Div6, | ||
| 35 | Div8, | ||
| 36 | Div10, | ||
| 37 | Div16, | ||
| 38 | Div32, | ||
| 39 | Div64, | ||
| 40 | Div128, | ||
| 41 | Div256, | ||
| 42 | Div512, | ||
| 43 | } | ||
| 44 | |||
| 45 | /// APB prescaler | ||
| 46 | #[derive(Clone, Copy)] | ||
| 47 | pub enum APBPrescaler { | ||
| 48 | NotDivided, | ||
| 49 | Div2, | ||
| 50 | Div4, | ||
| 51 | Div8, | ||
| 52 | Div16, | ||
| 53 | } | ||
| 54 | |||
| 55 | impl Into<u8> for APBPrescaler { | ||
| 56 | fn into(self) -> u8 { | ||
| 57 | match self { | ||
| 58 | APBPrescaler::NotDivided => 1, | ||
| 59 | APBPrescaler::Div2 => 0x04, | ||
| 60 | APBPrescaler::Div4 => 0x05, | ||
| 61 | APBPrescaler::Div8 => 0x06, | ||
| 62 | APBPrescaler::Div16 => 0x07, | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Into<u8> for AHBPrescaler { | ||
| 68 | fn into(self) -> u8 { | ||
| 69 | match self { | ||
| 70 | AHBPrescaler::NotDivided => 1, | ||
| 71 | AHBPrescaler::Div2 => 0x08, | ||
| 72 | AHBPrescaler::Div3 => 0x01, | ||
| 73 | AHBPrescaler::Div4 => 0x09, | ||
| 74 | AHBPrescaler::Div5 => 0x02, | ||
| 75 | AHBPrescaler::Div6 => 0x05, | ||
| 76 | AHBPrescaler::Div8 => 0x0a, | ||
| 77 | AHBPrescaler::Div10 => 0x06, | ||
| 78 | AHBPrescaler::Div16 => 0x0b, | ||
| 79 | AHBPrescaler::Div32 => 0x07, | ||
| 80 | AHBPrescaler::Div64 => 0x0c, | ||
| 81 | AHBPrescaler::Div128 => 0x0d, | ||
| 82 | AHBPrescaler::Div256 => 0x0e, | ||
| 83 | AHBPrescaler::Div512 => 0x0f, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Clocks configutation | ||
| 89 | pub struct Config { | ||
| 90 | pub mux: ClockSrc, | ||
| 91 | pub ahb_pre: AHBPrescaler, | ||
| 92 | pub apb1_pre: APBPrescaler, | ||
| 93 | pub apb2_pre: APBPrescaler, | ||
| 94 | } | ||
| 95 | |||
| 96 | impl Default for Config { | ||
| 97 | #[inline] | ||
| 98 | fn default() -> Config { | ||
| 99 | Config { | ||
| 100 | mux: ClockSrc::HSI16, | ||
| 101 | ahb_pre: AHBPrescaler::NotDivided, | ||
| 102 | apb1_pre: APBPrescaler::NotDivided, | ||
| 103 | apb2_pre: APBPrescaler::NotDivided, | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | /// RCC peripheral | ||
| 109 | pub struct Rcc<'d> { | ||
| 110 | _rb: peripherals::RCC, | ||
| 111 | phantom: PhantomData<&'d mut peripherals::RCC>, | ||
| 112 | } | ||
| 113 | |||
| 114 | impl<'d> Rcc<'d> { | ||
| 115 | pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { | ||
| 116 | unborrow!(rcc); | ||
| 117 | Self { | ||
| 118 | _rb: rcc, | ||
| 119 | phantom: PhantomData, | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | pub fn enable_lsi(&mut self) { | ||
| 124 | let rcc = pac::RCC; | ||
| 125 | unsafe { | ||
| 126 | let csr = rcc.csr().read(); | ||
| 127 | if !csr.lsion() { | ||
| 128 | rcc.csr().modify(|w| w.set_lsion(true)); | ||
| 129 | while !rcc.csr().read().lsirdy() {} | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | // Safety: RCC init must have been called | ||
| 135 | pub fn clocks(&self) -> &'static Clocks { | ||
| 136 | unsafe { get_freqs() } | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration | ||
| 141 | pub trait RccExt { | ||
| 142 | fn freeze(self, config: Config) -> Clocks; | ||
| 143 | } | ||
| 144 | |||
| 145 | impl RccExt for RCC { | ||
| 146 | #[inline] | ||
| 147 | fn freeze(self, cfgr: Config) -> Clocks { | ||
| 148 | let rcc = pac::RCC; | ||
| 149 | let (sys_clk, sw) = match cfgr.mux { | ||
| 150 | ClockSrc::HSI16 => { | ||
| 151 | // Enable HSI16 | ||
| 152 | unsafe { | ||
| 153 | rcc.cr().write(|w| w.set_hsion(true)); | ||
| 154 | while !rcc.cr().read().hsirdy() {} | ||
| 155 | } | ||
| 156 | |||
| 157 | (HSI_FREQ, 0x01) | ||
| 158 | } | ||
| 159 | ClockSrc::HSE32 => { | ||
| 160 | // Enable HSE32 | ||
| 161 | unsafe { | ||
| 162 | rcc.cr().write(|w| { | ||
| 163 | w.set_hsebyppwr(true); | ||
| 164 | w.set_hseon(true); | ||
| 165 | }); | ||
| 166 | while !rcc.cr().read().hserdy() {} | ||
| 167 | } | ||
| 168 | |||
| 169 | (HSE32_FREQ, 0x02) | ||
| 170 | } | ||
| 171 | }; | ||
| 172 | |||
| 173 | unsafe { | ||
| 174 | rcc.cfgr().modify(|w| { | ||
| 175 | w.set_sw(sw.into()); | ||
| 176 | if cfgr.ahb_pre == AHBPrescaler::NotDivided { | ||
| 177 | w.set_hpre(0); | ||
| 178 | } else { | ||
| 179 | w.set_hpre(cfgr.ahb_pre.into()); | ||
| 180 | } | ||
| 181 | w.set_ppre1(cfgr.apb1_pre.into()); | ||
| 182 | w.set_ppre2(cfgr.apb2_pre.into()); | ||
| 183 | }); | ||
| 184 | } | ||
| 185 | |||
| 186 | let ahb_freq: u32 = match cfgr.ahb_pre { | ||
| 187 | AHBPrescaler::NotDivided => sys_clk, | ||
| 188 | pre => { | ||
| 189 | let pre: u8 = pre.into(); | ||
| 190 | let pre = 1 << (pre as u32 - 7); | ||
| 191 | sys_clk / pre | ||
| 192 | } | ||
| 193 | }; | ||
| 194 | |||
| 195 | let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { | ||
| 196 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 197 | pre => { | ||
| 198 | let pre: u8 = pre.into(); | ||
| 199 | let pre: u8 = 1 << (pre - 3); | ||
| 200 | let freq = ahb_freq / pre as u32; | ||
| 201 | (freq, freq * 2) | ||
| 202 | } | ||
| 203 | }; | ||
| 204 | |||
| 205 | let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { | ||
| 206 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | ||
| 207 | pre => { | ||
| 208 | let pre: u8 = pre.into(); | ||
| 209 | let pre: u8 = 1 << (pre - 3); | ||
| 210 | let freq = ahb_freq / (1 << (pre as u8 - 3)); | ||
| 211 | (freq, freq * 2) | ||
| 212 | } | ||
| 213 | }; | ||
| 214 | |||
| 215 | // TODO: completely untested | ||
| 216 | let apb3_freq = ahb_freq; | ||
| 217 | |||
| 218 | Clocks { | ||
| 219 | sys: sys_clk.hz(), | ||
| 220 | ahb1: ahb_freq.hz(), | ||
| 221 | ahb2: ahb_freq.hz(), | ||
| 222 | ahb3: ahb_freq.hz(), | ||
| 223 | apb1: apb1_freq.hz(), | ||
| 224 | apb2: apb2_freq.hz(), | ||
| 225 | apb3: apb3_freq.hz(), | ||
| 226 | apb1_tim: apb1_tim_freq.hz(), | ||
| 227 | apb2_tim: apb2_tim_freq.hz(), | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | pub unsafe fn init(config: Config) { | ||
| 233 | let r = <peripherals::RCC as embassy::util::Steal>::steal(); | ||
| 234 | let clocks = r.freeze(config); | ||
| 235 | set_freqs(clocks); | ||
| 236 | } | ||
diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index 20d6f5555..88c75ce6d 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs | |||
| @@ -8,16 +8,18 @@ mod example_common; | |||
| 8 | use embassy::executor::Spawner; | 8 | use embassy::executor::Spawner; |
| 9 | use embassy_stm32::exti::ExtiInput; | 9 | use embassy_stm32::exti::ExtiInput; |
| 10 | use embassy_stm32::gpio::{Input, Pull}; | 10 | use embassy_stm32::gpio::{Input, Pull}; |
| 11 | use embassy_stm32::{rcc, Peripherals}; | 11 | use embassy_stm32::Peripherals; |
| 12 | use embassy_traits::gpio::{WaitForFallingEdge, WaitForRisingEdge}; | 12 | use embassy_traits::gpio::{WaitForFallingEdge, WaitForRisingEdge}; |
| 13 | use example_common::*; | 13 | use example_common::*; |
| 14 | 14 | ||
| 15 | #[embassy::main] | 15 | fn config() -> embassy_stm32::Config { |
| 16 | async fn main(_spawner: Spawner, mut p: Peripherals) { | 16 | let mut config = embassy_stm32::Config::default(); |
| 17 | let mut rcc = rcc::Rcc::new(p.RCC); | 17 | config.rcc.enable_hsi48 = true; |
| 18 | // Enables SYSCFG | 18 | config |
| 19 | let _ = rcc.enable_hsi48(&mut p.SYSCFG, p.CRS); | 19 | } |
| 20 | 20 | ||
| 21 | #[embassy::main(config = "config()")] | ||
| 22 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 21 | let button = Input::new(p.PB2, Pull::Up); | 23 | let button = Input::new(p.PB2, Pull::Up); |
| 22 | let mut button = ExtiInput::new(button, p.EXTI2); | 24 | let mut button = ExtiInput::new(button, p.EXTI2); |
| 23 | 25 | ||
diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 7ce859a8d..df08ba18c 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs | |||
| @@ -11,10 +11,8 @@ mod example_common; | |||
| 11 | 11 | ||
| 12 | use embassy_lora::{sx127x::*, LoraTimer}; | 12 | use embassy_lora::{sx127x::*, LoraTimer}; |
| 13 | use embassy_stm32::{ | 13 | use embassy_stm32::{ |
| 14 | dbgmcu::Dbgmcu, | ||
| 15 | exti::ExtiInput, | 14 | exti::ExtiInput, |
| 16 | gpio::{Input, Level, Output, Pull, Speed}, | 15 | gpio::{Input, Level, Output, Pull, Speed}, |
| 17 | rcc, | ||
| 18 | rng::Rng, | 16 | rng::Rng, |
| 19 | spi, | 17 | spi, |
| 20 | time::U32Ext, | 18 | time::U32Ext, |
| @@ -26,18 +24,12 @@ use lorawan_encoding::default_crypto::DefaultFactory as Crypto; | |||
| 26 | fn config() -> embassy_stm32::Config { | 24 | fn config() -> embassy_stm32::Config { |
| 27 | let mut config = embassy_stm32::Config::default(); | 25 | let mut config = embassy_stm32::Config::default(); |
| 28 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | 26 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; |
| 27 | config.rcc.enable_hsi48 = true; | ||
| 29 | config | 28 | config |
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | #[embassy::main(config = "config()")] | 31 | #[embassy::main(config = "config()")] |
| 33 | async fn main(_spawner: embassy::executor::Spawner, mut p: Peripherals) { | 32 | async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { |
| 34 | unsafe { | ||
| 35 | Dbgmcu::enable_all(); | ||
| 36 | } | ||
| 37 | |||
| 38 | let mut rcc = rcc::Rcc::new(p.RCC); | ||
| 39 | let _ = rcc.enable_hsi48(&mut p.SYSCFG, p.CRS); | ||
| 40 | |||
| 41 | // SPI for sx127x | 33 | // SPI for sx127x |
| 42 | let spi = spi::Spi::new( | 34 | let spi = spi::Spi::new( |
| 43 | p.SPI1, | 35 | p.SPI1, |
diff --git a/examples/stm32wl55/src/bin/lorawan.rs b/examples/stm32wl55/src/bin/lorawan.rs index 7048a5814..5d26dead2 100644 --- a/examples/stm32wl55/src/bin/lorawan.rs +++ b/examples/stm32wl55/src/bin/lorawan.rs | |||
| @@ -10,10 +10,9 @@ mod example_common; | |||
| 10 | 10 | ||
| 11 | use embassy_lora::{stm32wl::*, LoraTimer}; | 11 | use embassy_lora::{stm32wl::*, LoraTimer}; |
| 12 | use embassy_stm32::{ | 12 | use embassy_stm32::{ |
| 13 | dbgmcu::Dbgmcu, | ||
| 14 | dma::NoDma, | 13 | dma::NoDma, |
| 15 | gpio::{Level, Output, Pin, Speed}, | 14 | gpio::{Level, Output, Pin, Speed}, |
| 16 | interrupt, pac, rcc, | 15 | interrupt, pac, |
| 17 | rng::Rng, | 16 | rng::Rng, |
| 18 | subghz::*, | 17 | subghz::*, |
| 19 | Peripherals, | 18 | Peripherals, |
| @@ -24,19 +23,13 @@ use lorawan_encoding::default_crypto::DefaultFactory as Crypto; | |||
| 24 | fn config() -> embassy_stm32::Config { | 23 | fn config() -> embassy_stm32::Config { |
| 25 | let mut config = embassy_stm32::Config::default(); | 24 | let mut config = embassy_stm32::Config::default(); |
| 26 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; | 25 | config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; |
| 26 | config.rcc.enable_lsi = true; | ||
| 27 | config | 27 | config |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | #[embassy::main(config = "config()")] | 30 | #[embassy::main(config = "config()")] |
| 31 | async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { | 31 | async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { |
| 32 | unsafe { | 32 | unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } |
| 33 | Dbgmcu::enable_all(); | ||
| 34 | let mut rcc = rcc::Rcc::new(p.RCC); | ||
| 35 | rcc.enable_lsi(); | ||
| 36 | pac::RCC.ccipr().modify(|w| { | ||
| 37 | w.set_rngsel(0b01); | ||
| 38 | }); | ||
| 39 | } | ||
| 40 | 33 | ||
| 41 | let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | 34 | let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High); |
| 42 | let ctrl2 = Output::new(p.PC4.degrade(), Level::High, Speed::High); | 35 | let ctrl2 = Output::new(p.PC4.degrade(), Level::High, Speed::High); |
diff --git a/examples/stm32wl55/src/bin/subghz.rs b/examples/stm32wl55/src/bin/subghz.rs index a73c361c2..52fe6e9fa 100644 --- a/examples/stm32wl55/src/bin/subghz.rs +++ b/examples/stm32wl55/src/bin/subghz.rs | |||
| @@ -11,7 +11,6 @@ mod example_common; | |||
| 11 | use embassy::channel::signal::Signal; | 11 | use embassy::channel::signal::Signal; |
| 12 | use embassy::interrupt::{Interrupt, InterruptExt}; | 12 | use embassy::interrupt::{Interrupt, InterruptExt}; |
| 13 | use embassy::traits::gpio::WaitForRisingEdge; | 13 | use embassy::traits::gpio::WaitForRisingEdge; |
| 14 | use embassy_stm32::dbgmcu::Dbgmcu; | ||
| 15 | use embassy_stm32::dma::NoDma; | 14 | use embassy_stm32::dma::NoDma; |
| 16 | use embassy_stm32::exti::ExtiInput; | 15 | use embassy_stm32::exti::ExtiInput; |
| 17 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 16 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; |
| @@ -72,10 +71,6 @@ fn config() -> embassy_stm32::Config { | |||
| 72 | 71 | ||
| 73 | #[embassy::main(config = "config()")] | 72 | #[embassy::main(config = "config()")] |
| 74 | async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { | 73 | async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { |
| 75 | unsafe { | ||
| 76 | Dbgmcu::enable_all(); | ||
| 77 | } | ||
| 78 | |||
| 79 | let mut led1 = Output::new(p.PB15, Level::High, Speed::Low); | 74 | let mut led1 = Output::new(p.PB15, Level::High, Speed::Low); |
| 80 | let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low); | 75 | let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low); |
| 81 | let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low); | 76 | let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low); |
diff --git a/stm32-data b/stm32-data | |||
| Subproject 27f9d6dc2c5afaa5003ce9afc06def9b16d30ad | Subproject 3fa97966f07d43a28c0031175591e1c2ff5d083 | ||
diff --git a/tests/stm32/link_ram.x b/tests/stm32/link_ram.x index d23ffc747..26da86baa 100644 --- a/tests/stm32/link_ram.x +++ b/tests/stm32/link_ram.x | |||
| @@ -125,6 +125,7 @@ SECTIONS | |||
| 125 | { | 125 | { |
| 126 | . = ALIGN(4); | 126 | . = ALIGN(4); |
| 127 | __sdata = .; | 127 | __sdata = .; |
| 128 | __edata = .; | ||
| 128 | *(.data .data.*); | 129 | *(.data .data.*); |
| 129 | . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ | 130 | . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ |
| 130 | } > RAM | 131 | } > RAM |
| @@ -132,7 +133,6 @@ SECTIONS | |||
| 132 | * use the .data loading mechanism by pushing __edata. Note: do not change | 133 | * use the .data loading mechanism by pushing __edata. Note: do not change |
| 133 | * output region or load region in those user sections! */ | 134 | * output region or load region in those user sections! */ |
| 134 | . = ALIGN(4); | 135 | . = ALIGN(4); |
| 135 | __edata = .; | ||
| 136 | 136 | ||
| 137 | /* LMA of .data */ | 137 | /* LMA of .data */ |
| 138 | __sidata = LOADADDR(.data); | 138 | __sidata = LOADADDR(.data); |
