Add minimal RCC impls for L4 and F4

This commit is contained in:
Ulf Lilleengen 2021-06-14 10:48:14 +02:00
parent a13e07625f
commit 95532726b2
9 changed files with 455 additions and 137 deletions

View File

@ -0,0 +1,204 @@
pub use super::common::*;
use crate::pac;
use crate::peripherals::{self, RCC};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz;
use crate::time::U32Ext;
use core::marker::PhantomData;
use embassy::util::Unborrow;
use embassy_extras::unborrow;
use pac::rcc::vals::{Hpre, Ppre, Sw};
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
/// and with the addition of the init function to configure a system clock.
/// Only the basic setup using the HSE and HSI clocks are supported as of now.
/// HSI speed
pub const HSI_FREQ: u32 = 16_000_000;
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE(Hertz),
HSI16,
}
impl Into<Ppre> for APBPrescaler {
fn into(self) -> Ppre {
match self {
APBPrescaler::NotDivided => Ppre::DIV1,
APBPrescaler::Div2 => Ppre::DIV2,
APBPrescaler::Div4 => Ppre::DIV4,
APBPrescaler::Div8 => Ppre::DIV8,
APBPrescaler::Div16 => Ppre::DIV16,
}
}
}
impl Into<Hpre> for AHBPrescaler {
fn into(self) -> Hpre {
match self {
AHBPrescaler::NotDivided => Hpre::DIV1,
AHBPrescaler::Div2 => Hpre::DIV2,
AHBPrescaler::Div4 => Hpre::DIV4,
AHBPrescaler::Div8 => Hpre::DIV8,
AHBPrescaler::Div16 => Hpre::DIV16,
AHBPrescaler::Div64 => Hpre::DIV64,
AHBPrescaler::Div128 => Hpre::DIV128,
AHBPrescaler::Div256 => Hpre::DIV256,
AHBPrescaler::Div512 => Hpre::DIV512,
}
}
}
/// Clocks configutation
pub struct Config {
mux: ClockSrc,
ahb_pre: AHBPrescaler,
apb1_pre: APBPrescaler,
apb2_pre: APBPrescaler,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::HSI16,
ahb_pre: AHBPrescaler::NotDivided,
apb1_pre: APBPrescaler::NotDivided,
apb2_pre: APBPrescaler::NotDivided,
}
}
}
impl Config {
#[inline]
pub fn clock_src(mut self, mux: ClockSrc) -> Self {
self.mux = mux;
self
}
#[inline]
pub fn ahb_pre(mut self, pre: AHBPrescaler) -> Self {
self.ahb_pre = pre;
self
}
#[inline]
pub fn apb1_pre(mut self, pre: APBPrescaler) -> Self {
self.apb1_pre = pre;
self
}
#[inline]
pub fn apb2_pre(mut self, pre: APBPrescaler) -> Self {
self.apb2_pre = pre;
self
}
}
/// RCC peripheral
pub struct Rcc<'d> {
_rb: peripherals::RCC,
phantom: PhantomData<&'d mut peripherals::RCC>,
}
impl<'d> Rcc<'d> {
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
unborrow!(rcc);
Self {
_rb: rcc,
phantom: PhantomData,
}
}
// Safety: RCC init must have been called
pub fn clocks(&self) -> &'static Clocks {
unsafe { get_freqs() }
}
}
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
pub trait RccExt {
fn freeze(self, config: Config) -> Clocks;
}
impl RccExt for RCC {
#[inline]
fn freeze(self, cfgr: Config) -> Clocks {
let rcc = pac::RCC;
let (sys_clk, sw) = match cfgr.mux {
ClockSrc::HSI16 => {
// Enable HSI16
unsafe {
rcc.cr().write(|w| w.set_hsion(true));
while !rcc.cr().read().hsirdy() {}
}
(HSI_FREQ, Sw::HSI)
}
ClockSrc::HSE(freq) => {
// Enable HSE
unsafe {
rcc.cr().write(|w| w.set_hseon(true));
while !rcc.cr().read().hserdy() {}
}
(freq.0, Sw::HSE)
}
};
unsafe {
rcc.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(cfgr.ahb_pre.into());
w.set_ppre1(cfgr.apb1_pre.into());
w.set_ppre2(cfgr.apb2_pre.into());
});
}
let ahb_freq: u32 = match cfgr.ahb_pre {
AHBPrescaler::NotDivided => sys_clk,
pre => {
let pre: Hpre = pre.into();
let pre = 1 << (pre.0 as u32 - 7);
sys_clk / pre
}
};
let apb1_freq = match cfgr.apb1_pre {
APBPrescaler::NotDivided => ahb_freq,
pre => {
let pre: Ppre = pre.into();
let pre: u8 = 1 << (pre.0 - 3);
let freq = ahb_freq / pre as u32;
freq
}
};
let apb2_freq = match cfgr.apb2_pre {
APBPrescaler::NotDivided => ahb_freq,
pre => {
let pre: Ppre = pre.into();
let pre: u8 = 1 << (pre.0 - 3);
let freq = ahb_freq / (1 << (pre as u8 - 3));
freq
}
};
Clocks {
sys: sys_clk.hz(),
ahb1: ahb_freq.hz(),
ahb2: ahb_freq.hz(),
apb1: apb1_freq.hz(),
apb2: apb2_freq.hz(),
}
}
}
pub unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
}

