STM32G4: Add CRS support to RCC
Create working CRS USB Example
This commit is contained in:
parent
2eb7a67c70
commit
5666c56903
@ -3,6 +3,7 @@ use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
|
|||||||
use stm32_metapac::FLASH;
|
use stm32_metapac::FLASH;
|
||||||
|
|
||||||
use crate::pac::{PWR, RCC};
|
use crate::pac::{PWR, RCC};
|
||||||
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
@ -316,6 +317,27 @@ impl Into<Hpre> for AHBPrescaler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
|
||||||
|
pub enum Clock48MhzSrc {
|
||||||
|
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
|
||||||
|
/// oscillator to comply with the USB specification for oscillator tolerance.
|
||||||
|
Hsi48(Option<CrsConfig>),
|
||||||
|
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
|
||||||
|
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator
|
||||||
|
/// tolerance.
|
||||||
|
PllQ,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the sync source for the Clock Recovery System (CRS).
|
||||||
|
pub enum CrsSyncSource {
|
||||||
|
/// Use an external GPIO to sync the CRS.
|
||||||
|
Gpio,
|
||||||
|
/// Use the Low Speed External oscillator to sync the CRS.
|
||||||
|
Lse,
|
||||||
|
/// Use the USB SOF to sync the CRS.
|
||||||
|
Usb,
|
||||||
|
}
|
||||||
|
|
||||||
/// Clocks configutation
|
/// Clocks configutation
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub mux: ClockSrc,
|
pub mux: ClockSrc,
|
||||||
@ -326,6 +348,14 @@ pub struct Config {
|
|||||||
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
|
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
|
||||||
/// MUST turn on the PLLR output.
|
/// MUST turn on the PLLR output.
|
||||||
pub pll: Option<Pll>,
|
pub pll: Option<Pll>,
|
||||||
|
/// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
|
||||||
|
pub clock_48mhz_src: Option<Clock48MhzSrc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
|
||||||
|
pub struct CrsConfig {
|
||||||
|
/// Sync source for the CRS.
|
||||||
|
pub sync_src: CrsSyncSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -338,6 +368,7 @@ impl Default for Config {
|
|||||||
apb2_pre: APBPrescaler::NotDivided,
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
low_power_run: false,
|
low_power_run: false,
|
||||||
pll: None,
|
pll: None,
|
||||||
|
clock_48mhz_src: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
assert!(pll_freq.is_some());
|
assert!(pll_freq.is_some());
|
||||||
assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
|
assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
|
||||||
|
|
||||||
let freq = pll_freq.unwrap().pll_r.unwrap().0;
|
let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0;
|
||||||
|
|
||||||
assert!(freq <= 170_000_000);
|
assert!(freq <= 170_000_000);
|
||||||
|
|
||||||
@ -497,6 +528,50 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Setup the 48 MHz clock if needed
|
||||||
|
if let Some(clock_48mhz_src) = config.clock_48mhz_src {
|
||||||
|
let source = match clock_48mhz_src {
|
||||||
|
Clock48MhzSrc::PllQ => {
|
||||||
|
// Make sure the PLLQ is enabled and running at 48Mhz
|
||||||
|
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
|
||||||
|
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
|
||||||
|
|
||||||
|
crate::pac::rcc::vals::Clk48sel::PLLQCLK
|
||||||
|
}
|
||||||
|
Clock48MhzSrc::Hsi48(crs_config) => {
|
||||||
|
// Enable HSI48
|
||||||
|
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
||||||
|
// Wait for HSI48 to turn on
|
||||||
|
while RCC.crrcr().read().hsi48rdy() == false {}
|
||||||
|
|
||||||
|
// Enable and setup CRS if needed
|
||||||
|
if let Some(crs_config) = crs_config {
|
||||||
|
crate::peripherals::CRS::enable();
|
||||||
|
|
||||||
|
let sync_src = match crs_config.sync_src {
|
||||||
|
CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
|
||||||
|
CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
|
||||||
|
CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::pac::CRS.cfgr().modify(|w| {
|
||||||
|
w.set_syncsrc(sync_src);
|
||||||
|
});
|
||||||
|
|
||||||
|
// These are the correct settings for standard USB operation. If other settings
|
||||||
|
// are needed there will need to be additional config options for the CRS.
|
||||||
|
crate::pac::CRS.cr().modify(|w| {
|
||||||
|
w.set_autotrimen(true);
|
||||||
|
w.set_cen(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
crate::pac::rcc::vals::Clk48sel::HSI48
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RCC.ccipr().modify(|w| w.set_clk48sel(source));
|
||||||
|
}
|
||||||
|
|
||||||
if config.low_power_run {
|
if config.low_power_run {
|
||||||
assert!(sys_clk <= 2_000_000);
|
assert!(sys_clk <= 2_000_000);
|
||||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
use defmt::{panic, *};
|
use defmt::{panic, *};
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllQ, PllR, PllSrc};
|
use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc};
|
||||||
use embassy_stm32::time::Hertz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::usb::{self, Driver, Instance};
|
use embassy_stm32::usb::{self, Driver, Instance};
|
||||||
use embassy_stm32::{bind_interrupts, pac, peripherals, Config};
|
use embassy_stm32::{bind_interrupts, peripherals, Config};
|
||||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
use embassy_usb::driver::EndpointError;
|
use embassy_usb::driver::EndpointError;
|
||||||
use embassy_usb::Builder;
|
use embassy_usb::Builder;
|
||||||
@ -22,25 +22,35 @@ bind_interrupts!(struct Irqs {
|
|||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
|
|
||||||
|
// Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
|
||||||
|
const USE_HSI48: bool = true;
|
||||||
|
|
||||||
|
let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) };
|
||||||
|
|
||||||
config.rcc.pll = Some(Pll {
|
config.rcc.pll = Some(Pll {
|
||||||
source: PllSrc::HSE(Hertz(8000000)),
|
source: PllSrc::HSE(Hertz(8_000_000)),
|
||||||
prediv_m: PllM::Div2,
|
prediv_m: PllM::Div2,
|
||||||
mul_n: PllN::Mul72,
|
mul_n: PllN::Mul72,
|
||||||
div_p: None,
|
div_p: None,
|
||||||
// USB and CAN at 48 MHz
|
div_q: pllq_div,
|
||||||
div_q: Some(PllQ::Div6),
|
|
||||||
// Main system clock at 144 MHz
|
// Main system clock at 144 MHz
|
||||||
div_r: Some(PllR::Div2),
|
div_r: Some(PllR::Div2),
|
||||||
});
|
});
|
||||||
|
|
||||||
config.rcc.mux = ClockSrc::PLL;
|
config.rcc.mux = ClockSrc::PLL;
|
||||||
|
|
||||||
let p = embassy_stm32::init(config);
|
if USE_HSI48 {
|
||||||
info!("Hello World!");
|
// Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
|
||||||
|
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig {
|
||||||
|
sync_src: CrsSyncSource::Usb,
|
||||||
|
})));
|
||||||
|
} else {
|
||||||
|
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ);
|
||||||
|
}
|
||||||
|
|
||||||
pac::RCC.ccipr().write(|w| {
|
let p = embassy_stm32::init(config);
|
||||||
w.set_clk48sel(pac::rcc::vals::Clk48sel::PLLQCLK);
|
|
||||||
});
|
info!("Hello World!");
|
||||||
|
|
||||||
let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
|
let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user