aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorGerzain Mata <[email protected]>2025-07-25 18:57:27 -0700
committerGerzain Mata <[email protected]>2025-07-25 18:59:10 -0700
commita5e8891fe315e2ee84992d94bd7f7d5b7710cce6 (patch)
tree84de7eac12d3b6abeb920f5bd0f040bbe7501157 /embassy-stm32
parent996974e313fa5ec2c7c2d9dd0998fab244c0a180 (diff)
Added support for PLL as a clock source on STM32WBA
- PLL multiplier and dividers work - Added timer example
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/src/rcc/wba.rs247
2 files changed, 239 insertions, 12 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 02e75733e..520443466 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false }
81sdio-host = "0.9.0" 81sdio-host = "0.9.0"
82critical-section = "1.1" 82critical-section = "1.1"
83#stm32-metapac = { version = "16" } 83#stm32-metapac = { version = "16" }
84stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3" } 84stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-df3f5212f2dd70955a6b3d0137e7b4457c6047bf" }
85 85
86vcell = "0.1.3" 86vcell = "0.1.3"
87nb = "1.0.0" 87nb = "1.0.0"
@@ -110,7 +110,7 @@ proc-macro2 = "1.0.36"
110quote = "1.0.15" 110quote = "1.0.15"
111 111
112#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} 112#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]}
113stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3", default-features = false, features = ["metadata"] } 113stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-df3f5212f2dd70955a6b3d0137e7b4457c6047bf", default-features = false, features = ["metadata"] }
114 114
115[features] 115[features]
116default = ["rt"] 116default = ["rt"]
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs
index b494997b3..0025d2a51 100644
--- a/embassy-stm32/src/rcc/wba.rs
+++ b/embassy-stm32/src/rcc/wba.rs
@@ -1,19 +1,81 @@
1pub use crate::pac::pwr::vals::Vos as VoltageScale; 1pub use crate::pac::pwr::vals::Vos as VoltageScale;
2use crate::pac::rcc::regs::Cfgr1; 2use crate::pac::rcc::regs::Cfgr1;
3pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsepre as HsePrescaler, Ppre as APBPrescaler, Sw as Sysclk}; 3use core::ops::Div;
4pub use crate::pac::rcc::vals::{
5 Hpre as AHBPrescaler, Hsepre as HsePrescaler, Ppre as APBPrescaler, Sw as Sysclk, Pllsrc as PllSource,
6 Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Hpre5 as AHB5Prescaler, Hdiv5,
7};
8use crate::pac::rcc::vals::Pllrge;
4use crate::pac::{FLASH, RCC}; 9use crate::pac::{FLASH, RCC};
10use crate::rcc::LSI_FREQ;
5use crate::time::Hertz; 11use crate::time::Hertz;
6 12
13#[cfg(all(peri_usb_otg_hs))]
14pub use crate::pac::rcc::vals::Otghssel;
15
16#[cfg(all(peri_usb_otg_hs))]
17pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG};
18
7/// HSI speed 19/// HSI speed
8pub const HSI_FREQ: Hertz = Hertz(16_000_000); 20pub const HSI_FREQ: Hertz = Hertz(16_000_000);
9// HSE speed 21// HSE speed
10pub const HSE_FREQ: Hertz = Hertz(32_000_000); 22pub const HSE_FREQ: Hertz = Hertz(32_000_000);
11 23
24// Allow dividing a Hertz value by an AHB5 prescaler directly
25impl Div<AHB5Prescaler> for Hertz {
26 type Output = Hertz;
27 fn div(self, rhs: AHB5Prescaler) -> Hertz {
28 // Map the prescaler enum to its integer divisor
29 let divisor = match rhs {
30 AHB5Prescaler::DIV1 => 1,
31 AHB5Prescaler::DIV2 => 2,
32 AHB5Prescaler::DIV3 => 3,
33 AHB5Prescaler::DIV4 => 4,
34 AHB5Prescaler::DIV6 => 6,
35 _ => unreachable!("Invalid AHB5 prescaler: {:?}", rhs),
36 };
37 Hertz(self.0 / divisor)
38 }
39}
40
12#[derive(Clone, Copy, Eq, PartialEq)] 41#[derive(Clone, Copy, Eq, PartialEq)]
13pub struct Hse { 42pub struct Hse {
14 pub prescaler: HsePrescaler, 43 pub prescaler: HsePrescaler,
15} 44}
16 45
46#[derive(Clone, Copy)]
47pub struct Pll {
48 /// The clock source for the PLL.
49 pub source: PllSource,
50 /// The PLL pre-divider.
51 ///
52 /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
53 pub prediv: PllPreDiv,
54 /// The PLL multiplier.
55 ///
56 /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544
57 /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
58 pub mul: PllMul,
59 /// The divider for the P output.
60 ///
61 /// The P output is one of several options
62 /// that can be used to feed the SAI/MDF/ADF Clock mux's.
63 pub divp: Option<PllDiv>,
64 /// The divider for the Q output.
65 ///
66 /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks
67 /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's.
68 pub divq: Option<PllDiv>,
69 /// The divider for the R output.
70 ///
71 /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
72 /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
73 /// `Config { voltage_range }`.
74 pub divr: Option<PllDiv>,
75
76 pub frac: Option<u16>,
77}
78
17/// Clocks configuration 79/// Clocks configuration
18#[derive(Clone, Copy)] 80#[derive(Clone, Copy)]
19pub struct Config { 81pub struct Config {
@@ -21,15 +83,20 @@ pub struct Config {
21 pub hsi: bool, 83 pub hsi: bool,
22 pub hse: Option<Hse>, 84 pub hse: Option<Hse>,
23 85
86 // pll
87 pub pll1: Option<Pll>,
88
24 // sysclk, buses. 89 // sysclk, buses.
25 pub sys: Sysclk, 90 pub sys: Sysclk,
26 pub ahb_pre: AHBPrescaler, 91 pub ahb_pre: AHBPrescaler,
92 pub ahb5_pre: AHB5Prescaler,
27 pub apb1_pre: APBPrescaler, 93 pub apb1_pre: APBPrescaler,
28 pub apb2_pre: APBPrescaler, 94 pub apb2_pre: APBPrescaler,
29 pub apb7_pre: APBPrescaler, 95 pub apb7_pre: APBPrescaler,
30 96
31 // low speed LSI/LSE/RTC 97 // low speed LSI/LSE/RTC
32 pub ls: super::LsConfig, 98 pub lsi: super::LsConfig,
99 // pub lsi2: super::LsConfig,
33 100
34 pub voltage_scale: VoltageScale, 101 pub voltage_scale: VoltageScale,
35 102
@@ -40,14 +107,17 @@ pub struct Config {
40impl Config { 107impl Config {
41 pub const fn new() -> Self { 108 pub const fn new() -> Self {
42 Config { 109 Config {
43 hse: None,
44 hsi: true, 110 hsi: true,
111 hse: None,
112 pll1: None,
45 sys: Sysclk::HSI, 113 sys: Sysclk::HSI,
46 ahb_pre: AHBPrescaler::DIV1, 114 ahb_pre: AHBPrescaler::DIV1,
115 ahb5_pre: AHB5Prescaler::DIV1,
47 apb1_pre: APBPrescaler::DIV1, 116 apb1_pre: APBPrescaler::DIV1,
48 apb2_pre: APBPrescaler::DIV1, 117 apb2_pre: APBPrescaler::DIV1,
49 apb7_pre: APBPrescaler::DIV1, 118 apb7_pre: APBPrescaler::DIV1,
50 ls: crate::rcc::LsConfig::new(), 119 lsi: crate::rcc::LsConfig::new(),
120 // lsi2: crate::rcc::LsConfig::new(),
51 voltage_scale: VoltageScale::RANGE2, 121 voltage_scale: VoltageScale::RANGE2,
52 mux: super::mux::ClockMux::default(), 122 mux: super::mux::ClockMux::default(),
53 } 123 }
@@ -81,7 +151,7 @@ pub(crate) unsafe fn init(config: Config) {
81 crate::pac::PWR.vosr().write(|w| w.set_vos(config.voltage_scale)); 151 crate::pac::PWR.vosr().write(|w| w.set_vos(config.voltage_scale));
82 while !crate::pac::PWR.vosr().read().vosrdy() {} 152 while !crate::pac::PWR.vosr().read().vosrdy() {}
83 153
84 let rtc = config.ls.init(); 154 let rtc = config.lsi.init();
85 155
86 let hsi = config.hsi.then(|| { 156 let hsi = config.hsi.then(|| {
87 hsi_enable(); 157 hsi_enable();
@@ -99,11 +169,15 @@ pub(crate) unsafe fn init(config: Config) {
99 HSE_FREQ 169 HSE_FREQ
100 }); 170 });
101 171
172 let pll_input = PllInput {hse, hsi };
173
174 let pll1 = init_pll(config.pll1, &pll_input, config.voltage_scale);
175
102 let sys_clk = match config.sys { 176 let sys_clk = match config.sys {
103 Sysclk::HSE => hse.unwrap(), 177 Sysclk::HSE => hse.unwrap(),
104 Sysclk::HSI => hsi.unwrap(), 178 Sysclk::HSI => hsi.unwrap(),
105 Sysclk::_RESERVED_1 => unreachable!(), 179 Sysclk::_RESERVED_1 => unreachable!(),
106 Sysclk::PLL1_R => todo!(), 180 Sysclk::PLL1_R => pll1.r.unwrap(),
107 }; 181 };
108 182
109 assert!(sys_clk.0 <= 100_000_000); 183 assert!(sys_clk.0 <= 100_000_000);
@@ -111,7 +185,6 @@ pub(crate) unsafe fn init(config: Config) {
111 let hclk1 = sys_clk / config.ahb_pre; 185 let hclk1 = sys_clk / config.ahb_pre;
112 let hclk2 = hclk1; 186 let hclk2 = hclk1;
113 let hclk4 = hclk1; 187 let hclk4 = hclk1;
114 // TODO: hclk5
115 let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre); 188 let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
116 let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre); 189 let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
117 let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre); 190 let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre);
@@ -157,6 +230,54 @@ pub(crate) unsafe fn init(config: Config) {
157 w.set_ppre2(config.apb2_pre); 230 w.set_ppre2(config.apb2_pre);
158 }); 231 });
159 232
233 // Set AHB5 prescaler depending on sysclk source
234 RCC.cfgr4().modify(|w| match config.sys {
235 // When using HSI or HSE, use HDIV5 bit (0 = div1, 1 = div2)
236 Sysclk::HSI | Sysclk::HSE => {
237 // Only Div1 and Div2 are valid for HDIV5, enforce this
238 match config.ahb5_pre {
239 AHB5Prescaler::DIV1 => w.set_hdiv5(Hdiv5::DIV1),
240 AHB5Prescaler::DIV2 => w.set_hdiv5(Hdiv5::DIV2),
241 _ => panic!("Invalid ahb5_pre for HSI/HSE sysclk: only DIV1 and DIV2 are allowed"),
242 };
243 }
244 // When using PLL1, use HPRE5 bits [2:0]
245 Sysclk::PLL1_R => {
246 w.set_hpre5(config.ahb5_pre);
247 }
248 _ => {}
249 });
250
251 let hclk5 = sys_clk / config.ahb5_pre;
252
253
254 #[cfg(all(stm32wba, peri_usb_otg_hs))]
255 let usb_refck = match config.mux.otghssel {
256 Otghssel::HSE => hse,
257 Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),
258 Otghssel::PLL1_P => pll1.p,
259 Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8),
260 };
261 #[cfg(all(stm32wba, peri_usb_otg_hs))]
262 let usb_refck_sel = match usb_refck {
263 Some(clk_val) => match clk_val {
264 Hertz(16_000_000) => Usbrefcksel::MHZ16,
265 Hertz(19_200_000) => Usbrefcksel::MHZ19_2,
266 Hertz(20_000_000) => Usbrefcksel::MHZ20,
267 Hertz(24_000_000) => Usbrefcksel::MHZ24,
268 Hertz(26_000_000) => Usbrefcksel::MHZ26,
269 Hertz(32_000_000) => Usbrefcksel::MHZ32,
270 _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val),
271 },
272 None => Usbrefcksel::MHZ24,
273 };
274 #[cfg(all(stm32wba, peri_usb_otg_hs))]
275 SYSCFG.otghsphycr().modify(|w| {
276 w.set_clksel(usb_refck_sel);
277 });
278
279 let lsi = config.lsi.lsi.then_some(LSI_FREQ);
280
160 config.mux.init(); 281 config.mux.init();
161 282
162 set_clocks!( 283 set_clocks!(
@@ -164,6 +285,7 @@ pub(crate) unsafe fn init(config: Config) {
164 hclk1: Some(hclk1), 285 hclk1: Some(hclk1),
165 hclk2: Some(hclk2), 286 hclk2: Some(hclk2),
166 hclk4: Some(hclk4), 287 hclk4: Some(hclk4),
288 hclk5: Some(hclk5),
167 pclk1: Some(pclk1), 289 pclk1: Some(pclk1),
168 pclk2: Some(pclk2), 290 pclk2: Some(pclk2),
169 pclk7: Some(pclk7), 291 pclk7: Some(pclk7),
@@ -171,12 +293,117 @@ pub(crate) unsafe fn init(config: Config) {
171 pclk2_tim: Some(pclk2_tim), 293 pclk2_tim: Some(pclk2_tim),
172 rtc: rtc, 294 rtc: rtc,
173 hse: hse, 295 hse: hse,
296 lsi: lsi,
174 hsi: hsi, 297 hsi: hsi,
298 pll1_p: pll1.p,
299 pll1_q: pll1.q,
300 pll1_r: pll1.r,
175 301
176 // TODO 302 // TODO
177 lse: None, 303 lse: None,
178 lsi: None,
179 pll1_p: None,
180 pll1_q: None,
181 ); 304 );
182} 305}
306
307pub(super) struct PllInput {
308 pub hsi: Option<Hertz>,
309 pub hse: Option<Hertz>,
310}
311
312#[allow(unused)]
313#[derive(Default)]
314pub(super) struct PllOutput {
315 pub p: Option<Hertz>,
316 pub q: Option<Hertz>,
317 pub r: Option<Hertz>,
318}
319
320fn pll_enable(enabled: bool) {
321 RCC.cr().modify(|w| w.set_pllon(enabled));
322 while RCC.cr().read().pllrdy() != enabled {}
323}
324
325fn init_pll(config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput {
326 // Disable PLL
327 pll_enable(false);
328
329 let Some(pll) = config else { return PllOutput::default() };
330
331 let pre_src_freq = match pll.source {
332 PllSource::DISABLE => panic!("must not select PLL source as DISABLE"),
333 PllSource::HSE => unwrap!(input.hse),
334 PllSource::HSI => unwrap!(input.hsi),
335 PllSource::_RESERVED_1 => panic!("must not select RESERVED_1 source as DISABLE"),
336 };
337
338 // Only divide by the HSE prescaler when the PLL source is HSE
339 let src_freq = match pll.source {
340 PllSource::HSE => {
341 // read the prescaler bits and divide
342 let hsepre = RCC.cr().read().hsepre();
343 pre_src_freq / hsepre
344 }
345 _ => pre_src_freq,
346 };
347
348 // Calculate the reference clock, which is the source divided by m
349 let ref_freq = src_freq / pll.prediv;
350 // Check limits per RM0515 § 12.4.3
351 assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));
352
353 // Check PLL clocks per RM0515 § 12.4.5
354 let (vco_min, vco_max, out_max) = match voltage_range {
355 VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(100)),
356 VoltageScale::RANGE2 => panic!("PLL is unavailable in voltage range 2"),
357 };
358
359 // Calculate the PLL VCO clock
360 // let vco_freq = ref_freq * pll.mul;
361 // Calculate VCO frequency including fractional part: FVCO = Fref_ck × (N + FRAC/2^13)
362 let numerator = (ref_freq.0 as u64) * (((pll.mul as u64) + 1 << 13) + pll.frac.unwrap_or(0) as u64);
363 let vco_hz = (numerator >> 13) as u32;
364 let vco_freq = Hertz(vco_hz);
365 assert!(vco_freq >= vco_min && vco_freq <= vco_max);
366
367 // Calculate output clocks.
368 let p = pll.divp.map(|div| vco_freq / div);
369 let q = pll.divq.map(|div| vco_freq / div);
370 let r = pll.divr.map(|div| vco_freq / div);
371 for freq in [p, q, r] {
372 if let Some(freq) = freq {
373 assert!(freq <= out_max);
374 }
375 }
376
377 let divr = RCC.pll1divr();
378 divr.write(|w| {
379 w.set_plln(pll.mul);
380 w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1));
381 w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1));
382 w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1));
383 });
384 RCC.pll1fracr().write(|w| {w.set_pllfracn(pll.frac.unwrap_or(0));});
385
386 let input_range = match ref_freq.0 {
387 ..=8_000_000 => Pllrge::FREQ_4TO8MHZ,
388 _ => Pllrge::FREQ_8TO16MHZ,
389 };
390
391 macro_rules! write_fields {
392 ($w:ident) => {
393 $w.set_pllpen(pll.divp.is_some());
394 $w.set_pllqen(pll.divq.is_some());
395 $w.set_pllren(pll.divr.is_some());
396 $w.set_pllfracen(pll.frac.is_some());
397 $w.set_pllm(pll.prediv);
398 $w.set_pllsrc(pll.source);
399 $w.set_pllrge(input_range);
400 };
401 }
402
403 RCC.pll1cfgr().write(|w| {write_fields!(w);});
404
405 // Enable PLL
406 pll_enable(true);
407
408 PllOutput{ p, q, r }
409} \ No newline at end of file