diff options
| author | Thales Fragoso <[email protected]> | 2021-05-17 21:35:29 -0300 |
|---|---|---|
| committer | Thales Fragoso <[email protected]> | 2021-05-21 20:13:37 -0300 |
| commit | 054f0d51dc8f953140c10585e697e6cf9b500ae7 (patch) | |
| tree | 144dde7e747fae22ec2934dc7037170eac683363 | |
| parent | 7e388fcf5882011b00508a4e67f68d26c5137f7e (diff) | |
H7: Add initial PLL configuration
| -rw-r--r-- | embassy-stm32/src/lib.rs | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h7/mod.rs | 27 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h7/pll.rs | 150 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 4 |
4 files changed, 183 insertions, 2 deletions
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index c856c8eb1..8d7b67b74 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -13,6 +13,7 @@ pub mod fmt; | |||
| 13 | pub mod dma; | 13 | pub mod dma; |
| 14 | pub mod exti; | 14 | pub mod exti; |
| 15 | pub mod gpio; | 15 | pub mod gpio; |
| 16 | mod rcc; | ||
| 16 | #[cfg(feature = "_rng")] | 17 | #[cfg(feature = "_rng")] |
| 17 | pub mod rng; | 18 | pub mod rng; |
| 18 | #[cfg(feature = "_sdmmc")] | 19 | #[cfg(feature = "_sdmmc")] |
| @@ -23,8 +24,7 @@ pub mod spi; | |||
| 23 | pub mod usart; | 24 | pub mod usart; |
| 24 | 25 | ||
| 25 | // This must go LAST so that it sees the `impl_foo!` macros | 26 | // This must go LAST so that it sees the `impl_foo!` macros |
| 26 | mod pac; | 27 | pub mod pac; |
| 27 | |||
| 28 | pub mod time; | 28 | pub mod time; |
| 29 | 29 | ||
| 30 | pub use embassy_macros::interrupt; | 30 | pub use embassy_macros::interrupt; |
diff --git a/embassy-stm32/src/rcc/h7/mod.rs b/embassy-stm32/src/rcc/h7/mod.rs new file mode 100644 index 000000000..69b9c79a9 --- /dev/null +++ b/embassy-stm32/src/rcc/h7/mod.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | use crate::pac::RCC; | ||
| 2 | |||
| 3 | mod pll; | ||
| 4 | pub use pll::PllConfig; | ||
| 5 | |||
| 6 | const HSI: u32 = 64_000_000; // Hz | ||
| 7 | const CSI: u32 = 4_000_000; // Hz | ||
| 8 | const HSI48: u32 = 48_000_000; // Hz | ||
| 9 | const LSI: u32 = 32_000; // Hz | ||
| 10 | |||
| 11 | /// Configuration of the core clocks | ||
| 12 | #[non_exhaustive] | ||
| 13 | #[derive(Default)] | ||
| 14 | pub struct Config { | ||
| 15 | pub hse: Option<u32>, | ||
| 16 | pub bypass_hse: bool, | ||
| 17 | pub sys_ck: Option<u32>, | ||
| 18 | pub per_ck: Option<u32>, | ||
| 19 | pub hclk: Option<u32>, | ||
| 20 | pub pclk1: Option<u32>, | ||
| 21 | pub pclk2: Option<u32>, | ||
| 22 | pub pclk3: Option<u32>, | ||
| 23 | pub pclk4: Option<u32>, | ||
| 24 | pub pll1: PllConfig, | ||
| 25 | pub pll2: PllConfig, | ||
| 26 | pub pll3: PllConfig, | ||
| 27 | } | ||
diff --git a/embassy-stm32/src/rcc/h7/pll.rs b/embassy-stm32/src/rcc/h7/pll.rs new file mode 100644 index 000000000..37bd611cc --- /dev/null +++ b/embassy-stm32/src/rcc/h7/pll.rs | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | use super::{Config, HSI, RCC}; | ||
| 2 | use crate::fmt::assert; | ||
| 3 | |||
| 4 | const VCO_MIN: u32 = 150_000_000; | ||
| 5 | const VCO_MAX: u32 = 420_000_000; | ||
| 6 | |||
| 7 | #[derive(Default)] | ||
| 8 | pub struct PllConfig { | ||
| 9 | pub p_ck: Option<u32>, | ||
| 10 | pub q_ck: Option<u32>, | ||
| 11 | pub r_ck: Option<u32>, | ||
| 12 | } | ||
| 13 | |||
| 14 | pub(super) struct PllConfigResults { | ||
| 15 | pub ref_x_ck: u32, | ||
| 16 | pub pll_x_m: u32, | ||
| 17 | pub pll_x_p: u32, | ||
| 18 | pub vco_ck_target: u32, | ||
| 19 | } | ||
| 20 | |||
| 21 | fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) { | ||
| 22 | let pll_x_p = if plln == 0 { | ||
| 23 | if output > VCO_MAX / 2 { | ||
| 24 | 1 | ||
| 25 | } else { | ||
| 26 | ((VCO_MAX / output) | 1) - 1 // Must be even or unity | ||
| 27 | } | ||
| 28 | } else { | ||
| 29 | // Specific to PLL2/3, will subtract 1 later | ||
| 30 | if output > VCO_MAX / 2 { | ||
| 31 | 1 | ||
| 32 | } else { | ||
| 33 | VCO_MAX / output | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | let vco_ck = output + pll_x_p; | ||
| 38 | |||
| 39 | assert!(pll_x_p < 128); | ||
| 40 | assert!(vco_ck >= VCO_MIN); | ||
| 41 | assert!(vco_ck <= VCO_MAX); | ||
| 42 | |||
| 43 | (vco_ck, pll_x_p) | ||
| 44 | } | ||
| 45 | |||
| 46 | /// # Safety | ||
| 47 | /// | ||
| 48 | /// Must have exclusive access to the RCC register block | ||
| 49 | unsafe fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults { | ||
| 50 | use crate::pac::rcc::vals::{Pll1rge, Pll1vcosel}; | ||
| 51 | |||
| 52 | let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln); | ||
| 53 | |||
| 54 | // Input divisor, resulting in a reference clock in the range | ||
| 55 | // 1 to 2 MHz. Choose the highest reference clock (lowest m) | ||
| 56 | let pll_x_m = (pll_src + 1_999_999) / 2_000_000; | ||
| 57 | assert!(pll_x_m < 64); | ||
| 58 | |||
| 59 | // Calculate resulting reference clock | ||
| 60 | let ref_x_ck = pll_src / pll_x_m; | ||
| 61 | assert!((1_000_000..=2_000_000).contains(&ref_x_ck)); | ||
| 62 | |||
| 63 | RCC.pllcfgr().modify(|w| { | ||
| 64 | w.set_pllvcosel(plln, Pll1vcosel::MEDIUMVCO); | ||
| 65 | w.set_pllrge(plln, Pll1rge::RANGE1); | ||
| 66 | }); | ||
| 67 | PllConfigResults { | ||
| 68 | ref_x_ck, | ||
| 69 | pll_x_m, | ||
| 70 | pll_x_p, | ||
| 71 | vco_ck_target, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | /// # Safety | ||
| 76 | /// | ||
| 77 | /// Must have exclusive access to the RCC register block | ||
| 78 | pub(super) unsafe fn pll_setup( | ||
| 79 | pll_src: u32, | ||
| 80 | config: &PllConfig, | ||
| 81 | plln: usize, | ||
| 82 | ) -> (Option<u32>, Option<u32>, Option<u32>) { | ||
| 83 | use crate::pac::rcc::vals::{Divp1, Divp1en, Pll1fracen}; | ||
| 84 | |||
| 85 | match config.p_ck { | ||
| 86 | Some(requested_output) => { | ||
| 87 | let config_results = vco_setup(pll_src, requested_output, plln); | ||
| 88 | let PllConfigResults { | ||
| 89 | ref_x_ck, | ||
| 90 | pll_x_m, | ||
| 91 | pll_x_p, | ||
| 92 | vco_ck_target, | ||
| 93 | } = config_results; | ||
| 94 | |||
| 95 | RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8)); | ||
| 96 | |||
| 97 | // Feedback divider. Integer only | ||
| 98 | let pll_x_n = vco_ck_target / ref_x_ck; | ||
| 99 | assert!(pll_x_n >= 4); | ||
| 100 | assert!(pll_x_n <= 512); | ||
| 101 | RCC.plldivr(plln) | ||
| 102 | .modify(|w| w.set_divn1((pll_x_n - 1) as u16)); | ||
| 103 | |||
| 104 | // No FRACN | ||
| 105 | RCC.pllcfgr() | ||
| 106 | .modify(|w| w.set_pllfracen(plln, Pll1fracen::RESET)); | ||
| 107 | let vco_ck = ref_x_ck * pll_x_n; | ||
| 108 | |||
| 109 | RCC.plldivr(plln) | ||
| 110 | .modify(|w| w.set_divp1(Divp1((pll_x_p - 1) as u8))); | ||
| 111 | RCC.pllcfgr() | ||
| 112 | .modify(|w| w.set_divpen(plln, Divp1en::ENABLED)); | ||
| 113 | |||
| 114 | // Calulate additional output dividers | ||
| 115 | let q_ck = match config.q_ck { | ||
| 116 | Some(ck) if ck > 0 => { | ||
| 117 | let div = (vco_ck + ck - 1) / ck; | ||
| 118 | RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8)); | ||
| 119 | RCC.pllcfgr() | ||
| 120 | .modify(|w| w.set_divqen(plln, Divp1en::ENABLED)); | ||
| 121 | Some(vco_ck / div) | ||
| 122 | } | ||
| 123 | _ => None, | ||
| 124 | }; | ||
| 125 | let r_ck = match config.r_ck { | ||
| 126 | Some(ck) if ck > 0 => { | ||
| 127 | let div = (vco_ck + ck - 1) / ck; | ||
| 128 | RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8)); | ||
| 129 | RCC.pllcfgr() | ||
| 130 | .modify(|w| w.set_divren(plln, Divp1en::ENABLED)); | ||
| 131 | Some(vco_ck / div) | ||
| 132 | } | ||
| 133 | _ => None, | ||
| 134 | }; | ||
| 135 | |||
| 136 | (Some(vco_ck / pll_x_p), q_ck, r_ck) | ||
| 137 | } | ||
| 138 | None => { | ||
| 139 | assert!( | ||
| 140 | config.q_ck.is_none(), | ||
| 141 | "Must set PLL P clock for Q clock to take effect!" | ||
| 142 | ); | ||
| 143 | assert!( | ||
| 144 | config.r_ck.is_none(), | ||
| 145 | "Must set PLL P clock for R clock to take effect!" | ||
| 146 | ); | ||
| 147 | (None, None, None) | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs new file mode 100644 index 000000000..0bf62ef7c --- /dev/null +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #[cfg(feature = "_stm32h7")] | ||
| 2 | mod h7; | ||
| 3 | #[cfg(feature = "_stm32h7")] | ||
| 4 | pub use h7::*; | ||
