diff options
| -rw-r--r-- | embassy-stm32/src/rcc/h7/mod.rs | 209 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h7/pll.rs | 14 | ||||
| -rw-r--r-- | embassy-stm32/src/time.rs | 2 |
3 files changed, 205 insertions, 20 deletions
diff --git a/embassy-stm32/src/rcc/h7/mod.rs b/embassy-stm32/src/rcc/h7/mod.rs index 69b9c79a9..b27eff747 100644 --- a/embassy-stm32/src/rcc/h7/mod.rs +++ b/embassy-stm32/src/rcc/h7/mod.rs | |||
| @@ -1,27 +1,212 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy::util::Unborrow; | ||
| 4 | use embassy_extras::unborrow; | ||
| 5 | |||
| 6 | use crate::fmt::assert; | ||
| 7 | use crate::pac::peripherals; | ||
| 1 | use crate::pac::RCC; | 8 | use crate::pac::RCC; |
| 9 | use crate::time::Hertz; | ||
| 2 | 10 | ||
| 3 | mod pll; | 11 | mod pll; |
| 12 | use pll::pll_setup; | ||
| 4 | pub use pll::PllConfig; | 13 | pub use pll::PllConfig; |
| 5 | 14 | ||
| 6 | const HSI: u32 = 64_000_000; // Hz | 15 | const HSI: Hertz = Hertz(64_000_000); |
| 7 | const CSI: u32 = 4_000_000; // Hz | 16 | const CSI: Hertz = Hertz(4_000_000); |
| 8 | const HSI48: u32 = 48_000_000; // Hz | 17 | const HSI48: Hertz = Hertz(48_000_000); |
| 9 | const LSI: u32 = 32_000; // Hz | 18 | const LSI: Hertz = Hertz(32_000); |
| 10 | 19 | ||
| 11 | /// Configuration of the core clocks | 20 | /// Configuration of the core clocks |
| 12 | #[non_exhaustive] | 21 | #[non_exhaustive] |
| 13 | #[derive(Default)] | 22 | #[derive(Default)] |
| 14 | pub struct Config { | 23 | pub struct Config { |
| 15 | pub hse: Option<u32>, | 24 | pub hse: Option<Hertz>, |
| 16 | pub bypass_hse: bool, | 25 | pub bypass_hse: bool, |
| 17 | pub sys_ck: Option<u32>, | 26 | pub sys_ck: Option<Hertz>, |
| 18 | pub per_ck: Option<u32>, | 27 | pub per_ck: Option<Hertz>, |
| 19 | pub hclk: Option<u32>, | 28 | rcc_hclk: Option<Hertz>, |
| 20 | pub pclk1: Option<u32>, | 29 | pub hclk: Option<Hertz>, |
| 21 | pub pclk2: Option<u32>, | 30 | pub pclk1: Option<Hertz>, |
| 22 | pub pclk3: Option<u32>, | 31 | pub pclk2: Option<Hertz>, |
| 23 | pub pclk4: Option<u32>, | 32 | pub pclk3: Option<Hertz>, |
| 33 | pub pclk4: Option<Hertz>, | ||
| 24 | pub pll1: PllConfig, | 34 | pub pll1: PllConfig, |
| 25 | pub pll2: PllConfig, | 35 | pub pll2: PllConfig, |
| 26 | pub pll3: PllConfig, | 36 | pub pll3: PllConfig, |
| 37 | pub vos: VoltageScale, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Voltage Scale | ||
| 41 | /// | ||
| 42 | /// Represents the voltage range feeding the CPU core. The maximum core | ||
| 43 | /// clock frequency depends on this value. | ||
| 44 | #[derive(Copy, Clone, PartialEq)] | ||
| 45 | pub enum VoltageScale { | ||
| 46 | /// VOS 0 range VCORE 1.26V - 1.40V | ||
| 47 | Scale0, | ||
| 48 | /// VOS 1 range VCORE 1.15V - 1.26V | ||
| 49 | Scale1, | ||
| 50 | /// VOS 2 range VCORE 1.05V - 1.15V | ||
| 51 | Scale2, | ||
| 52 | /// VOS 3 range VCORE 0.95V - 1.05V | ||
| 53 | Scale3, | ||
| 54 | } | ||
| 55 | |||
| 56 | impl Default for VoltageScale { | ||
| 57 | fn default() -> Self { | ||
| 58 | Self::Scale1 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | pub struct Rcc<'d> { | ||
| 63 | inner: PhantomData<&'d ()>, | ||
| 64 | config: Config, | ||
| 65 | } | ||
| 66 | |||
| 67 | impl<'d> Rcc<'d> { | ||
| 68 | pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self { | ||
| 69 | Self { | ||
| 70 | inner: PhantomData, | ||
| 71 | config, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | // TODO: FLASH and PWR | ||
| 76 | /// Freeze the core clocks, returning a Core Clocks Distribution | ||
| 77 | /// and Reset (CCDR) structure. The actual frequency of the clocks | ||
| 78 | /// configured is returned in the `clocks` member of the CCDR | ||
| 79 | /// structure. | ||
| 80 | /// | ||
| 81 | /// Note that `freeze` will never result in a clock _faster_ than | ||
| 82 | /// that specified. It may result in a clock that is a factor of [1, | ||
| 83 | /// 2) slower. | ||
| 84 | /// | ||
| 85 | /// `syscfg` is required to enable the I/O compensation cell. | ||
| 86 | /// | ||
| 87 | /// # Panics | ||
| 88 | /// | ||
| 89 | /// If a clock specification cannot be achieved within the | ||
| 90 | /// hardware specification then this function will panic. This | ||
| 91 | /// function may also panic if a clock specification can be | ||
| 92 | /// achieved, but the mechanism for doing so is not yet | ||
| 93 | /// implemented here. | ||
| 94 | pub fn freeze(mut self) { | ||
| 95 | use crate::pac::rcc::vals::{Ckpersel, Hpre, Hsidiv, Hsion, Lsion, Timpre}; | ||
| 96 | |||
| 97 | let srcclk = self.config.hse.unwrap_or(HSI); // Available clocks | ||
| 98 | let (sys_ck, sys_use_pll1_p) = self.sys_ck_setup(srcclk); | ||
| 99 | |||
| 100 | // NOTE(unsafe) We have exclusive access to the RCC | ||
| 101 | let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = | ||
| 102 | unsafe { pll_setup(srcclk.0, &self.config.pll1, 0) }; | ||
| 103 | let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = | ||
| 104 | unsafe { pll_setup(srcclk.0, &self.config.pll2, 1) }; | ||
| 105 | let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = | ||
| 106 | unsafe { pll_setup(srcclk.0, &self.config.pll3, 2) }; | ||
| 107 | |||
| 108 | let sys_ck = if sys_use_pll1_p { | ||
| 109 | Hertz(pll1_p_ck.unwrap()) // Must have been set by sys_ck_setup | ||
| 110 | } else { | ||
| 111 | sys_ck | ||
| 112 | }; | ||
| 113 | |||
| 114 | // NOTE(unsafe) We own the regblock | ||
| 115 | unsafe { | ||
| 116 | // This routine does not support HSIDIV != 1. To | ||
| 117 | // do so it would need to ensure all PLLxON bits are clear | ||
| 118 | // before changing the value of HSIDIV | ||
| 119 | let cr = RCC.cr().read(); | ||
| 120 | assert!(cr.hsion() == Hsion::ON); | ||
| 121 | assert!(cr.hsidiv() == Hsidiv::DIV1); | ||
| 122 | |||
| 123 | RCC.csr().modify(|w| w.set_lsion(Lsion::ON)); | ||
| 124 | while !RCC.csr().read().lsirdy() {} | ||
| 125 | } | ||
| 126 | |||
| 127 | // per_ck from HSI by default | ||
| 128 | let (per_ck, ckpersel) = match (self.config.per_ck == self.config.hse, self.config.per_ck) { | ||
| 129 | (true, Some(hse)) => (hse, Ckpersel::HSE), // HSE | ||
| 130 | (_, Some(CSI)) => (CSI, Ckpersel::CSI), // CSI | ||
| 131 | _ => (HSI, Ckpersel::HSI), // HSI | ||
| 132 | }; | ||
| 133 | |||
| 134 | // D1 Core Prescaler | ||
| 135 | // Set to 1 | ||
| 136 | let d1cpre_bits = 0; | ||
| 137 | let d1cpre_div = 1; | ||
| 138 | let sys_d1cpre_ck = sys_ck.0 / d1cpre_div; | ||
| 139 | |||
| 140 | // Timer prescaler selection | ||
| 141 | let timpre = Timpre::DEFAULTX2; | ||
| 142 | |||
| 143 | // Refer to part datasheet "General operating conditions" | ||
| 144 | // table for (rev V). We do not assert checks for earlier | ||
| 145 | // revisions which may have lower limits. | ||
| 146 | let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match self.config.vos { | ||
| 147 | VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000), | ||
| 148 | VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000), | ||
| 149 | VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000), | ||
| 150 | _ => (200_000_000, 100_000_000, 50_000_000), | ||
| 151 | }; | ||
| 152 | assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max); | ||
| 153 | |||
| 154 | let rcc_hclk = self | ||
| 155 | .config | ||
| 156 | .rcc_hclk | ||
| 157 | .map(|v| v.0) | ||
| 158 | .unwrap_or(sys_d1cpre_ck / 2); | ||
| 159 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 160 | |||
| 161 | // Estimate divisor | ||
| 162 | let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk { | ||
| 163 | 0 => unreachable!(), | ||
| 164 | 1 => (Hpre::DIV1, 1), | ||
| 165 | 2 => (Hpre::DIV2, 2), | ||
| 166 | 3..=5 => (Hpre::DIV4, 4), | ||
| 167 | 6..=11 => (Hpre::DIV8, 8), | ||
| 168 | 12..=39 => (Hpre::DIV16, 16), | ||
| 169 | 40..=95 => (Hpre::DIV64, 64), | ||
| 170 | 96..=191 => (Hpre::DIV128, 128), | ||
| 171 | 192..=383 => (Hpre::DIV256, 256), | ||
| 172 | _ => (Hpre::DIV512, 512), | ||
| 173 | }; | ||
| 174 | // Calculate real AXI and AHB clock | ||
| 175 | let rcc_hclk = sys_d1cpre_ck / hpre_div; | ||
| 176 | assert!(rcc_hclk <= rcc_hclk_max); | ||
| 177 | let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7 | ||
| 178 | |||
| 179 | todo!() | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Setup sys_ck | ||
| 183 | /// Returns sys_ck frequency, and a pll1_p_ck | ||
| 184 | fn sys_ck_setup(&mut self, srcclk: Hertz) -> (Hertz, bool) { | ||
| 185 | // Compare available with wanted clocks | ||
| 186 | let sys_ck = self.config.sys_ck.unwrap_or(srcclk); | ||
| 187 | |||
| 188 | if sys_ck != srcclk { | ||
| 189 | // The requested system clock is not the immediately available | ||
| 190 | // HSE/HSI clock. Perhaps there are other ways of obtaining | ||
| 191 | // the requested system clock (such as `HSIDIV`) but we will | ||
| 192 | // ignore those for now. | ||
| 193 | // | ||
| 194 | // Therefore we must use pll1_p_ck | ||
| 195 | let pll1_p_ck = match self.config.pll1.p_ck { | ||
| 196 | Some(p_ck) => { | ||
| 197 | assert!(p_ck == sys_ck, | ||
| 198 | "Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck"); | ||
| 199 | Some(p_ck) | ||
| 200 | } | ||
| 201 | None => Some(sys_ck), | ||
| 202 | }; | ||
| 203 | self.config.pll1.p_ck = pll1_p_ck; | ||
| 204 | |||
| 205 | (sys_ck, true) | ||
| 206 | } else { | ||
| 207 | // sys_ck is derived directly from a source clock | ||
| 208 | // (HSE/HSI). pll1_p_ck can be as requested | ||
| 209 | (sys_ck, false) | ||
| 210 | } | ||
| 211 | } | ||
| 27 | } | 212 | } |
diff --git a/embassy-stm32/src/rcc/h7/pll.rs b/embassy-stm32/src/rcc/h7/pll.rs index 37bd611cc..ead33e81e 100644 --- a/embassy-stm32/src/rcc/h7/pll.rs +++ b/embassy-stm32/src/rcc/h7/pll.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use super::{Config, HSI, RCC}; | 1 | use super::{Config, Hertz, HSI, RCC}; |
| 2 | use crate::fmt::assert; | 2 | use crate::fmt::assert; |
| 3 | 3 | ||
| 4 | const VCO_MIN: u32 = 150_000_000; | 4 | const VCO_MIN: u32 = 150_000_000; |
| @@ -6,9 +6,9 @@ const VCO_MAX: u32 = 420_000_000; | |||
| 6 | 6 | ||
| 7 | #[derive(Default)] | 7 | #[derive(Default)] |
| 8 | pub struct PllConfig { | 8 | pub struct PllConfig { |
| 9 | pub p_ck: Option<u32>, | 9 | pub p_ck: Option<Hertz>, |
| 10 | pub q_ck: Option<u32>, | 10 | pub q_ck: Option<Hertz>, |
| 11 | pub r_ck: Option<u32>, | 11 | pub r_ck: Option<Hertz>, |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | pub(super) struct PllConfigResults { | 14 | pub(super) struct PllConfigResults { |
| @@ -84,7 +84,7 @@ pub(super) unsafe fn pll_setup( | |||
| 84 | 84 | ||
| 85 | match config.p_ck { | 85 | match config.p_ck { |
| 86 | Some(requested_output) => { | 86 | Some(requested_output) => { |
| 87 | let config_results = vco_setup(pll_src, requested_output, plln); | 87 | let config_results = vco_setup(pll_src, requested_output.0, plln); |
| 88 | let PllConfigResults { | 88 | let PllConfigResults { |
| 89 | ref_x_ck, | 89 | ref_x_ck, |
| 90 | pll_x_m, | 90 | pll_x_m, |
| @@ -113,7 +113,7 @@ pub(super) unsafe fn pll_setup( | |||
| 113 | 113 | ||
| 114 | // Calulate additional output dividers | 114 | // Calulate additional output dividers |
| 115 | let q_ck = match config.q_ck { | 115 | let q_ck = match config.q_ck { |
| 116 | Some(ck) if ck > 0 => { | 116 | Some(Hertz(ck)) if ck > 0 => { |
| 117 | let div = (vco_ck + ck - 1) / ck; | 117 | let div = (vco_ck + ck - 1) / ck; |
| 118 | RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8)); | 118 | RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8)); |
| 119 | RCC.pllcfgr() | 119 | RCC.pllcfgr() |
| @@ -123,7 +123,7 @@ pub(super) unsafe fn pll_setup( | |||
| 123 | _ => None, | 123 | _ => None, |
| 124 | }; | 124 | }; |
| 125 | let r_ck = match config.r_ck { | 125 | let r_ck = match config.r_ck { |
| 126 | Some(ck) if ck > 0 => { | 126 | Some(Hertz(ck)) if ck > 0 => { |
| 127 | let div = (vco_ck + ck - 1) / ck; | 127 | let div = (vco_ck + ck - 1) / ck; |
| 128 | RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8)); | 128 | RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8)); |
| 129 | RCC.pllcfgr() | 129 | RCC.pllcfgr() |
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index c131415c4..ac03ce5c4 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | pub struct Bps(pub u32); | 5 | pub struct Bps(pub u32); |
| 6 | 6 | ||
| 7 | /// Hertz | 7 | /// Hertz |
| 8 | #[derive(PartialEq, PartialOrd, Clone, Copy, Debug)] | 8 | #[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)] |
| 9 | pub struct Hertz(pub u32); | 9 | pub struct Hertz(pub u32); |
| 10 | 10 | ||
| 11 | /// KiloHertz | 11 | /// KiloHertz |
