aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/build.rs2
-rw-r--r--embassy-stm32/src/rcc/wba.rs223
-rw-r--r--embassy-stm32/src/usb/otg.rs2
-rw-r--r--examples/stm32wba/src/bin/pwm.rs65
5 files changed, 285 insertions, 11 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/build.rs b/embassy-stm32/build.rs
index 753f94fa6..deefb13c1 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1599,7 +1599,7 @@ fn main() {
1599 for e in rcc_registers.ir.enums { 1599 for e in rcc_registers.ir.enums {
1600 fn is_rcc_name(e: &str) -> bool { 1600 fn is_rcc_name(e: &str) -> bool {
1601 match e { 1601 match e {
1602 "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" => true, 1602 "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true,
1603 "Timpre" | "Pllrclkpre" => false, 1603 "Timpre" | "Pllrclkpre" => false,
1604 e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, 1604 e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true,
1605 _ => false, 1605 _ => false,
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs
index b494997b3..56ba7b58b 100644
--- a/embassy-stm32/src/rcc/wba.rs
+++ b/embassy-stm32/src/rcc/wba.rs
@@ -1,7 +1,16 @@
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}; 3#[cfg(all(peri_usb_otg_hs))]
4pub use crate::pac::rcc::vals::Otghssel;
5use crate::pac::rcc::vals::Pllrge;
6pub use crate::pac::rcc::vals::{
7 Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv,
8 Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
9};
10#[cfg(all(peri_usb_otg_hs))]
11pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG};
4use crate::pac::{FLASH, RCC}; 12use crate::pac::{FLASH, RCC};
13use crate::rcc::LSI_FREQ;
5use crate::time::Hertz; 14use crate::time::Hertz;
6 15
7/// HSI speed 16/// HSI speed
@@ -14,6 +23,39 @@ pub struct Hse {
14 pub prescaler: HsePrescaler, 23 pub prescaler: HsePrescaler,
15} 24}
16 25
26#[derive(Clone, Copy)]
27pub struct Pll {
28 /// The clock source for the PLL.
29 pub source: PllSource,
30 /// The PLL pre-divider.
31 ///
32 /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
33 pub prediv: PllPreDiv,
34 /// The PLL multiplier.
35 ///
36 /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544
37 /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
38 pub mul: PllMul,
39 /// The divider for the P output.
40 ///
41 /// The P output is one of several options
42 /// that can be used to feed the SAI/MDF/ADF Clock mux's.
43 pub divp: Option<PllDiv>,
44 /// The divider for the Q output.
45 ///
46 /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks
47 /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's.
48 pub divq: Option<PllDiv>,
49 /// The divider for the R output.
50 ///
51 /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
52 /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
53 /// `Config { voltage_range }`.
54 pub divr: Option<PllDiv>,
55
56 pub frac: Option<u16>,
57}
58
17/// Clocks configuration 59/// Clocks configuration
18#[derive(Clone, Copy)] 60#[derive(Clone, Copy)]
19pub struct Config { 61pub struct Config {
@@ -21,9 +63,13 @@ pub struct Config {
21 pub hsi: bool, 63 pub hsi: bool,
22 pub hse: Option<Hse>, 64 pub hse: Option<Hse>,
23 65
66 // pll
67 pub pll1: Option<Pll>,
68
24 // sysclk, buses. 69 // sysclk, buses.
25 pub sys: Sysclk, 70 pub sys: Sysclk,
26 pub ahb_pre: AHBPrescaler, 71 pub ahb_pre: AHBPrescaler,
72 pub ahb5_pre: AHB5Prescaler,
27 pub apb1_pre: APBPrescaler, 73 pub apb1_pre: APBPrescaler,
28 pub apb2_pre: APBPrescaler, 74 pub apb2_pre: APBPrescaler,
29 pub apb7_pre: APBPrescaler, 75 pub apb7_pre: APBPrescaler,
@@ -40,14 +86,17 @@ pub struct Config {
40impl Config { 86impl Config {
41 pub const fn new() -> Self { 87 pub const fn new() -> Self {
42 Config { 88 Config {
43 hse: None,
44 hsi: true, 89 hsi: true,
90 hse: None,
91 pll1: None,
45 sys: Sysclk::HSI, 92 sys: Sysclk::HSI,
46 ahb_pre: AHBPrescaler::DIV1, 93 ahb_pre: AHBPrescaler::DIV1,
94 ahb5_pre: AHB5Prescaler::DIV1,
47 apb1_pre: APBPrescaler::DIV1, 95 apb1_pre: APBPrescaler::DIV1,
48 apb2_pre: APBPrescaler::DIV1, 96 apb2_pre: APBPrescaler::DIV1,
49 apb7_pre: APBPrescaler::DIV1, 97 apb7_pre: APBPrescaler::DIV1,
50 ls: crate::rcc::LsConfig::new(), 98 ls: crate::rcc::LsConfig::new(),
99 // lsi2: crate::rcc::LsConfig::new(),
51 voltage_scale: VoltageScale::RANGE2, 100 voltage_scale: VoltageScale::RANGE2,
52 mux: super::mux::ClockMux::default(), 101 mux: super::mux::ClockMux::default(),
53 } 102 }
@@ -99,11 +148,15 @@ pub(crate) unsafe fn init(config: Config) {
99 HSE_FREQ 148 HSE_FREQ
100 }); 149 });
101 150
151 let pll_input = PllInput { hse, hsi };
152
153 let pll1 = init_pll(config.pll1, &pll_input, config.voltage_scale);
154
102 let sys_clk = match config.sys { 155 let sys_clk = match config.sys {
103 Sysclk::HSE => hse.unwrap(), 156 Sysclk::HSE => hse.unwrap(),
104 Sysclk::HSI => hsi.unwrap(), 157 Sysclk::HSI => hsi.unwrap(),
105 Sysclk::_RESERVED_1 => unreachable!(), 158 Sysclk::_RESERVED_1 => unreachable!(),
106 Sysclk::PLL1_R => todo!(), 159 Sysclk::PLL1_R => pll1.r.unwrap(),
107 }; 160 };
108 161
109 assert!(sys_clk.0 <= 100_000_000); 162 assert!(sys_clk.0 <= 100_000_000);
@@ -111,7 +164,6 @@ pub(crate) unsafe fn init(config: Config) {
111 let hclk1 = sys_clk / config.ahb_pre; 164 let hclk1 = sys_clk / config.ahb_pre;
112 let hclk2 = hclk1; 165 let hclk2 = hclk1;
113 let hclk4 = hclk1; 166 let hclk4 = hclk1;
114 // TODO: hclk5
115 let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre); 167 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); 168 let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
117 let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre); 169 let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre);
@@ -157,6 +209,53 @@ pub(crate) unsafe fn init(config: Config) {
157 w.set_ppre2(config.apb2_pre); 209 w.set_ppre2(config.apb2_pre);
158 }); 210 });
159 211
212 // Set AHB5 prescaler depending on sysclk source
213 RCC.cfgr4().modify(|w| match config.sys {
214 // When using HSI or HSE, use HDIV5 bit (0 = div1, 1 = div2)
215 Sysclk::HSI | Sysclk::HSE => {
216 // Only Div1 and Div2 are valid for HDIV5, enforce this
217 match config.ahb5_pre {
218 AHB5Prescaler::DIV1 => w.set_hdiv5(Hdiv5::DIV1),
219 AHB5Prescaler::DIV2 => w.set_hdiv5(Hdiv5::DIV2),
220 _ => panic!("Invalid ahb5_pre for HSI/HSE sysclk: only DIV1 and DIV2 are allowed"),
221 };
222 }
223 // When using PLL1, use HPRE5 bits [2:0]
224 Sysclk::PLL1_R => {
225 w.set_hpre5(config.ahb5_pre);
226 }
227 _ => {}
228 });
229
230 let hclk5 = sys_clk / config.ahb5_pre;
231
232 #[cfg(all(stm32wba, peri_usb_otg_hs))]
233 let usb_refck = match config.mux.otghssel {
234 Otghssel::HSE => hse,
235 Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),
236 Otghssel::PLL1_P => pll1.p,
237 Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8),
238 };
239 #[cfg(all(stm32wba, peri_usb_otg_hs))]
240 let usb_refck_sel = match usb_refck {
241 Some(clk_val) => match clk_val {
242 Hertz(16_000_000) => Usbrefcksel::MHZ16,
243 Hertz(19_200_000) => Usbrefcksel::MHZ19_2,
244 Hertz(20_000_000) => Usbrefcksel::MHZ20,
245 Hertz(24_000_000) => Usbrefcksel::MHZ24,
246 Hertz(26_000_000) => Usbrefcksel::MHZ26,
247 Hertz(32_000_000) => Usbrefcksel::MHZ32,
248 _ => 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),
249 },
250 None => Usbrefcksel::MHZ24,
251 };
252 #[cfg(all(stm32wba, peri_usb_otg_hs))]
253 SYSCFG.otghsphycr().modify(|w| {
254 w.set_clksel(usb_refck_sel);
255 });
256
257 let lsi = config.ls.lsi.then_some(LSI_FREQ);
258
160 config.mux.init(); 259 config.mux.init();
161 260
162 set_clocks!( 261 set_clocks!(
@@ -164,6 +263,7 @@ pub(crate) unsafe fn init(config: Config) {
164 hclk1: Some(hclk1), 263 hclk1: Some(hclk1),
165 hclk2: Some(hclk2), 264 hclk2: Some(hclk2),
166 hclk4: Some(hclk4), 265 hclk4: Some(hclk4),
266 hclk5: Some(hclk5),
167 pclk1: Some(pclk1), 267 pclk1: Some(pclk1),
168 pclk2: Some(pclk2), 268 pclk2: Some(pclk2),
169 pclk7: Some(pclk7), 269 pclk7: Some(pclk7),
@@ -171,12 +271,121 @@ pub(crate) unsafe fn init(config: Config) {
171 pclk2_tim: Some(pclk2_tim), 271 pclk2_tim: Some(pclk2_tim),
172 rtc: rtc, 272 rtc: rtc,
173 hse: hse, 273 hse: hse,
274 lsi: lsi,
174 hsi: hsi, 275 hsi: hsi,
276 pll1_p: pll1.p,
277 pll1_q: pll1.q,
278 pll1_r: pll1.r,
175 279
176 // TODO 280 // TODO
177 lse: None, 281 lse: None,
178 lsi: None,
179 pll1_p: None,
180 pll1_q: None,
181 ); 282 );
182} 283}
284
285pub(super) struct PllInput {
286 pub hsi: Option<Hertz>,
287 pub hse: Option<Hertz>,
288}
289
290#[allow(unused)]
291#[derive(Default)]
292pub(super) struct PllOutput {
293 pub p: Option<Hertz>,
294 pub q: Option<Hertz>,
295 pub r: Option<Hertz>,
296}
297
298fn pll_enable(enabled: bool) {
299 RCC.cr().modify(|w| w.set_pllon(enabled));
300 while RCC.cr().read().pllrdy() != enabled {}
301}
302
303fn init_pll(config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput {
304 // Disable PLL
305 pll_enable(false);
306
307 let Some(pll) = config else { return PllOutput::default() };
308
309 let pre_src_freq = match pll.source {
310 PllSource::DISABLE => panic!("must not select PLL source as DISABLE"),
311 PllSource::HSE => unwrap!(input.hse),
312 PllSource::HSI => unwrap!(input.hsi),
313 PllSource::_RESERVED_1 => panic!("must not select RESERVED_1 source as DISABLE"),
314 };
315
316 // Only divide by the HSE prescaler when the PLL source is HSE
317 let src_freq = match pll.source {
318 PllSource::HSE => {
319 // read the prescaler bits and divide
320 let hsepre = RCC.cr().read().hsepre();
321 pre_src_freq / hsepre
322 }
323 _ => pre_src_freq,
324 };
325
326 // Calculate the reference clock, which is the source divided by m
327 let ref_freq = src_freq / pll.prediv;
328 // Check limits per RM0515 § 12.4.3
329 assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));
330
331 // Check PLL clocks per RM0515 § 12.4.5
332 let (vco_min, vco_max, out_max) = match voltage_range {
333 VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(100)),
334 VoltageScale::RANGE2 => panic!("PLL is unavailable in voltage range 2"),
335 };
336
337 // Calculate the PLL VCO clock
338 // let vco_freq = ref_freq * pll.mul;
339 // Calculate VCO frequency including fractional part: FVCO = Fref_ck × (N + FRAC/2^13)
340 let numerator = (ref_freq.0 as u64) * (((pll.mul as u64) + 1 << 13) + pll.frac.unwrap_or(0) as u64);
341 let vco_hz = (numerator >> 13) as u32;
342 let vco_freq = Hertz(vco_hz);
343 assert!(vco_freq >= vco_min && vco_freq <= vco_max);
344
345 // Calculate output clocks.
346 let p = pll.divp.map(|div| vco_freq / div);
347 let q = pll.divq.map(|div| vco_freq / div);
348 let r = pll.divr.map(|div| vco_freq / div);
349 for freq in [p, q, r] {
350 if let Some(freq) = freq {
351 assert!(freq <= out_max);
352 }
353 }
354
355 let divr = RCC.pll1divr();
356 divr.write(|w| {
357 w.set_plln(pll.mul);
358 w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1));
359 w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1));
360 w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1));
361 });
362 RCC.pll1fracr().write(|w| {
363 w.set_pllfracn(pll.frac.unwrap_or(0));
364 });
365
366 let input_range = match ref_freq.0 {
367 ..=8_000_000 => Pllrge::FREQ_4TO8MHZ,
368 _ => Pllrge::FREQ_8TO16MHZ,
369 };
370
371 macro_rules! write_fields {
372 ($w:ident) => {
373 $w.set_pllpen(pll.divp.is_some());
374 $w.set_pllqen(pll.divq.is_some());
375 $w.set_pllren(pll.divr.is_some());
376 $w.set_pllfracen(pll.frac.is_some());
377 $w.set_pllm(pll.prediv);
378 $w.set_pllsrc(pll.source);
379 $w.set_pllrge(input_range);
380 };
381 }
382
383 RCC.pll1cfgr().write(|w| {
384 write_fields!(w);
385 });
386
387 // Enable PLL
388 pll_enable(true);
389
390 PllOutput { p, q, r }
391}
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs
index b074cfa1b..81e6bff4c 100644
--- a/embassy-stm32/src/usb/otg.rs
+++ b/embassy-stm32/src/usb/otg.rs
@@ -336,7 +336,7 @@ impl<'d, T: Instance> Bus<'d, T> {
336 critical_section::with(|_| { 336 critical_section::with(|_| {
337 crate::pac::RCC.ahb2enr().modify(|w| { 337 crate::pac::RCC.ahb2enr().modify(|w| {
338 w.set_usb_otg_hsen(true); 338 w.set_usb_otg_hsen(true);
339 w.set_otghsphyen(true); 339 w.set_usb_otg_hs_phyen(true);
340 }); 340 });
341 }); 341 });
342 } 342 }
diff --git a/examples/stm32wba/src/bin/pwm.rs b/examples/stm32wba/src/bin/pwm.rs
new file mode 100644
index 000000000..de690fda0
--- /dev/null
+++ b/examples/stm32wba/src/bin/pwm.rs
@@ -0,0 +1,65 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use defmt_rtt as _; // global logger
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::OutputType;
8use embassy_stm32::rcc::{
9 AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
10};
11use embassy_stm32::time::khz;
12use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
13use embassy_stm32::Config;
14use embassy_time::Timer;
15use panic_probe as _;
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 info!("Hello World!");
20
21 let mut config = Config::default();
22 // Fine-tune PLL1 dividers/multipliers
23 config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {
24 source: PllSource::HSI,
25 prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
26 mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
27 divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
28 // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)
29 divq: None,
30 divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)
31 frac: Some(0), // Fractional part (enabled)
32 });
33
34 config.rcc.ahb_pre = AHBPrescaler::DIV1;
35 config.rcc.apb1_pre = APBPrescaler::DIV1;
36 config.rcc.apb2_pre = APBPrescaler::DIV1;
37 config.rcc.apb7_pre = APBPrescaler::DIV1;
38 config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
39
40 // voltage scale for max performance
41 config.rcc.voltage_scale = VoltageScale::RANGE1;
42 // route PLL1_P into the USB‐OTG‐HS block
43 config.rcc.sys = Sysclk::PLL1_R;
44
45 let p = embassy_stm32::init(config);
46
47 let ch1_pin = PwmPin::new(p.PB8, OutputType::PushPull);
48 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());
49 let mut ch1 = pwm.ch1();
50 ch1.enable();
51
52 info!("PWM initialized");
53 info!("PWM max duty {}", ch1.max_duty_cycle());
54
55 loop {
56 ch1.set_duty_cycle_fully_off();
57 Timer::after_millis(300).await;
58 ch1.set_duty_cycle_fraction(1, 4);
59 Timer::after_millis(300).await;
60 ch1.set_duty_cycle_fraction(1, 2);
61 Timer::after_millis(300).await;
62 ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
63 Timer::after_millis(300).await;
64 }
65}