diff options
| author | Dario Nieuwenhuis <[email protected]> | 2025-07-27 21:39:11 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-27 21:39:11 +0000 |
| commit | 4e4a1c0054fe1245426798166b3bf51e8a0300d3 (patch) | |
| tree | 17bcbbc7a33e7f65a81b062609b353d9974567ef | |
| parent | c708cefe03363135c466a3c0e8543a95973bce7a (diff) | |
| parent | 05f1c75f8b01e36e641dca35b6d6f763b6babde5 (diff) | |
Merge pull request #4463 from leftger/feat/stm32wba-rcc-pll-support
Added support for PLL as a clock source on STM32WBA
| -rw-r--r-- | embassy-stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-stm32/build.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/wba.rs | 223 | ||||
| -rw-r--r-- | embassy-stm32/src/usb/otg.rs | 2 | ||||
| -rw-r--r-- | examples/stm32wba/src/bin/pwm.rs | 65 |
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 } | |||
| 81 | sdio-host = "0.9.0" | 81 | sdio-host = "0.9.0" |
| 82 | critical-section = "1.1" | 82 | critical-section = "1.1" |
| 83 | #stm32-metapac = { version = "16" } | 83 | #stm32-metapac = { version = "16" } |
| 84 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3" } | 84 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-df3f5212f2dd70955a6b3d0137e7b4457c6047bf" } |
| 85 | 85 | ||
| 86 | vcell = "0.1.3" | 86 | vcell = "0.1.3" |
| 87 | nb = "1.0.0" | 87 | nb = "1.0.0" |
| @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" | |||
| 110 | quote = "1.0.15" | 110 | quote = "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"]} |
| 113 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3", default-features = false, features = ["metadata"] } | 113 | stm32-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] |
| 116 | default = ["rt"] | 116 | default = ["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 @@ | |||
| 1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 2 | use crate::pac::rcc::regs::Cfgr1; | 2 | use crate::pac::rcc::regs::Cfgr1; |
| 3 | pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsepre as HsePrescaler, Ppre as APBPrescaler, Sw as Sysclk}; | 3 | #[cfg(all(peri_usb_otg_hs))] |
| 4 | pub use crate::pac::rcc::vals::Otghssel; | ||
| 5 | use crate::pac::rcc::vals::Pllrge; | ||
| 6 | pub 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))] | ||
| 11 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 4 | use crate::pac::{FLASH, RCC}; | 12 | use crate::pac::{FLASH, RCC}; |
| 13 | use crate::rcc::LSI_FREQ; | ||
| 5 | use crate::time::Hertz; | 14 | use 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)] | ||
| 27 | pub 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)] |
| 19 | pub struct Config { | 61 | pub 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 { | |||
| 40 | impl Config { | 86 | impl 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 | |||
| 285 | pub(super) struct PllInput { | ||
| 286 | pub hsi: Option<Hertz>, | ||
| 287 | pub hse: Option<Hertz>, | ||
| 288 | } | ||
| 289 | |||
| 290 | #[allow(unused)] | ||
| 291 | #[derive(Default)] | ||
| 292 | pub(super) struct PllOutput { | ||
| 293 | pub p: Option<Hertz>, | ||
| 294 | pub q: Option<Hertz>, | ||
| 295 | pub r: Option<Hertz>, | ||
| 296 | } | ||
| 297 | |||
| 298 | fn pll_enable(enabled: bool) { | ||
| 299 | RCC.cr().modify(|w| w.set_pllon(enabled)); | ||
| 300 | while RCC.cr().read().pllrdy() != enabled {} | ||
| 301 | } | ||
| 302 | |||
| 303 | fn 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 | |||
| 4 | use defmt::*; | ||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::OutputType; | ||
| 8 | use embassy_stm32::rcc::{ | ||
| 9 | AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, | ||
| 10 | }; | ||
| 11 | use embassy_stm32::time::khz; | ||
| 12 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | ||
| 13 | use embassy_stm32::Config; | ||
| 14 | use embassy_time::Timer; | ||
| 15 | use panic_probe as _; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async 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 | } | ||
