diff options
| author | Kevin Lannen <[email protected]> | 2023-06-28 13:05:39 -0600 |
|---|---|---|
| committer | Kevin Lannen <[email protected]> | 2023-06-28 16:53:16 -0600 |
| commit | 5666c569033d59fc894230ed4161e6c686733b2d (patch) | |
| tree | 2f86425f8ead9b10872bf82a2ef71834e1ff2171 | |
| parent | 2eb7a67c7027c6768fa95031caf60bcd0eade1ad (diff) | |
STM32G4: Add CRS support to RCC
Create working CRS USB Example
| -rw-r--r-- | embassy-stm32/src/rcc/g4.rs | 77 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/usb_serial.rs | 28 |
2 files changed, 95 insertions, 10 deletions
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 9401af4c3..ff8f97541 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs | |||
| @@ -3,6 +3,7 @@ use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; | |||
| 3 | use stm32_metapac::FLASH; | 3 | use stm32_metapac::FLASH; |
| 4 | 4 | ||
| 5 | use crate::pac::{PWR, RCC}; | 5 | use crate::pac::{PWR, RCC}; |
| 6 | use crate::rcc::sealed::RccPeripheral; | ||
| 6 | use crate::rcc::{set_freqs, Clocks}; | 7 | use crate::rcc::{set_freqs, Clocks}; |
| 7 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 8 | 9 | ||
| @@ -316,6 +317,27 @@ impl Into<Hpre> for AHBPrescaler { | |||
| 316 | } | 317 | } |
| 317 | } | 318 | } |
| 318 | 319 | ||
| 320 | /// Sets the source for the 48MHz clock to the USB and RNG peripherals. | ||
| 321 | pub enum Clock48MhzSrc { | ||
| 322 | /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the | ||
| 323 | /// oscillator to comply with the USB specification for oscillator tolerance. | ||
| 324 | Hsi48(Option<CrsConfig>), | ||
| 325 | /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the | ||
| 326 | /// PLL needs to be using the HSE source to comply with the USB specification for oscillator | ||
| 327 | /// tolerance. | ||
| 328 | PllQ, | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Sets the sync source for the Clock Recovery System (CRS). | ||
| 332 | pub enum CrsSyncSource { | ||
| 333 | /// Use an external GPIO to sync the CRS. | ||
| 334 | Gpio, | ||
| 335 | /// Use the Low Speed External oscillator to sync the CRS. | ||
| 336 | Lse, | ||
| 337 | /// Use the USB SOF to sync the CRS. | ||
| 338 | Usb, | ||
| 339 | } | ||
| 340 | |||
| 319 | /// Clocks configutation | 341 | /// Clocks configutation |
| 320 | pub struct Config { | 342 | pub struct Config { |
| 321 | pub mux: ClockSrc, | 343 | pub mux: ClockSrc, |
| @@ -326,6 +348,14 @@ pub struct Config { | |||
| 326 | /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration | 348 | /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration |
| 327 | /// MUST turn on the PLLR output. | 349 | /// MUST turn on the PLLR output. |
| 328 | pub pll: Option<Pll>, | 350 | pub pll: Option<Pll>, |
| 351 | /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. | ||
| 352 | pub clock_48mhz_src: Option<Clock48MhzSrc>, | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator. | ||
| 356 | pub struct CrsConfig { | ||
| 357 | /// Sync source for the CRS. | ||
| 358 | pub sync_src: CrsSyncSource, | ||
| 329 | } | 359 | } |
| 330 | 360 | ||
| 331 | impl Default for Config { | 361 | impl Default for Config { |
| @@ -338,6 +368,7 @@ impl Default for Config { | |||
| 338 | apb2_pre: APBPrescaler::NotDivided, | 368 | apb2_pre: APBPrescaler::NotDivided, |
| 339 | low_power_run: false, | 369 | low_power_run: false, |
| 340 | pll: None, | 370 | pll: None, |
| 371 | clock_48mhz_src: None, | ||
| 341 | } | 372 | } |
| 342 | } | 373 | } |
| 343 | } | 374 | } |
| @@ -430,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 430 | assert!(pll_freq.is_some()); | 461 | assert!(pll_freq.is_some()); |
| 431 | assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); | 462 | assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); |
| 432 | 463 | ||
| 433 | let freq = pll_freq.unwrap().pll_r.unwrap().0; | 464 | let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0; |
| 434 | 465 | ||
| 435 | assert!(freq <= 170_000_000); | 466 | assert!(freq <= 170_000_000); |
| 436 | 467 | ||
| @@ -497,6 +528,50 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 497 | } | 528 | } |
| 498 | }; | 529 | }; |
| 499 | 530 | ||
| 531 | // Setup the 48 MHz clock if needed | ||
| 532 | if let Some(clock_48mhz_src) = config.clock_48mhz_src { | ||
| 533 | let source = match clock_48mhz_src { | ||
| 534 | Clock48MhzSrc::PllQ => { | ||
| 535 | // Make sure the PLLQ is enabled and running at 48Mhz | ||
| 536 | let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q); | ||
| 537 | assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000); | ||
| 538 | |||
| 539 | crate::pac::rcc::vals::Clk48sel::PLLQCLK | ||
| 540 | } | ||
| 541 | Clock48MhzSrc::Hsi48(crs_config) => { | ||
| 542 | // Enable HSI48 | ||
| 543 | RCC.crrcr().modify(|w| w.set_hsi48on(true)); | ||
| 544 | // Wait for HSI48 to turn on | ||
| 545 | while RCC.crrcr().read().hsi48rdy() == false {} | ||
| 546 | |||
| 547 | // Enable and setup CRS if needed | ||
| 548 | if let Some(crs_config) = crs_config { | ||
| 549 | crate::peripherals::CRS::enable(); | ||
| 550 | |||
| 551 | let sync_src = match crs_config.sync_src { | ||
| 552 | CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO, | ||
| 553 | CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE, | ||
| 554 | CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB, | ||
| 555 | }; | ||
| 556 | |||
| 557 | crate::pac::CRS.cfgr().modify(|w| { | ||
| 558 | w.set_syncsrc(sync_src); | ||
| 559 | }); | ||
| 560 | |||
| 561 | // These are the correct settings for standard USB operation. If other settings | ||
| 562 | // are needed there will need to be additional config options for the CRS. | ||
| 563 | crate::pac::CRS.cr().modify(|w| { | ||
| 564 | w.set_autotrimen(true); | ||
| 565 | w.set_cen(true); | ||
| 566 | }); | ||
| 567 | } | ||
| 568 | crate::pac::rcc::vals::Clk48sel::HSI48 | ||
| 569 | } | ||
| 570 | }; | ||
| 571 | |||
| 572 | RCC.ccipr().modify(|w| w.set_clk48sel(source)); | ||
| 573 | } | ||
| 574 | |||
| 500 | if config.low_power_run { | 575 | if config.low_power_run { |
| 501 | assert!(sys_clk <= 2_000_000); | 576 | assert!(sys_clk <= 2_000_000); |
| 502 | PWR.cr1().modify(|w| w.set_lpr(true)); | 577 | PWR.cr1().modify(|w| w.set_lpr(true)); |
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 289d0ed86..77cfa67d3 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs | |||
| @@ -4,10 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::{panic, *}; | 5 | use defmt::{panic, *}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllQ, PllR, PllSrc}; | 7 | use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc}; |
| 8 | use embassy_stm32::time::Hertz; | 8 | use embassy_stm32::time::Hertz; |
| 9 | use embassy_stm32::usb::{self, Driver, Instance}; | 9 | use embassy_stm32::usb::{self, Driver, Instance}; |
| 10 | use embassy_stm32::{bind_interrupts, pac, peripherals, Config}; | 10 | use embassy_stm32::{bind_interrupts, peripherals, Config}; |
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; |
| 12 | use embassy_usb::driver::EndpointError; | 12 | use embassy_usb::driver::EndpointError; |
| 13 | use embassy_usb::Builder; | 13 | use embassy_usb::Builder; |
| @@ -22,25 +22,35 @@ bind_interrupts!(struct Irqs { | |||
| 22 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 23 | let mut config = Config::default(); | 23 | let mut config = Config::default(); |
| 24 | 24 | ||
| 25 | // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE. | ||
| 26 | const USE_HSI48: bool = true; | ||
| 27 | |||
| 28 | let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) }; | ||
| 29 | |||
| 25 | config.rcc.pll = Some(Pll { | 30 | config.rcc.pll = Some(Pll { |
| 26 | source: PllSrc::HSE(Hertz(8000000)), | 31 | source: PllSrc::HSE(Hertz(8_000_000)), |
| 27 | prediv_m: PllM::Div2, | 32 | prediv_m: PllM::Div2, |
| 28 | mul_n: PllN::Mul72, | 33 | mul_n: PllN::Mul72, |
| 29 | div_p: None, | 34 | div_p: None, |
| 30 | // USB and CAN at 48 MHz | 35 | div_q: pllq_div, |
| 31 | div_q: Some(PllQ::Div6), | ||
| 32 | // Main system clock at 144 MHz | 36 | // Main system clock at 144 MHz |
| 33 | div_r: Some(PllR::Div2), | 37 | div_r: Some(PllR::Div2), |
| 34 | }); | 38 | }); |
| 35 | 39 | ||
| 36 | config.rcc.mux = ClockSrc::PLL; | 40 | config.rcc.mux = ClockSrc::PLL; |
| 37 | 41 | ||
| 42 | if USE_HSI48 { | ||
| 43 | // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. | ||
| 44 | config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig { | ||
| 45 | sync_src: CrsSyncSource::Usb, | ||
| 46 | }))); | ||
| 47 | } else { | ||
| 48 | config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); | ||
| 49 | } | ||
| 50 | |||
| 38 | let p = embassy_stm32::init(config); | 51 | let p = embassy_stm32::init(config); |
| 39 | info!("Hello World!"); | ||
| 40 | 52 | ||
| 41 | pac::RCC.ccipr().write(|w| { | 53 | info!("Hello World!"); |
| 42 | w.set_clk48sel(pac::rcc::vals::Clk48sel::PLLQCLK); | ||
| 43 | }); | ||
| 44 | 54 | ||
| 45 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); | 55 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); |
| 46 | 56 | ||
