aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoonas Javanainen <[email protected]>2022-04-26 20:33:57 +0300
committerJoonas Javanainen <[email protected]>2022-04-29 18:21:40 +0300
commit07ad52162ba05cec358c35375f1f5e91d0b398e9 (patch)
tree097b71dd0deee3065f889ee288d77ed1646f68d4
parent0cfe1dc9df9966ed36021869e9bf247aedfabc27 (diff)
Add PLL config support for F2
-rw-r--r--embassy-stm32/src/rcc/f2.rs246
-rw-r--r--embassy-stm32/src/rcc/mod.rs2
2 files changed, 227 insertions, 21 deletions
diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs
index e44613926..7074d7c3a 100644
--- a/embassy-stm32/src/rcc/f2.rs
+++ b/embassy-stm32/src/rcc/f2.rs
@@ -1,7 +1,8 @@
1use core::ops::Div; 1use core::convert::TryFrom;
2use core::ops::{Div, Mul};
2 3
3use crate::pac::flash::vals::Latency; 4use crate::pac::flash::vals::Latency;
4use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; 5use crate::pac::rcc::vals::{Hpre, Pllp, Pllsrc, Ppre, Sw};
5use crate::pac::{FLASH, RCC}; 6use crate::pac::{FLASH, RCC};
6use crate::rcc::{set_freqs, Clocks}; 7use crate::rcc::{set_freqs, Clocks};
7use crate::time::Hertz; 8use crate::time::Hertz;
@@ -20,6 +21,7 @@ pub struct HSEConfig {
20pub enum ClockSrc { 21pub enum ClockSrc {
21 HSE, 22 HSE,
22 HSI, 23 HSI,
24 PLL,
23} 25}
24 26
25/// HSE clock source 27/// HSE clock source
@@ -31,6 +33,170 @@ pub enum HSESrc {
31 Bypass, 33 Bypass,
32} 34}
33 35
36#[derive(Clone, Copy)]
37pub struct PLLConfig {
38 pub pre_div: PLLPreDiv,
39 pub mul: PLLMul,
40 pub main_div: PLLMainDiv,
41 pub pll48_div: PLL48Div,
42}
43
44impl Default for PLLConfig {
45 fn default() -> Self {
46 PLLConfig {
47 pre_div: PLLPreDiv(16),
48 mul: PLLMul(192),
49 main_div: PLLMainDiv::Div2,
50 pll48_div: PLL48Div(4),
51 }
52 }
53}
54
55impl PLLConfig {
56 pub fn clocks(&self, src_freq: Hertz) -> PLLClocks {
57 let in_freq = src_freq / self.pre_div;
58 let vco_freq = src_freq * self.mul / self.pre_div;
59 let main_freq = vco_freq / self.main_div;
60 let pll48_freq = vco_freq / self.pll48_div;
61 PLLClocks {
62 in_freq,
63 vco_freq,
64 main_freq,
65 pll48_freq,
66 }
67 }
68}
69
70/// Clock source for both main PLL and PLLI2S
71#[derive(Clone, Copy, PartialEq)]
72pub enum PLLSrc {
73 HSE,
74 HSI,
75}
76
77impl Into<Pllsrc> for PLLSrc {
78 fn into(self) -> Pllsrc {
79 match self {
80 PLLSrc::HSE => Pllsrc::HSE,
81 PLLSrc::HSI => Pllsrc::HSI,
82 }
83 }
84}
85
86/// Division factor for both main PLL and PLLI2S
87#[derive(Clone, Copy, PartialEq)]
88#[repr(transparent)]
89pub struct PLLPreDiv(u8);
90
91impl TryFrom<u8> for PLLPreDiv {
92 type Error = &'static str;
93
94 fn try_from(value: u8) -> Result<Self, Self::Error> {
95 match value {
96 2..=63 => Ok(PLLPreDiv(value)),
97 _ => Err("PLLPreDiv must be within range 2..=63"),
98 }
99 }
100}
101
102impl Div<PLLPreDiv> for Hertz {
103 type Output = Hertz;
104
105 fn div(self, rhs: PLLPreDiv) -> Self::Output {
106 Hertz(self.0 / u32::from(rhs.0))
107 }
108}
109
110/// Multiplication factor for main PLL
111#[derive(Clone, Copy, PartialEq)]
112#[repr(transparent)]
113pub struct PLLMul(u16);
114
115impl Mul<PLLMul> for Hertz {
116 type Output = Hertz;
117
118 fn mul(self, rhs: PLLMul) -> Self::Output {
119 Hertz(self.0 * u32::from(rhs.0))
120 }
121}
122
123impl TryFrom<u16> for PLLMul {
124 type Error = &'static str;
125
126 fn try_from(value: u16) -> Result<Self, Self::Error> {
127 match value {
128 192..=432 => Ok(PLLMul(value)),
129 _ => Err("PLLMul must be within range 192..=432"),
130 }
131 }
132}
133
134/// PLL division factor for the main system clock
135#[derive(Clone, Copy, PartialEq)]
136pub enum PLLMainDiv {
137 Div2,
138 Div4,
139 Div6,
140 Div8,
141}
142
143impl Into<Pllp> for PLLMainDiv {
144 fn into(self) -> Pllp {
145 match self {
146 PLLMainDiv::Div2 => Pllp::DIV2,
147 PLLMainDiv::Div4 => Pllp::DIV4,
148 PLLMainDiv::Div6 => Pllp::DIV8,
149 PLLMainDiv::Div8 => Pllp::DIV8,
150 }
151 }
152}
153
154impl Div<PLLMainDiv> for Hertz {
155 type Output = Hertz;
156
157 fn div(self, rhs: PLLMainDiv) -> Self::Output {
158 let divisor = match rhs {
159 PLLMainDiv::Div2 => 2,
160 PLLMainDiv::Div4 => 4,
161 PLLMainDiv::Div6 => 6,
162 PLLMainDiv::Div8 => 8,
163 };
164 Hertz(self.0 / divisor)
165 }
166}
167
168/// PLL division factor for USB OTG FS / SDIO / RNG
169#[derive(Clone, Copy, PartialEq)]
170#[repr(transparent)]
171pub struct PLL48Div(u8);
172
173impl Div<PLL48Div> for Hertz {
174 type Output = Hertz;
175
176 fn div(self, rhs: PLL48Div) -> Self::Output {
177 Hertz(self.0 / u32::from(rhs.0))
178 }
179}
180
181impl TryFrom<u8> for PLL48Div {
182 type Error = &'static str;
183
184 fn try_from(value: u8) -> Result<Self, Self::Error> {
185 match value {
186 2..=15 => Ok(PLL48Div(value)),
187 _ => Err("PLL48Div must be within range 2..=15"),
188 }
189 }
190}
191
192#[derive(Clone, Copy, PartialEq)]
193pub struct PLLClocks {
194 pub in_freq: Hertz,
195 pub vco_freq: Hertz,
196 pub main_freq: Hertz,
197 pub pll48_freq: Hertz,
198}
199
34/// AHB prescaler 200/// AHB prescaler
35#[derive(Clone, Copy, PartialEq)] 201#[derive(Clone, Copy, PartialEq)]
36pub enum AHBPrescaler { 202pub enum AHBPrescaler {
@@ -213,6 +379,9 @@ impl VoltageRange {
213/// Clocks configuration 379/// Clocks configuration
214pub struct Config { 380pub struct Config {
215 pub hse: Option<HSEConfig>, 381 pub hse: Option<HSEConfig>,
382 pub hsi: bool,
383 pub pll_mux: PLLSrc,
384 pub pll: PLLConfig,
216 pub mux: ClockSrc, 385 pub mux: ClockSrc,
217 pub voltage: VoltageRange, 386 pub voltage: VoltageRange,
218 pub ahb_pre: AHBPrescaler, 387 pub ahb_pre: AHBPrescaler,
@@ -225,6 +394,9 @@ impl Default for Config {
225 fn default() -> Config { 394 fn default() -> Config {
226 Config { 395 Config {
227 hse: None, 396 hse: None,
397 hsi: true,
398 pll_mux: PLLSrc::HSI,
399 pll: PLLConfig::default(),
228 voltage: VoltageRange::Min1V8, 400 voltage: VoltageRange::Min1V8,
229 mux: ClockSrc::HSI, 401 mux: ClockSrc::HSI,
230 ahb_pre: AHBPrescaler::NotDivided, 402 ahb_pre: AHBPrescaler::NotDivided,
@@ -234,31 +406,53 @@ impl Default for Config {
234 } 406 }
235} 407}
236 408
237#[inline] 409pub(crate) unsafe fn init(config: Config) {
238unsafe fn enable_hse(source: HSESrc) { 410 // Make sure HSI is enabled
239 RCC.cr().write(|w| {
240 w.set_hsebyp(match source {
241 HSESrc::Bypass => true,
242 HSESrc::Crystal => false,
243 });
244 w.set_hseon(true)
245 });
246 while !RCC.cr().read().hserdy() {}
247}
248
249#[inline]
250unsafe fn enable_hsi() {
251 RCC.cr().write(|w| w.set_hsion(true)); 411 RCC.cr().write(|w| w.set_hsion(true));
252 while !RCC.cr().read().hsirdy() {} 412 while !RCC.cr().read().hsirdy() {}
253}
254 413
255pub(crate) unsafe fn init(config: Config) {
256 if let Some(hse_config) = config.hse { 414 if let Some(hse_config) = config.hse {
257 enable_hse(hse_config.source); 415 RCC.cr().modify(|w| {
416 w.set_hsebyp(match hse_config.source {
417 HSESrc::Bypass => true,
418 HSESrc::Crystal => false,
419 });
420 w.set_hseon(true)
421 });
422 while !RCC.cr().read().hserdy() {}
258 } 423 }
424
425 let pll_src_freq = match config.pll_mux {
426 PLLSrc::HSE => {
427 config
428 .hse
429 .expect("HSE must be configured to be used as PLL input")
430 .frequency
431 }
432 PLLSrc::HSI => HSI,
433 };
434
435 // Reference: STM32F215xx/217xx datasheet Table 33. Main PLL characteristics
436 let pll_clocks = config.pll.clocks(pll_src_freq);
437 assert!(Hertz(950_000) <= pll_clocks.in_freq && pll_clocks.in_freq <= Hertz(2_100_000));
438 assert!(Hertz(192_000_000) <= pll_clocks.vco_freq && pll_clocks.vco_freq <= Hertz(432_000_000));
439 assert!(
440 Hertz(24_000_000) <= pll_clocks.main_freq && pll_clocks.main_freq <= Hertz(120_000_000)
441 );
442 // USB actually requires == 48 MHz, but other PLL48 peripherals are fine with <= 48MHz
443 assert!(pll_clocks.pll48_freq <= Hertz(48_000_000));
444
445 RCC.pllcfgr().write(|w| {
446 w.set_pllsrc(config.pll_mux.into());
447 w.set_pllm(config.pll.pre_div.0);
448 w.set_plln(config.pll.mul.0);
449 w.set_pllp(config.pll.main_div.into());
450 w.set_pllq(config.pll.pll48_div.0);
451 });
452
259 let (sys_clk, sw) = match config.mux { 453 let (sys_clk, sw) = match config.mux {
260 ClockSrc::HSI => { 454 ClockSrc::HSI => {
261 enable_hsi(); 455 assert!(config.hsi, "HSI must be enabled to be used as system clock");
262 (HSI, Sw::HSI) 456 (HSI, Sw::HSI)
263 } 457 }
264 ClockSrc::HSE => { 458 ClockSrc::HSE => {
@@ -267,6 +461,11 @@ pub(crate) unsafe fn init(config: Config) {
267 .expect("HSE must be configured to be used as system clock"); 461 .expect("HSE must be configured to be used as system clock");
268 (hse_config.frequency, Sw::HSE) 462 (hse_config.frequency, Sw::HSE)
269 } 463 }
464 ClockSrc::PLL => {
465 RCC.cr().modify(|w| w.set_pllon(true));
466 while !RCC.cr().read().pllrdy() {}
467 (pll_clocks.main_freq, Sw::PLL)
468 }
270 }; 469 };
271 // RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL 470 // RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
272 // max output to be 120 MHz, so there's no way to get higher frequencies 471 // max output to be 120 MHz, so there's no way to get higher frequencies
@@ -285,6 +484,12 @@ pub(crate) unsafe fn init(config: Config) {
285 w.set_ppre1(config.apb1_pre.into()); 484 w.set_ppre1(config.apb1_pre.into());
286 w.set_ppre2(config.apb2_pre.into()); 485 w.set_ppre2(config.apb2_pre.into());
287 }); 486 });
487 while RCC.cfgr().read().sws() != sw.0 {}
488
489 // Turn off HSI to save power if we don't need it
490 if !config.hsi {
491 RCC.cr().modify(|w| w.set_hsion(false));
492 }
288 493
289 let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { 494 let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
290 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 495 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
@@ -315,5 +520,6 @@ pub(crate) unsafe fn init(config: Config) {
315 apb1_tim: apb1_tim_freq, 520 apb1_tim: apb1_tim_freq,
316 apb2: apb2_freq, 521 apb2: apb2_freq,
317 apb2_tim: apb2_tim_freq, 522 apb2_tim: apb2_tim_freq,
523 pll48: Some(pll_clocks.pll48_freq),
318 }); 524 });
319} 525}
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 9a95836a6..d3710b8c3 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -53,7 +53,7 @@ pub struct Clocks {
53 #[cfg(any(rcc_h7, rcc_h7ab))] 53 #[cfg(any(rcc_h7, rcc_h7ab))]
54 pub ahb4: Hertz, 54 pub ahb4: Hertz,
55 55
56 #[cfg(any(rcc_f4, rcc_f410, rcc_f7))] 56 #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
57 pub pll48: Option<Hertz>, 57 pub pll48: Option<Hertz>,
58 58
59 #[cfg(rcc_f1)] 59 #[cfg(rcc_f1)]