View File

@ -6,22 +6,13 @@ use crate::pac::rcc::vals::Timpre;
use crate::pac::{DBGMCU, RCC, SYSCFG}; use crate::pac::{DBGMCU, RCC, SYSCFG};
use crate::peripherals; use crate::peripherals;
use crate::pwr::{Power, VoltageScale}; use crate::pwr::{Power, VoltageScale};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
mod pll; mod pll;
use pll::pll_setup; use pll::pll_setup;
pub use pll::PllConfig; pub use pll::PllConfig;
// Clock type used by peripherals
#[derive(Clone, Copy)]
pub struct Clocks {
pub apb1: Hertz,
pub apb2: Hertz,
pub apb4: Hertz,
pub ahb2: Hertz,
pub c1: Hertz,
}
const HSI: Hertz = Hertz(64_000_000); const HSI: Hertz = Hertz(64_000_000);
const CSI: Hertz = Hertz(4_000_000); const CSI: Hertz = Hertz(4_000_000);
const HSI48: Hertz = Hertz(48_000_000); const HSI48: Hertz = Hertz(48_000_000);
@ -532,5 +523,16 @@ impl<'d> Rcc<'d> {
} }
} }
// TODO pub unsafe fn init(config: Config) {
pub unsafe fn init(_config: Config) {} let mut power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal(), false);
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
let core_clocks = rcc.freeze(&mut power);
set_freqs(Clocks {
sys: core_clocks.c_ck,
ahb1: core_clocks.hclk,
ahb2: core_clocks.hclk,
apb1: core_clocks.pclk1,
apb2: core_clocks.pclk2,
apb4: core_clocks.pclk4,
});
}

View File

