Merge pull request #2015 from willglynn/stm32u5_faster_clocks
stm32: u5: implement >55 MHz clock speeds
This commit is contained in:
commit
3bf8e4de5f
@ -1,7 +1,7 @@
|
|||||||
use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw};
|
use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllmboost, Pllrge, Pllsrc, Sw};
|
||||||
|
|
||||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::{FLASH, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
@ -15,23 +15,86 @@ pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
|||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
|
/// Use an internal medium speed oscillator (MSIS) as the system clock.
|
||||||
MSI(MSIRange),
|
MSI(MSIRange),
|
||||||
|
/// Use the external high speed clock as the system clock.
|
||||||
|
///
|
||||||
|
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
|
||||||
|
/// never exceed 50 MHz.
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
|
/// Use the 16 MHz internal high speed oscillator as the system clock.
|
||||||
HSI16,
|
HSI16,
|
||||||
PLL1R(PllSrc, PllM, PllN, PllClkDiv),
|
/// Use PLL1 as the system clock.
|
||||||
|
PLL1R(PllConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClockSrc {
|
||||||
|
fn default() -> Self {
|
||||||
|
// The default system clock source is MSIS @ 4 MHz, per RM0456 § 11.4.9
|
||||||
|
ClockSrc::MSI(MSIRange::Range4mhz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct PllConfig {
|
||||||
|
/// The clock source for the PLL.
|
||||||
|
pub source: PllSrc,
|
||||||
|
/// The PLL prescaler.
|
||||||
|
///
|
||||||
|
/// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
|
||||||
|
pub m: PllM,
|
||||||
|
/// The PLL multiplier.
|
||||||
|
///
|
||||||
|
/// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544
|
||||||
|
/// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
|
||||||
|
pub n: PllN,
|
||||||
|
/// The divider for the R output.
|
||||||
|
///
|
||||||
|
/// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
|
||||||
|
/// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
|
||||||
|
/// `Config { voltage_range }`.
|
||||||
|
pub r: PllClkDiv,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PllConfig {
|
||||||
|
/// A configuration for HSI16 / 1 * 10 / 1 = 160 MHz
|
||||||
|
pub const fn hsi16_160mhz() -> Self {
|
||||||
|
PllConfig {
|
||||||
|
source: PllSrc::HSI16,
|
||||||
|
m: PllM::NotDivided,
|
||||||
|
n: PllN::Mul10,
|
||||||
|
r: PllClkDiv::NotDivided,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A configuration for MSIS @ 48 MHz / 3 * 10 / 1 = 160 MHz
|
||||||
|
pub const fn msis_160mhz() -> Self {
|
||||||
|
PllConfig {
|
||||||
|
source: PllSrc::MSIS(MSIRange::Range48mhz),
|
||||||
|
m: PllM::Div3,
|
||||||
|
n: PllN::Mul10,
|
||||||
|
r: PllClkDiv::NotDivided,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum PllSrc {
|
pub enum PllSrc {
|
||||||
MSI(MSIRange),
|
/// Use an internal medium speed oscillator as the PLL source.
|
||||||
|
MSIS(MSIRange),
|
||||||
|
/// Use the external high speed clock as the system PLL source.
|
||||||
|
///
|
||||||
|
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
|
||||||
|
/// never exceed 50 MHz.
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
|
/// Use the 16 MHz internal high speed oscillator as the PLL source.
|
||||||
HSI16,
|
HSI16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Pllsrc> for PllSrc {
|
impl Into<Pllsrc> for PllSrc {
|
||||||
fn into(self) -> Pllsrc {
|
fn into(self) -> Pllsrc {
|
||||||
match self {
|
match self {
|
||||||
PllSrc::MSI(..) => Pllsrc::MSIS,
|
PllSrc::MSIS(..) => Pllsrc::MSIS,
|
||||||
PllSrc::HSE(..) => Pllsrc::HSE,
|
PllSrc::HSE(..) => Pllsrc::HSE,
|
||||||
PllSrc::HSI16 => Pllsrc::HSI16,
|
PllSrc::HSI16 => Pllsrc::HSI16,
|
||||||
}
|
}
|
||||||
@ -41,57 +104,45 @@ impl Into<Pllsrc> for PllSrc {
|
|||||||
seq_macro::seq!(N in 2..=128 {
|
seq_macro::seq!(N in 2..=128 {
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum PllClkDiv {
|
pub enum PllClkDiv {
|
||||||
NotDivided,
|
NotDivided = 1,
|
||||||
#(
|
#(
|
||||||
Div~N = (N-1),
|
Div~N = N,
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PllClkDiv {
|
impl PllClkDiv {
|
||||||
fn to_div(&self) -> u8 {
|
fn to_div(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
PllClkDiv::NotDivided => 1,
|
PllClkDiv::NotDivided => 0,
|
||||||
#(
|
#(
|
||||||
PllClkDiv::Div~N => N + 1,
|
PllClkDiv::Div~N => N - 1,
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
impl Into<u8> for PllClkDiv {
|
|
||||||
fn into(self) -> u8 {
|
|
||||||
(self as u8) + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
seq_macro::seq!(N in 4..=512 {
|
seq_macro::seq!(N in 4..=512 {
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum PllN {
|
pub enum PllN {
|
||||||
NotMultiplied,
|
NotMultiplied = 1,
|
||||||
#(
|
#(
|
||||||
Mul~N = N-1,
|
Mul~N = N,
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PllN {
|
impl PllN {
|
||||||
fn to_mul(&self) -> u16 {
|
fn to_mul(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
PllN::NotMultiplied => 1,
|
PllN::NotMultiplied => 0,
|
||||||
#(
|
#(
|
||||||
PllN::Mul~N => N + 1,
|
PllN::Mul~N => N - 1,
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
impl Into<u16> for PllN {
|
|
||||||
fn into(self) -> u16 {
|
|
||||||
(self as u16) + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-division
|
// Pre-division
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum PllM {
|
pub enum PllM {
|
||||||
@ -132,6 +183,7 @@ impl Into<Sw> for ClockSrc {
|
|||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum MSIRange {
|
pub enum MSIRange {
|
||||||
|
/// The 48 MHz MSI speed is unavailable in `VoltageScale::RANGE4`.
|
||||||
Range48mhz = 48_000_000,
|
Range48mhz = 48_000_000,
|
||||||
Range24mhz = 24_000_000,
|
Range24mhz = 24_000_000,
|
||||||
Range16mhz = 16_000_000,
|
Range16mhz = 16_000_000,
|
||||||
@ -179,12 +231,6 @@ impl Into<Msirange> for MSIRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MSIRange {
|
|
||||||
fn default() -> Self {
|
|
||||||
MSIRange::Range4mhz
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub mux: ClockSrc,
|
pub mux: ClockSrc,
|
||||||
@ -193,24 +239,57 @@ pub struct Config {
|
|||||||
pub apb2_pre: APBPrescaler,
|
pub apb2_pre: APBPrescaler,
|
||||||
pub apb3_pre: APBPrescaler,
|
pub apb3_pre: APBPrescaler,
|
||||||
pub hsi48: bool,
|
pub hsi48: bool,
|
||||||
|
/// The voltage range influences the maximum clock frequencies for different parts of the
|
||||||
|
/// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
|
||||||
|
/// exceeding 55 MHz require at least `RANGE2`.
|
||||||
|
///
|
||||||
|
/// See RM0456 § 10.5.4 for a general overview and § 11.4.10 for clock source frequency limits.
|
||||||
|
pub voltage_range: VoltageScale,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Config {
|
||||||
fn default() -> Self {
|
unsafe fn init_hsi16(&self) -> Hertz {
|
||||||
Self {
|
RCC.cr().write(|w| w.set_hsion(true));
|
||||||
mux: ClockSrc::MSI(MSIRange::default()),
|
while !RCC.cr().read().hsirdy() {}
|
||||||
ahb_pre: AHBPrescaler::DIV1,
|
|
||||||
apb1_pre: APBPrescaler::DIV1,
|
HSI_FREQ
|
||||||
apb2_pre: APBPrescaler::DIV1,
|
}
|
||||||
apb3_pre: APBPrescaler::DIV1,
|
|
||||||
hsi48: false,
|
unsafe fn init_hse(&self, frequency: Hertz) -> Hertz {
|
||||||
}
|
// Check frequency limits per RM456 § 11.4.10
|
||||||
}
|
match self.voltage_range {
|
||||||
}
|
VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {
|
||||||
|
assert!(frequency.0 <= 50_000_000);
|
||||||
|
}
|
||||||
|
VoltageScale::RANGE4 => {
|
||||||
|
assert!(frequency.0 <= 25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable HSE, and wait for it to stabilize
|
||||||
|
RCC.cr().write(|w| w.set_hseon(true));
|
||||||
|
while !RCC.cr().read().hserdy() {}
|
||||||
|
|
||||||
|
frequency
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn init_msis(&self, range: MSIRange) -> Hertz {
|
||||||
|
// Check MSI output per RM0456 § 11.4.10
|
||||||
|
match self.voltage_range {
|
||||||
|
VoltageScale::RANGE4 => {
|
||||||
|
assert!(range as u32 <= 24_000_000);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range
|
||||||
|
loop {
|
||||||
|
let cr = RCC.cr().read();
|
||||||
|
if cr.msison() == false || cr.msisrdy() == true {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
|
||||||
let sys_clk = match config.mux {
|
|
||||||
ClockSrc::MSI(range) => {
|
|
||||||
RCC.icscr1().modify(|w| {
|
RCC.icscr1().modify(|w| {
|
||||||
let bits: Msirange = range.into();
|
let bits: Msirange = range.into();
|
||||||
w.set_msisrange(bits);
|
w.set_msisrange(bits);
|
||||||
@ -221,75 +300,159 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
w.set_msison(true);
|
w.set_msison(true);
|
||||||
});
|
});
|
||||||
while !RCC.cr().read().msisrdy() {}
|
while !RCC.cr().read().msisrdy() {}
|
||||||
|
Hertz(range as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
range.into()
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
mux: ClockSrc::default(),
|
||||||
|
ahb_pre: AHBPrescaler::DIV1,
|
||||||
|
apb1_pre: APBPrescaler::DIV1,
|
||||||
|
apb2_pre: APBPrescaler::DIV1,
|
||||||
|
apb3_pre: APBPrescaler::DIV1,
|
||||||
|
hsi48: false,
|
||||||
|
voltage_range: VoltageScale::RANGE3,
|
||||||
}
|
}
|
||||||
ClockSrc::HSE(freq) => {
|
}
|
||||||
RCC.cr().write(|w| w.set_hseon(true));
|
}
|
||||||
while !RCC.cr().read().hserdy() {}
|
|
||||||
|
|
||||||
freq.0
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
}
|
// Ensure PWR peripheral clock is enabled
|
||||||
ClockSrc::HSI16 => {
|
RCC.ahb3enr().modify(|w| {
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
w.set_pwren(true);
|
||||||
while !RCC.cr().read().hsirdy() {}
|
});
|
||||||
|
RCC.ahb3enr().read(); // synchronize
|
||||||
|
|
||||||
HSI_FREQ.0
|
// Set the requested power mode
|
||||||
}
|
PWR.vosr().modify(|w| {
|
||||||
ClockSrc::PLL1R(src, m, n, div) => {
|
w.set_vos(config.voltage_range);
|
||||||
let freq = match src {
|
});
|
||||||
PllSrc::MSI(_) => {
|
while !PWR.vosr().read().vosrdy() {}
|
||||||
// TODO: enable MSI
|
|
||||||
MSIRange::default().into()
|
|
||||||
}
|
|
||||||
PllSrc::HSE(hertz) => {
|
|
||||||
// TODO: enable HSE
|
|
||||||
hertz.0
|
|
||||||
}
|
|
||||||
PllSrc::HSI16 => {
|
|
||||||
RCC.cr().write(|w| w.set_hsion(true));
|
|
||||||
while !RCC.cr().read().hsirdy() {}
|
|
||||||
|
|
||||||
HSI_FREQ.0
|
let sys_clk = match config.mux {
|
||||||
}
|
ClockSrc::MSI(range) => config.init_msis(range),
|
||||||
|
ClockSrc::HSE(freq) => config.init_hse(freq),
|
||||||
|
ClockSrc::HSI16 => config.init_hsi16(),
|
||||||
|
ClockSrc::PLL1R(pll) => {
|
||||||
|
// Configure the PLL source
|
||||||
|
let source_clk = match pll.source {
|
||||||
|
PllSrc::MSIS(range) => config.init_msis(range),
|
||||||
|
PllSrc::HSE(hertz) => config.init_hse(hertz),
|
||||||
|
PllSrc::HSI16 => config.init_hsi16(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// disable
|
// Calculate the reference clock, which is the source divided by m
|
||||||
|
let reference_clk = source_clk / (pll.m as u8 as u32 + 1);
|
||||||
|
|
||||||
|
// Check limits per RM0456 § 11.4.6
|
||||||
|
assert!(Hertz::mhz(4) <= reference_clk && reference_clk <= Hertz::mhz(16));
|
||||||
|
|
||||||
|
// Calculate the PLL1 VCO clock and PLL1 R output clock
|
||||||
|
let pll1_clk = reference_clk * (pll.n as u8 as u32);
|
||||||
|
let pll1r_clk = pll1_clk / (pll.r as u8 as u32);
|
||||||
|
|
||||||
|
// Check system clock per RM0456 § 11.4.9
|
||||||
|
assert!(pll1r_clk <= Hertz::mhz(160));
|
||||||
|
|
||||||
|
// Check PLL clocks per RM0456 § 11.4.10
|
||||||
|
match config.voltage_range {
|
||||||
|
VoltageScale::RANGE1 => {
|
||||||
|
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
|
||||||
|
assert!(pll1r_clk <= Hertz::mhz(208));
|
||||||
|
}
|
||||||
|
VoltageScale::RANGE2 => {
|
||||||
|
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
|
||||||
|
assert!(pll1r_clk <= Hertz::mhz(110));
|
||||||
|
}
|
||||||
|
VoltageScale::RANGE3 => {
|
||||||
|
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(330));
|
||||||
|
assert!(pll1r_clk <= Hertz::mhz(55));
|
||||||
|
}
|
||||||
|
VoltageScale::RANGE4 => {
|
||||||
|
panic!("PLL is unavailable in voltage range 4");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
|
||||||
|
// value that results in an output between 4 and 16 MHz for the PWR EPOD boost
|
||||||
|
let mboost = if pll1r_clk >= Hertz::mhz(55) {
|
||||||
|
// source_clk can be up to 50 MHz, so there's just a few cases:
|
||||||
|
if source_clk > Hertz::mhz(32) {
|
||||||
|
// Divide by 4, giving EPOD 8-12.5 MHz
|
||||||
|
Pllmboost::DIV4
|
||||||
|
} else if source_clk > Hertz::mhz(16) {
|
||||||
|
// Divide by 2, giving EPOD 8-16 MHz
|
||||||
|
Pllmboost::DIV2
|
||||||
|
} else {
|
||||||
|
// Bypass, giving EPOD 4-16 MHz
|
||||||
|
Pllmboost::BYPASS
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Nothing to do
|
||||||
|
Pllmboost::BYPASS
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable the PLL, and wait for it to disable
|
||||||
RCC.cr().modify(|w| w.set_pllon(0, false));
|
RCC.cr().modify(|w| w.set_pllon(0, false));
|
||||||
while RCC.cr().read().pllrdy(0) {}
|
while RCC.cr().read().pllrdy(0) {}
|
||||||
|
|
||||||
let vco = freq * n as u8 as u32;
|
// Configure the PLL
|
||||||
let pll_ck = vco / (div as u8 as u32 + 1);
|
|
||||||
|
|
||||||
RCC.pll1cfgr().write(|w| {
|
RCC.pll1cfgr().write(|w| {
|
||||||
w.set_pllm(m.into());
|
// Configure PLL1 source and prescaler
|
||||||
w.set_pllsrc(src.into());
|
w.set_pllsrc(pll.source.into());
|
||||||
|
w.set_pllm(pll.m.into());
|
||||||
|
|
||||||
|
// Configure PLL1 input frequncy range
|
||||||
|
let input_range = if reference_clk <= Hertz::mhz(8) {
|
||||||
|
Pllrge::FREQ_4TO8MHZ
|
||||||
|
} else {
|
||||||
|
Pllrge::FREQ_8TO16MHZ
|
||||||
|
};
|
||||||
|
w.set_pllrge(input_range);
|
||||||
|
|
||||||
|
// Set the prescaler for PWR EPOD
|
||||||
|
w.set_pllmboost(mboost);
|
||||||
|
|
||||||
|
// Enable PLL1R output
|
||||||
w.set_pllren(true);
|
w.set_pllren(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Configure the PLL divisors
|
||||||
RCC.pll1divr().modify(|w| {
|
RCC.pll1divr().modify(|w| {
|
||||||
w.set_pllr(div.to_div());
|
// Set the VCO multiplier
|
||||||
w.set_plln(n.to_mul());
|
w.set_plln(pll.n.to_mul());
|
||||||
|
// Set the R output divisor
|
||||||
|
w.set_pllr(pll.r.to_div());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enable PLL
|
// Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
|
||||||
|
if pll1r_clk >= Hertz::mhz(55) {
|
||||||
|
// Enable the booster
|
||||||
|
PWR.vosr().modify(|w| {
|
||||||
|
w.set_boosten(true);
|
||||||
|
});
|
||||||
|
while !PWR.vosr().read().boostrdy() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the PLL
|
||||||
RCC.cr().modify(|w| w.set_pllon(0, true));
|
RCC.cr().modify(|w| w.set_pllon(0, true));
|
||||||
while !RCC.cr().read().pllrdy(0) {}
|
while !RCC.cr().read().pllrdy(0) {}
|
||||||
|
|
||||||
pll_ck
|
pll1r_clk
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
.0;
|
||||||
|
|
||||||
if config.hsi48 {
|
if config.hsi48 {
|
||||||
RCC.cr().modify(|w| w.set_hsi48on(true));
|
RCC.cr().modify(|w| w.set_hsi48on(true));
|
||||||
while !RCC.cr().read().hsi48rdy() {}
|
while !RCC.cr().read().hsi48rdy() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO make configurable
|
// The clock source is ready
|
||||||
let power_vos = VoltageScale::RANGE3;
|
// Calculate and set the flash wait states
|
||||||
|
let wait_states = match config.voltage_range {
|
||||||
// states and programming delay
|
|
||||||
let wait_states = match power_vos {
|
|
||||||
// VOS 1 range VCORE 1.26V - 1.40V
|
// VOS 1 range VCORE 1.26V - 1.40V
|
||||||
VoltageScale::RANGE1 => {
|
VoltageScale::RANGE1 => {
|
||||||
if sys_clk < 32_000_000 {
|
if sys_clk < 32_000_000 {
|
||||||
@ -335,21 +498,34 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FLASH.acr().modify(|w| {
|
FLASH.acr().modify(|w| {
|
||||||
w.set_latency(wait_states);
|
w.set_latency(wait_states);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Switch the system clock source
|
||||||
RCC.cfgr1().modify(|w| {
|
RCC.cfgr1().modify(|w| {
|
||||||
w.set_sw(config.mux.into());
|
w.set_sw(config.mux.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// RM0456 § 11.4.9 specifies maximum bus frequencies per voltage range, but the maximum bus
|
||||||
|
// frequency for each voltage range exactly matches the maximum permitted PLL output frequency.
|
||||||
|
// Given that:
|
||||||
|
//
|
||||||
|
// 1. Any bus frequency can never exceed the system clock frequency;
|
||||||
|
// 2. We checked the PLL output frequency if we're using it as a system clock;
|
||||||
|
// 3. The maximum HSE frequencies at each voltage range are lower than the bus limits, and
|
||||||
|
// we checked the HSE frequency if configured as a system clock; and
|
||||||
|
// 4. The maximum frequencies from the other clock sources are lower than the lowest bus
|
||||||
|
// frequency limit
|
||||||
|
//
|
||||||
|
// ...then we do not need to perform additional bus-related frequency checks.
|
||||||
|
|
||||||
|
// Configure the bus prescalers
|
||||||
RCC.cfgr2().modify(|w| {
|
RCC.cfgr2().modify(|w| {
|
||||||
w.set_hpre(config.ahb_pre.into());
|
w.set_hpre(config.ahb_pre.into());
|
||||||
w.set_ppre1(config.apb1_pre.into());
|
w.set_ppre1(config.apb1_pre.into());
|
||||||
w.set_ppre2(config.apb2_pre.into());
|
w.set_ppre2(config.apb2_pre.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
RCC.cfgr3().modify(|w| {
|
RCC.cfgr3().modify(|w| {
|
||||||
w.set_ppre3(config.apb3_pre.into());
|
w.set_ppre3(config.apb3_pre.into());
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,12 @@ async fn main(_spawner: Spawner) {
|
|||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided);
|
config.rcc.mux = ClockSrc::PLL1R(PllConfig {
|
||||||
|
source: PllSrc::HSI16,
|
||||||
|
m: PllM::Div2,
|
||||||
|
n: PllN::Mul10,
|
||||||
|
r: PllClkDiv::NotDivided,
|
||||||
|
});
|
||||||
//config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz);
|
//config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz);
|
||||||
config.rcc.hsi48 = true;
|
config.rcc.hsi48 = true;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user