aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorThales Fragoso <[email protected]>2021-05-18 21:52:34 -0300
committerThales Fragoso <[email protected]>2021-05-21 20:13:39 -0300
commit2ea12d96eeb99b28abe321c577b830c48a7f48a4 (patch)
tree09be0c87bd3b064b54ec06945295ab00278989ed /embassy-stm32
parent054f0d51dc8f953140c10585e697e6cf9b500ae7 (diff)
More work on H7 RCC
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/src/rcc/h7/mod.rs209
-rw-r--r--embassy-stm32/src/rcc/h7/pll.rs14
-rw-r--r--embassy-stm32/src/time.rs2
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 @@
1use core::marker::PhantomData;
2
3use embassy::util::Unborrow;
4use embassy_extras::unborrow;
5
6use crate::fmt::assert;
7use crate::pac::peripherals;
1use crate::pac::RCC; 8use crate::pac::RCC;
9use crate::time::Hertz;
2 10
3mod pll; 11mod pll;
12use pll::pll_setup;
4pub use pll::PllConfig; 13pub use pll::PllConfig;
5 14
6const HSI: u32 = 64_000_000; // Hz 15const HSI: Hertz = Hertz(64_000_000);
7const CSI: u32 = 4_000_000; // Hz 16const CSI: Hertz = Hertz(4_000_000);
8const HSI48: u32 = 48_000_000; // Hz 17const HSI48: Hertz = Hertz(48_000_000);
9const LSI: u32 = 32_000; // Hz 18const 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)]
14pub struct Config { 23pub 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)]
45pub 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
56impl Default for VoltageScale {
57 fn default() -> Self {
58 Self::Scale1
59 }
60}
61
62pub struct Rcc<'d> {
63 inner: PhantomData<&'d ()>,
64 config: Config,
65}
66
67impl<'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 @@
1use super::{Config, HSI, RCC}; 1use super::{Config, Hertz, HSI, RCC};
2use crate::fmt::assert; 2use crate::fmt::assert;
3 3
4const VCO_MIN: u32 = 150_000_000; 4const 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)]
8pub struct PllConfig { 8pub 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
14pub(super) struct PllConfigResults { 14pub(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 @@
5pub struct Bps(pub u32); 5pub 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)]
9pub struct Hertz(pub u32); 9pub struct Hertz(pub u32);
10 10
11/// KiloHertz 11/// KiloHertz