@ -1,6 +1,7 @@
pub use super::common::*;
use crate::pac; use crate::pac;
use crate::peripherals::{self, CRS, RCC, SYSCFG}; use crate::peripherals::{self, CRS, RCC, SYSCFG};
use crate::rcc::{get_freqs, set_freqs}; use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use crate::time::U32Ext; use crate::time::U32Ext;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -12,13 +13,8 @@ use pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
/// and with the addition of the init function to configure a system clock. /// and with the addition of the init function to configure a system clock.
#[derive(Clone, Copy)] /// HSI speed
pub struct Clocks { pub const HSI_FREQ: u32 = 16_000_000;
pub sys: Hertz,
pub ahb: Hertz,
pub apb1: Hertz,
pub apb2: Hertz,
}
/// System clock mux source /// System clock mux source
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -29,90 +25,6 @@ pub enum ClockSrc {
HSI16, HSI16,
} }
/// MSI Clock Range
///
/// These ranges control the frequency of the MSI. Internally, these ranges map
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
#[derive(Clone, Copy)]
pub enum MSIRange {
/// Around 65.536 kHz
Range0,
/// Around 131.072 kHz
Range1,
/// Around 262.144 kHz
Range2,
/// Around 524.288 kHz
Range3,
/// Around 1.048 MHz
Range4,
/// Around 2.097 MHz (reset value)
Range5,
/// Around 4.194 MHz
Range6,
}
impl Default for MSIRange {
fn default() -> MSIRange {
MSIRange::Range5
}
}
/// PLL divider
#[derive(Clone, Copy)]
pub enum PLLDiv {
Div2,
Div3,
Div4,
}
/// PLL multiplier
#[derive(Clone, Copy)]
pub enum PLLMul {
Mul3,
Mul4,
Mul6,
Mul8,
Mul12,
Mul16,
Mul24,
Mul32,
Mul48,
}
/// AHB prescaler
#[derive(Clone, Copy)]
pub enum AHBPrescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
Div64,
Div128,
Div256,
Div512,
}
/// APB prescaler
#[derive(Clone, Copy)]
pub enum APBPrescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
}
/// PLL clock input source
#[derive(Clone, Copy)]
pub enum PLLSource {
HSI16,
HSE(Hertz),
}
/// HSI speed
pub const HSI_FREQ: u32 = 16_000_000;
impl Into<Pllmul> for PLLMul { impl Into<Pllmul> for PLLMul {
fn into(self) -> Pllmul { fn into(self) -> Pllmul {
match self { match self {

View File

@ -0,0 +1,203 @@
pub use super::common::*;
use crate::pac;
use crate::peripherals::{self, RCC};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz;
use crate::time::U32Ext;
use core::marker::PhantomData;
use embassy::util::Unborrow;
use embassy_extras::unborrow;
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
/// and with the addition of the init function to configure a system clock.
/// Only the basic setup using the HSE and HSI clocks are supported as of now.
/// HSI speed
pub const HSI_FREQ: u32 = 16_000_000;
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE(Hertz),
HSI16,
}
impl Into<u8> for APBPrescaler {
fn into(self) -> u8 {
match self {
APBPrescaler::NotDivided => 1,
APBPrescaler::Div2 => 0x04,
APBPrescaler::Div4 => 0x05,
APBPrescaler::Div8 => 0x06,
APBPrescaler::Div16 => 0x07,
}
}
}
impl Into<u8> for AHBPrescaler {
fn into(self) -> u8 {
match self {
AHBPrescaler::NotDivided => 1,
AHBPrescaler::Div2 => 0x08,
AHBPrescaler::Div4 => 0x09,
AHBPrescaler::Div8 => 0x0a,
AHBPrescaler::Div16 => 0x0b,
AHBPrescaler::Div64 => 0x0c,
AHBPrescaler::Div128 => 0x0d,
AHBPrescaler::Div256 => 0x0e,
AHBPrescaler::Div512 => 0x0f,
}
}
}
/// Clocks configutation
pub struct Config {
mux: ClockSrc,
ahb_pre: AHBPrescaler,
apb1_pre: APBPrescaler,
apb2_pre: APBPrescaler,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::HSI16,
ahb_pre: AHBPrescaler::NotDivided,
apb1_pre: APBPrescaler::NotDivided,
apb2_pre: APBPrescaler::NotDivided,
}
}
}
impl Config {
#[inline]
pub fn clock_src(mut self, mux: ClockSrc) -> Self {
self.mux = mux;
self
}
#[inline]
pub fn ahb_pre(mut self, pre: AHBPrescaler) -> Self {
self.ahb_pre = pre;
self
}
#[inline]
pub fn apb1_pre(mut self, pre: APBPrescaler) -> Self {
self.apb1_pre = pre;
self
}
#[inline]
pub fn apb2_pre(mut self, pre: APBPrescaler) -> Self {
self.apb2_pre = pre;
self
}
}
/// RCC peripheral
pub struct Rcc<'d> {
_rb: peripherals::RCC,
phantom: PhantomData<&'d mut peripherals::RCC>,
}
impl<'d> Rcc<'d> {
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
unborrow!(rcc);
Self {
_rb: rcc,
phantom: PhantomData,
}
}
// Safety: RCC init must have been called
pub fn clocks(&self) -> &'static Clocks {
unsafe { get_freqs() }
}
}
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
pub trait RccExt {
fn freeze(self, config: Config) -> Clocks;
}
impl RccExt for RCC {
#[inline]
fn freeze(self, cfgr: Config) -> Clocks {
let rcc = pac::RCC;
let (sys_clk, sw) = match cfgr.mux {
ClockSrc::HSI16 => {
// Enable HSI16
unsafe {
rcc.cr().write(|w| w.set_hsion(true));
while !rcc.cr().read().hsirdy() {}
}
(HSI_FREQ, 0x01)
}
ClockSrc::HSE(freq) => {
// Enable HSE
unsafe {
rcc.cr().write(|w| w.set_hseon(true));
while !rcc.cr().read().hserdy() {}
}
(freq.0, 0x02)
}
};
unsafe {
rcc.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(cfgr.ahb_pre.into());
w.set_ppre1(cfgr.apb1_pre.into());
w.set_ppre2(cfgr.apb2_pre.into());
});
}
let ahb_freq: u32 = match cfgr.ahb_pre {
AHBPrescaler::NotDivided => sys_clk,
pre => {
let pre: u8 = pre.into();
let pre = 1 << (pre as u32 - 7);
sys_clk / pre
}
};
let apb1_freq = match cfgr.apb1_pre {
APBPrescaler::NotDivided => ahb_freq,
pre => {
let pre: u8 = pre.into();
let pre: u8 = 1 << (pre - 3);
let freq = ahb_freq / pre as u32;
freq
}
};
let apb2_freq = match cfgr.apb2_pre {
APBPrescaler::NotDivided => ahb_freq,
pre => {
let pre: u8 = pre.into();
let pre: u8 = 1 << (pre - 3);
let freq = ahb_freq / (1 << (pre as u8 - 3));
freq
}
};
Clocks {
sys: sys_clk.hz(),
ahb1: ahb_freq.hz(),
ahb2: ahb_freq.hz(),
apb1: apb1_freq.hz(),
apb2: apb2_freq.hz(),
}
}
}
pub unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
}

View File

@ -1,7 +1,28 @@
#![macro_use] #![macro_use]
use crate::peripherals; use crate::peripherals;
use crate::time::Hertz;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
mod common;
#[derive(Clone, Copy)]
pub struct Clocks {
pub sys: Hertz,
pub apb1: Hertz,
pub apb2: Hertz,
#[cfg(any(rcc_l0))]
pub ahb: Hertz,
#[cfg(any(rcc_l4, rcc_f4, rcc_h7))]
pub ahb1: Hertz,
#[cfg(any(rcc_l4, rcc_f4, rcc_h7))]
pub ahb2: Hertz,
#[cfg(any(rcc_h7))]
pub apb4: Hertz,
}
/// Frozen clock frequencies /// Frozen clock frequencies
/// ///
@ -28,36 +49,11 @@ cfg_if::cfg_if! {
mod l0; mod l0;
pub use l0::*; pub use l0::*;
} else if #[cfg(rcc_l4)] { } else if #[cfg(rcc_l4)] {
// TODO: Implement mod l4;
use crate::time::Hertz; pub use l4::*;
#[derive(Clone, Copy)]
pub struct Clocks {
pub apb1: Hertz,
pub apb2: Hertz,
pub ahb2: Hertz,
}
#[derive(Default)]
pub struct Config {}
pub unsafe fn init(_config: Config) {
}
} else if #[cfg(rcc_f4)] { } else if #[cfg(rcc_f4)] {
// TODO: Implement mod f4;
use crate::time::Hertz; pub use f4::*;
#[derive(Clone, Copy)]
pub struct Clocks {
pub apb1: Hertz,
pub apb2: Hertz,
pub ahb2: Hertz,
}
#[derive(Default)]
pub struct Config {}
pub unsafe fn init(_config: Config) {
}
} else { } else {
#[derive(Default)] #[derive(Default)]
pub struct Config {} pub struct Config {}

View File

@ -50,7 +50,6 @@ fn main() -> ! {
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
let mut spi = Spi::new( let mut spi = Spi::new(
Hertz(16_000_000),
p.SPI3, p.SPI3,
p.PC10, p.PC10,
p.PC12, p.PC12,

View File

@ -28,7 +28,6 @@ fn main() -> ! {
rcc.enable_debug_wfe(&mut p.DBGMCU, true); rcc.enable_debug_wfe(&mut p.DBGMCU, true);
let mut spi = Spi::new( let mut spi = Spi::new(
Hertz(16_000_000),
p.SPI1, p.SPI1,
p.PB3, p.PB3,
p.PA7, p.PA7,

View File

@ -44,7 +44,6 @@ fn main() -> ! {
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
let mut spi = Spi::new( let mut spi = Spi::new(
Hertz(16_000_000),
p.SPI3, p.SPI3,
p.PC10, p.PC10,
p.PC12, p.PC12,

View File

@ -84,7 +84,11 @@ fn find_reg_for_field<'c>(
field_name: &str, field_name: &str,
) -> Option<(&'c str, &'c str)> { ) -> Option<(&'c str, &'c str)> {
rcc.fieldsets.iter().find_map(|(name, fieldset)| { rcc.fieldsets.iter().find_map(|(name, fieldset)| {
if name.starts_with(reg_prefix) { // Workaround for some families that prefix register aliases with C1_, which does
// not help matching for clock name.
if name.starts_with("C1") {
None
} else if name.starts_with(reg_prefix) {
fieldset fieldset
.fields .fields
.iter() .iter()