Merge #540
540: Initial support for STM32F3 r=Dirbaio a=VasanthakumarV The [companion PR](https://github.com/embassy-rs/stm32-data/pull/109) in `stm32-data` should be merged before this PR. The examples were tested on an STM32F303VC MCU. Co-authored-by: VasanthakumarV <vasanth260m12@gmail.com> Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
commit
d5a3064c2c
18 changed files with 695 additions and 2 deletions
1
ci.sh
1
ci.sh
|
@ -54,6 +54,7 @@ cargo batch \
|
||||||
--- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
|
--- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
|
||||||
--- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
|
--- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
|
||||||
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
|
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
|
||||||
|
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
|
||||||
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
|
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
|
||||||
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
|
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
|
||||||
--- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \
|
--- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \
|
||||||
|
|
|
@ -223,6 +223,75 @@ stm32f107rb = [ "stm32-metapac/stm32f107rb" ]
|
||||||
stm32f107rc = [ "stm32-metapac/stm32f107rc" ]
|
stm32f107rc = [ "stm32-metapac/stm32f107rc" ]
|
||||||
stm32f107vb = [ "stm32-metapac/stm32f107vb" ]
|
stm32f107vb = [ "stm32-metapac/stm32f107vb" ]
|
||||||
stm32f107vc = [ "stm32-metapac/stm32f107vc" ]
|
stm32f107vc = [ "stm32-metapac/stm32f107vc" ]
|
||||||
|
stm32f301c6 = [ "stm32-metapac/stm32f301c6" ]
|
||||||
|
stm32f301c8 = [ "stm32-metapac/stm32f301c8" ]
|
||||||
|
stm32f301k6 = [ "stm32-metapac/stm32f301k6" ]
|
||||||
|
stm32f301k8 = [ "stm32-metapac/stm32f301k8" ]
|
||||||
|
stm32f301r6 = [ "stm32-metapac/stm32f301r6" ]
|
||||||
|
stm32f301r8 = [ "stm32-metapac/stm32f301r8" ]
|
||||||
|
stm32f302c6 = [ "stm32-metapac/stm32f302c6" ]
|
||||||
|
stm32f302c8 = [ "stm32-metapac/stm32f302c8" ]
|
||||||
|
stm32f302cb = [ "stm32-metapac/stm32f302cb" ]
|
||||||
|
stm32f302cc = [ "stm32-metapac/stm32f302cc" ]
|
||||||
|
stm32f302k6 = [ "stm32-metapac/stm32f302k6" ]
|
||||||
|
stm32f302k8 = [ "stm32-metapac/stm32f302k8" ]
|
||||||
|
stm32f302r6 = [ "stm32-metapac/stm32f302r6" ]
|
||||||
|
stm32f302r8 = [ "stm32-metapac/stm32f302r8" ]
|
||||||
|
stm32f302rb = [ "stm32-metapac/stm32f302rb" ]
|
||||||
|
stm32f302rc = [ "stm32-metapac/stm32f302rc" ]
|
||||||
|
stm32f302rd = [ "stm32-metapac/stm32f302rd" ]
|
||||||
|
stm32f302re = [ "stm32-metapac/stm32f302re" ]
|
||||||
|
stm32f302vb = [ "stm32-metapac/stm32f302vb" ]
|
||||||
|
stm32f302vc = [ "stm32-metapac/stm32f302vc" ]
|
||||||
|
stm32f302vd = [ "stm32-metapac/stm32f302vd" ]
|
||||||
|
stm32f302ve = [ "stm32-metapac/stm32f302ve" ]
|
||||||
|
stm32f302zd = [ "stm32-metapac/stm32f302zd" ]
|
||||||
|
stm32f302ze = [ "stm32-metapac/stm32f302ze" ]
|
||||||
|
stm32f303c6 = [ "stm32-metapac/stm32f303c6" ]
|
||||||
|
stm32f303c8 = [ "stm32-metapac/stm32f303c8" ]
|
||||||
|
stm32f303cb = [ "stm32-metapac/stm32f303cb" ]
|
||||||
|
stm32f303cc = [ "stm32-metapac/stm32f303cc" ]
|
||||||
|
stm32f303k6 = [ "stm32-metapac/stm32f303k6" ]
|
||||||
|
stm32f303k8 = [ "stm32-metapac/stm32f303k8" ]
|
||||||
|
stm32f303r6 = [ "stm32-metapac/stm32f303r6" ]
|
||||||
|
stm32f303r8 = [ "stm32-metapac/stm32f303r8" ]
|
||||||
|
stm32f303rb = [ "stm32-metapac/stm32f303rb" ]
|
||||||
|
stm32f303rc = [ "stm32-metapac/stm32f303rc" ]
|
||||||
|
stm32f303rd = [ "stm32-metapac/stm32f303rd" ]
|
||||||
|
stm32f303re = [ "stm32-metapac/stm32f303re" ]
|
||||||
|
stm32f303vb = [ "stm32-metapac/stm32f303vb" ]
|
||||||
|
stm32f303vc = [ "stm32-metapac/stm32f303vc" ]
|
||||||
|
stm32f303vd = [ "stm32-metapac/stm32f303vd" ]
|
||||||
|
stm32f303ve = [ "stm32-metapac/stm32f303ve" ]
|
||||||
|
stm32f303zd = [ "stm32-metapac/stm32f303zd" ]
|
||||||
|
stm32f303ze = [ "stm32-metapac/stm32f303ze" ]
|
||||||
|
stm32f318c8 = [ "stm32-metapac/stm32f318c8" ]
|
||||||
|
stm32f318k8 = [ "stm32-metapac/stm32f318k8" ]
|
||||||
|
stm32f328c8 = [ "stm32-metapac/stm32f328c8" ]
|
||||||
|
stm32f334c4 = [ "stm32-metapac/stm32f334c4" ]
|
||||||
|
stm32f334c6 = [ "stm32-metapac/stm32f334c6" ]
|
||||||
|
stm32f334c8 = [ "stm32-metapac/stm32f334c8" ]
|
||||||
|
stm32f334k4 = [ "stm32-metapac/stm32f334k4" ]
|
||||||
|
stm32f334k6 = [ "stm32-metapac/stm32f334k6" ]
|
||||||
|
stm32f334k8 = [ "stm32-metapac/stm32f334k8" ]
|
||||||
|
stm32f334r6 = [ "stm32-metapac/stm32f334r6" ]
|
||||||
|
stm32f334r8 = [ "stm32-metapac/stm32f334r8" ]
|
||||||
|
stm32f358cc = [ "stm32-metapac/stm32f358cc" ]
|
||||||
|
stm32f358rc = [ "stm32-metapac/stm32f358rc" ]
|
||||||
|
stm32f358vc = [ "stm32-metapac/stm32f358vc" ]
|
||||||
|
stm32f373c8 = [ "stm32-metapac/stm32f373c8" ]
|
||||||
|
stm32f373cb = [ "stm32-metapac/stm32f373cb" ]
|
||||||
|
stm32f373cc = [ "stm32-metapac/stm32f373cc" ]
|
||||||
|
stm32f373r8 = [ "stm32-metapac/stm32f373r8" ]
|
||||||
|
stm32f373rb = [ "stm32-metapac/stm32f373rb" ]
|
||||||
|
stm32f373rc = [ "stm32-metapac/stm32f373rc" ]
|
||||||
|
stm32f373v8 = [ "stm32-metapac/stm32f373v8" ]
|
||||||
|
stm32f373vb = [ "stm32-metapac/stm32f373vb" ]
|
||||||
|
stm32f373vc = [ "stm32-metapac/stm32f373vc" ]
|
||||||
|
stm32f378cc = [ "stm32-metapac/stm32f378cc" ]
|
||||||
|
stm32f378rc = [ "stm32-metapac/stm32f378rc" ]
|
||||||
|
stm32f378vc = [ "stm32-metapac/stm32f378vc" ]
|
||||||
|
stm32f398ve = [ "stm32-metapac/stm32f398ve" ]
|
||||||
stm32f401cb = [ "stm32-metapac/stm32f401cb" ]
|
stm32f401cb = [ "stm32-metapac/stm32f401cb" ]
|
||||||
stm32f401cc = [ "stm32-metapac/stm32f401cc" ]
|
stm32f401cc = [ "stm32-metapac/stm32f401cc" ]
|
||||||
stm32f401cd = [ "stm32-metapac/stm32f401cd" ]
|
stm32f401cd = [ "stm32-metapac/stm32f401cd" ]
|
||||||
|
|
1
embassy-stm32/src/pwr/f3.rs
Normal file
1
embassy-stm32/src/pwr/f3.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#[cfg_attr(any(pwr_h7, pwr_h7smps), path = "h7.rs")]
|
#[cfg_attr(any(pwr_h7, pwr_h7smps), path = "h7.rs")]
|
||||||
|
#[cfg_attr(pwr_f3, path = "f3.rs")]
|
||||||
#[cfg_attr(pwr_f4, path = "f4.rs")]
|
#[cfg_attr(pwr_f4, path = "f4.rs")]
|
||||||
#[cfg_attr(pwr_f7, path = "f7.rs")]
|
#[cfg_attr(pwr_f7, path = "f7.rs")]
|
||||||
#[cfg_attr(pwr_wl5, path = "wl5.rs")]
|
#[cfg_attr(pwr_wl5, path = "wl5.rs")]
|
||||||
|
|
374
embassy-stm32/src/rcc/f3/mod.rs
Normal file
374
embassy-stm32/src/rcc/f3/mod.rs
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use embassy::util::Unborrow;
|
||||||
|
|
||||||
|
use crate::pac::{
|
||||||
|
flash::vals::Latency,
|
||||||
|
rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre},
|
||||||
|
FLASH, RCC,
|
||||||
|
};
|
||||||
|
use crate::peripherals;
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
const HSI: u32 = 8_000_000;
|
||||||
|
|
||||||
|
/// RCC peripheral
|
||||||
|
pub struct Rcc<'d> {
|
||||||
|
config: Config,
|
||||||
|
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
/// Frequency of HSE oscillator
|
||||||
|
/// 4MHz to 32MHz
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
/// Bypass HSE for an external clock
|
||||||
|
pub bypass_hse: bool,
|
||||||
|
/// Frequency of the System Clock
|
||||||
|
pub sysclk: Option<Hertz>,
|
||||||
|
/// Frequency of AHB bus
|
||||||
|
pub hclk: Option<Hertz>,
|
||||||
|
/// Frequency of APB1 bus
|
||||||
|
/// - Max frequency 36MHz
|
||||||
|
pub pclk1: Option<Hertz>,
|
||||||
|
/// Frequency of APB2 bus
|
||||||
|
/// - Max frequency with HSE is 72MHz
|
||||||
|
/// - Max frequency without HSE is 64MHz
|
||||||
|
pub pclk2: Option<Hertz>,
|
||||||
|
/// USB clock setup
|
||||||
|
/// It is valid only when,
|
||||||
|
/// - HSE is enabled,
|
||||||
|
/// - The System clock frequency is either 48MHz or 72MHz
|
||||||
|
/// - APB1 clock has a minimum frequency of 10MHz
|
||||||
|
pub pll48: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Information required to setup the PLL clock
|
||||||
|
struct PllConfig {
|
||||||
|
pll_src: Pllsrc,
|
||||||
|
pll_mul: Pllmul,
|
||||||
|
pll_div: Option<Prediv>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize and Set the clock frequencies
|
||||||
|
pub unsafe fn init(config: Config) {
|
||||||
|
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||||
|
let clocks = Rcc::new(r, config).freeze();
|
||||||
|
set_freqs(clocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Rcc<'d> {
|
||||||
|
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freeze(self) -> Clocks {
|
||||||
|
// Calculate the real System clock, and PLL configuration if applicable
|
||||||
|
let (Hertz(sysclk), pll_config) = self.get_sysclk();
|
||||||
|
assert!(sysclk <= 72_000_000);
|
||||||
|
|
||||||
|
// Calculate real AHB clock
|
||||||
|
let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||||
|
let (hpre_bits, hpre_div) = match sysclk / hclk {
|
||||||
|
0 => unreachable!(),
|
||||||
|
1 => (Hpre::DIV1, 1),
|
||||||
|
2 => (Hpre::DIV2, 2),
|
||||||
|
3..=5 => (Hpre::DIV4, 4),
|
||||||
|
6..=11 => (Hpre::DIV8, 8),
|
||||||
|
12..=39 => (Hpre::DIV16, 16),
|
||||||
|
40..=95 => (Hpre::DIV64, 64),
|
||||||
|
96..=191 => (Hpre::DIV128, 128),
|
||||||
|
192..=383 => (Hpre::DIV256, 256),
|
||||||
|
_ => (Hpre::DIV512, 512),
|
||||||
|
};
|
||||||
|
let hclk = sysclk / hpre_div;
|
||||||
|
assert!(hclk <= 72_000_000);
|
||||||
|
|
||||||
|
// Calculate real APB1 clock
|
||||||
|
let pclk1 = self.config.pclk1.map(|p| p.0).unwrap_or(hclk);
|
||||||
|
let (ppre1_bits, ppre1) = match hclk / pclk1 {
|
||||||
|
0 => unreachable!(),
|
||||||
|
1 => (Ppre::DIV1, 1),
|
||||||
|
2 => (Ppre::DIV2, 2),
|
||||||
|
3..=5 => (Ppre::DIV4, 4),
|
||||||
|
6..=11 => (Ppre::DIV8, 8),
|
||||||
|
_ => (Ppre::DIV16, 16),
|
||||||
|
};
|
||||||
|
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||||
|
let pclk1 = hclk / ppre1;
|
||||||
|
assert!(pclk1 <= 36_000_000);
|
||||||
|
|
||||||
|
// Calculate real APB2 clock
|
||||||
|
let pclk2 = self.config.pclk2.map(|p| p.0).unwrap_or(hclk);
|
||||||
|
let (ppre2_bits, ppre2) = match hclk / pclk2 {
|
||||||
|
0 => unreachable!(),
|
||||||
|
1 => (Ppre::DIV1, 1),
|
||||||
|
2 => (Ppre::DIV2, 2),
|
||||||
|
3..=5 => (Ppre::DIV4, 4),
|
||||||
|
6..=11 => (Ppre::DIV8, 8),
|
||||||
|
_ => (Ppre::DIV16, 16),
|
||||||
|
};
|
||||||
|
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||||
|
let pclk2 = hclk / ppre2;
|
||||||
|
assert!(pclk2 <= 72_000_000);
|
||||||
|
|
||||||
|
// Set latency based on HCLK frquency
|
||||||
|
// NOTE(safety) Atomic write
|
||||||
|
unsafe {
|
||||||
|
FLASH.acr().write(|w| {
|
||||||
|
w.set_latency(if hclk <= 24_000_000 {
|
||||||
|
Latency::WS0
|
||||||
|
} else if hclk <= 48_000_000 {
|
||||||
|
Latency::WS1
|
||||||
|
} else {
|
||||||
|
Latency::WS2
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable HSE
|
||||||
|
if self.config.hse.is_some() {
|
||||||
|
// NOTE(unsafe) We own the peripheral block
|
||||||
|
unsafe {
|
||||||
|
RCC.cr().write(|w| {
|
||||||
|
w.set_hsebyp(if self.config.bypass_hse {
|
||||||
|
Hsebyp::BYPASSED
|
||||||
|
} else {
|
||||||
|
Hsebyp::NOTBYPASSED
|
||||||
|
});
|
||||||
|
// We turn on clock security to switch to HSI when HSE fails
|
||||||
|
w.set_csson(true);
|
||||||
|
w.set_hseon(true);
|
||||||
|
});
|
||||||
|
while !RCC.cr().read().hserdy() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable PLL
|
||||||
|
if let Some(ref pll_config) = pll_config {
|
||||||
|
// NOTE(unsafe) We own the peripheral block
|
||||||
|
unsafe {
|
||||||
|
RCC.cfgr().write(|w| {
|
||||||
|
w.set_pllmul(pll_config.pll_mul);
|
||||||
|
w.set_pllsrc(pll_config.pll_src);
|
||||||
|
});
|
||||||
|
if let Some(pll_div) = pll_config.pll_div {
|
||||||
|
RCC.cfgr2().write(|w| w.set_prediv(pll_div));
|
||||||
|
}
|
||||||
|
RCC.cr().modify(|w| w.set_pllon(true));
|
||||||
|
while !RCC.cr().read().pllrdy() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.config.pll48 {
|
||||||
|
let usb_pre = self.get_usb_pre(sysclk, pclk1, &pll_config);
|
||||||
|
// NOTE(unsafe) We own the peripheral block
|
||||||
|
unsafe {
|
||||||
|
RCC.cfgr().write(|w| {
|
||||||
|
w.set_usbpre(usb_pre);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set prescalers
|
||||||
|
unsafe {
|
||||||
|
// NOTE(unsafe) We own the peripheral block
|
||||||
|
RCC.cfgr().write(|w| {
|
||||||
|
w.set_ppre2(ppre2_bits);
|
||||||
|
w.set_ppre1(ppre1_bits);
|
||||||
|
w.set_hpre(hpre_bits);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for the new prescalers to kick in
|
||||||
|
// "The clocks are divided with the new prescaler factor from
|
||||||
|
// 1 to 16 AHB cycles after write"
|
||||||
|
cortex_m::asm::delay(16);
|
||||||
|
|
||||||
|
// NOTE(unsafe) We own the peripheral block
|
||||||
|
RCC.cfgr().write(|w| {
|
||||||
|
w.set_sw(match (pll_config, self.config.hse) {
|
||||||
|
(Some(_), _) => Sw::PLL,
|
||||||
|
(None, Some(_)) => Sw::HSE,
|
||||||
|
(None, None) => Sw::HSI,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Clocks {
|
||||||
|
sys: Hertz(sysclk),
|
||||||
|
apb1: Hertz(pclk1),
|
||||||
|
apb2: Hertz(pclk2),
|
||||||
|
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||||
|
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||||
|
ahb: Hertz(hclk),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_sysclk(&self) -> (Hertz, Option<PllConfig>) {
|
||||||
|
match (self.config.sysclk, self.config.hse) {
|
||||||
|
(Some(sysclk), Some(hse)) if sysclk == hse => (hse, None),
|
||||||
|
(Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None),
|
||||||
|
// If the user selected System clock is different from HSI or HSE
|
||||||
|
// we will have to setup PLL clock source
|
||||||
|
(Some(sysclk), _) => {
|
||||||
|
let (sysclk, pll_config) = self.calc_pll(sysclk);
|
||||||
|
(sysclk, Some(pll_config))
|
||||||
|
}
|
||||||
|
(None, Some(hse)) => (hse, None),
|
||||||
|
(None, None) => (Hertz(HSI), None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn calc_pll(&self, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
||||||
|
// Calculates the Multiplier and the Divisor to arrive at
|
||||||
|
// the required System clock from PLL source frequency
|
||||||
|
let get_mul_div = |sysclk, pllsrcclk| {
|
||||||
|
let common_div = gcd(sysclk, pllsrcclk);
|
||||||
|
let mut multiplier = sysclk / common_div;
|
||||||
|
let mut divisor = pllsrcclk / common_div;
|
||||||
|
// Minimum PLL multiplier is two
|
||||||
|
if multiplier == 1 {
|
||||||
|
multiplier *= 2;
|
||||||
|
divisor *= 2;
|
||||||
|
}
|
||||||
|
assert!(multiplier <= 16);
|
||||||
|
assert!(divisor <= 16);
|
||||||
|
(multiplier, divisor)
|
||||||
|
};
|
||||||
|
// Based on the source of Pll, we calculate the actual system clock
|
||||||
|
// frequency, PLL's source identifier, multiplier and divisor
|
||||||
|
let (act_sysclk, pll_src, pll_mul, pll_div) = match self.config.hse {
|
||||||
|
Some(Hertz(hse)) => {
|
||||||
|
let (multiplier, divisor) = get_mul_div(sysclk, hse);
|
||||||
|
(
|
||||||
|
Hertz((hse / divisor) * multiplier),
|
||||||
|
Pllsrc::HSE_DIV_PREDIV,
|
||||||
|
into_pll_mul(multiplier),
|
||||||
|
Some(into_pre_div(divisor)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
// For some chips PREDIV is always two, and cannot be changed
|
||||||
|
if #[cfg(any(
|
||||||
|
feature="stm32f302xd", feature="stm32f302xe", feature="stm32f303xd",
|
||||||
|
feature="stm32f303xe", feature="stm32f398xe"
|
||||||
|
))] {
|
||||||
|
let (multiplier, divisor) = get_mul_div(sysclk, HSI);
|
||||||
|
(
|
||||||
|
Hertz((hse / divisor) * multiplier),
|
||||||
|
Pllsrc::HSI_DIV_PREDIV,
|
||||||
|
into_pll_mul(multiplier),
|
||||||
|
Some(into_pre_div(divisor)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let pllsrcclk = HSI / 2;
|
||||||
|
let multiplier = sysclk / pllsrcclk;
|
||||||
|
assert!(multiplier <= 16);
|
||||||
|
(
|
||||||
|
Hertz(pllsrcclk * multiplier),
|
||||||
|
Pllsrc::HSI_DIV2,
|
||||||
|
into_pll_mul(multiplier),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(
|
||||||
|
act_sysclk,
|
||||||
|
PllConfig {
|
||||||
|
pll_src,
|
||||||
|
pll_mul,
|
||||||
|
pll_div,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_usb_pre(&self, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre {
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
// Some chips do not have USB
|
||||||
|
if #[cfg(any(stm32f301, stm32f318, stm32f334))] {
|
||||||
|
panic!("USB clock not supported by the chip");
|
||||||
|
} else {
|
||||||
|
let usb_ok = self.config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000);
|
||||||
|
match (usb_ok, sysclk) {
|
||||||
|
(true, 72_000_000) => Usbpre::DIV1_5,
|
||||||
|
(true, 48_000_000) => Usbpre::DIV1,
|
||||||
|
_ => panic!(
|
||||||
|
"USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function assumes cases when multiplier is one and it
|
||||||
|
// being greater than 16 is made impossible
|
||||||
|
#[inline]
|
||||||
|
fn into_pll_mul(multiplier: u32) -> Pllmul {
|
||||||
|
match multiplier {
|
||||||
|
2 => Pllmul::MUL2,
|
||||||
|
3 => Pllmul::MUL3,
|
||||||
|
4 => Pllmul::MUL4,
|
||||||
|
5 => Pllmul::MUL5,
|
||||||
|
6 => Pllmul::MUL6,
|
||||||
|
7 => Pllmul::MUL7,
|
||||||
|
8 => Pllmul::MUL8,
|
||||||
|
9 => Pllmul::MUL9,
|
||||||
|
10 => Pllmul::MUL10,
|
||||||
|
11 => Pllmul::MUL11,
|
||||||
|
12 => Pllmul::MUL12,
|
||||||
|
13 => Pllmul::MUL13,
|
||||||
|
14 => Pllmul::MUL14,
|
||||||
|
15 => Pllmul::MUL15,
|
||||||
|
16 => Pllmul::MUL16,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function assumes the incoming divisor cannot be greater
|
||||||
|
// than 16
|
||||||
|
#[inline]
|
||||||
|
fn into_pre_div(divisor: u32) -> Prediv {
|
||||||
|
match divisor {
|
||||||
|
1 => Prediv::DIV1,
|
||||||
|
2 => Prediv::DIV2,
|
||||||
|
3 => Prediv::DIV3,
|
||||||
|
4 => Prediv::DIV4,
|
||||||
|
5 => Prediv::DIV5,
|
||||||
|
6 => Prediv::DIV6,
|
||||||
|
7 => Prediv::DIV7,
|
||||||
|
8 => Prediv::DIV8,
|
||||||
|
9 => Prediv::DIV9,
|
||||||
|
10 => Prediv::DIV10,
|
||||||
|
11 => Prediv::DIV11,
|
||||||
|
12 => Prediv::DIV12,
|
||||||
|
13 => Prediv::DIV13,
|
||||||
|
14 => Prediv::DIV14,
|
||||||
|
15 => Prediv::DIV15,
|
||||||
|
16 => Prediv::DIV16,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine GCD using Euclidean algorithm
|
||||||
|
#[inline]
|
||||||
|
fn gcd(mut a: u32, mut b: u32) -> u32 {
|
||||||
|
while b != 0 {
|
||||||
|
let r = a % b;
|
||||||
|
a = b;
|
||||||
|
b = r;
|
||||||
|
}
|
||||||
|
a
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ pub struct Clocks {
|
||||||
#[cfg(any(rcc_wl5, rcc_u5))]
|
#[cfg(any(rcc_wl5, rcc_u5))]
|
||||||
pub apb3: Hertz,
|
pub apb3: Hertz,
|
||||||
|
|
||||||
#[cfg(any(rcc_l0, rcc_l1, rcc_f0, rcc_f1, rcc_f0x0, rcc_g0))]
|
#[cfg(any(rcc_l0, rcc_l1, rcc_f0, rcc_f1, rcc_f3, rcc_f0x0, rcc_g0))]
|
||||||
pub ahb: Hertz,
|
pub ahb: Hertz,
|
||||||
|
|
||||||
#[cfg(any(rcc_l4, rcc_f4, rcc_f7, rcc_h7, rcc_g4, rcc_u5, rcc_wb, rcc_wl5))]
|
#[cfg(any(rcc_l4, rcc_f4, rcc_f7, rcc_h7, rcc_g4, rcc_u5, rcc_wb, rcc_wl5))]
|
||||||
|
@ -81,6 +81,9 @@ cfg_if::cfg_if! {
|
||||||
} else if #[cfg(rcc_f1)] {
|
} else if #[cfg(rcc_f1)] {
|
||||||
mod f1;
|
mod f1;
|
||||||
pub use f1::*;
|
pub use f1::*;
|
||||||
|
} else if #[cfg(rcc_f3)] {
|
||||||
|
mod f3;
|
||||||
|
pub use f3::*;
|
||||||
} else if #[cfg(rcc_f4)] {
|
} else if #[cfg(rcc_f4)] {
|
||||||
mod f4;
|
mod f4;
|
||||||
pub use f4::*;
|
pub use f4::*;
|
||||||
|
|
6
examples/stm32f3/.cargo/config.toml
Normal file
6
examples/stm32f3/.cargo/config.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
|
||||||
|
runner = "probe-run --chip STM32F303VCTx"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7em-none-eabihf"
|
22
examples/stm32f3/Cargo.toml
Normal file
22
examples/stm32f3/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
name = "embassy-stm32f3-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
|
||||||
|
embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] }
|
||||||
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f303vc", "unstable-pac", "memory-x", "time-driver-tim2"] }
|
||||||
|
|
||||||
|
defmt = "0.3"
|
||||||
|
defmt-rtt = "0.3"
|
||||||
|
|
||||||
|
cortex-m = "0.7.3"
|
||||||
|
cortex-m-rt = "0.7.0"
|
||||||
|
embedded-hal = "0.2.6"
|
||||||
|
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
|
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||||
|
heapless = { version = "0.7.5", default-features = false }
|
||||||
|
nb = "1.0.0"
|
5
examples/stm32f3/build.rs
Normal file
5
examples/stm32f3/build.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
}
|
30
examples/stm32f3/src/bin/blinky.rs
Normal file
30
examples/stm32f3/src/bin/blinky.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embedded_hal::digital::v2::OutputPin;
|
||||||
|
use example_common::*;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let mut led = Output::new(p.PE12, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
info!("high");
|
||||||
|
unwrap!(led.set_high());
|
||||||
|
Timer::after(Duration::from_millis(1000)).await;
|
||||||
|
|
||||||
|
info!("low");
|
||||||
|
unwrap!(led.set_low());
|
||||||
|
Timer::after(Duration::from_millis(1000)).await;
|
||||||
|
}
|
||||||
|
}
|
33
examples/stm32f3/src/bin/button.rs
Normal file
33
examples/stm32f3/src/bin/button.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||||
|
use example_common::*;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
|
let button = Input::new(p.PA0, Pull::Down);
|
||||||
|
let mut led1 = Output::new(p.PE9, Level::High, Speed::Low);
|
||||||
|
let mut led2 = Output::new(p.PE15, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if unwrap!(button.is_high()) {
|
||||||
|
info!("high");
|
||||||
|
unwrap!(led1.set_high());
|
||||||
|
unwrap!(led2.set_low());
|
||||||
|
} else {
|
||||||
|
info!("low");
|
||||||
|
unwrap!(led1.set_low());
|
||||||
|
unwrap!(led2.set_high());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
examples/stm32f3/src/bin/button_exti.rs
Normal file
29
examples/stm32f3/src/bin/button_exti.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_stm32::exti::ExtiInput;
|
||||||
|
use embassy_stm32::gpio::{Input, Pull};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::gpio::{WaitForFallingEdge, WaitForRisingEdge};
|
||||||
|
use example_common::*;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let button = Input::new(p.PA0, Pull::Down);
|
||||||
|
let mut button = ExtiInput::new(button, p.EXTI0);
|
||||||
|
|
||||||
|
info!("Press the USER button...");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
button.wait_for_rising_edge().await;
|
||||||
|
info!("Pressed!");
|
||||||
|
button.wait_for_falling_edge().await;
|
||||||
|
info!("Released!");
|
||||||
|
}
|
||||||
|
}
|
28
examples/stm32f3/src/bin/hello.rs
Normal file
28
examples/stm32f3/src/bin/hello.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::Config;
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
fn config() -> Config {
|
||||||
|
let mut config = Config::default();
|
||||||
|
config.rcc.hse = Some(Hertz(8_000_000));
|
||||||
|
config.rcc.sysclk = Some(Hertz(16_000_000));
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy::main(config = "config()")]
|
||||||
|
async fn main(_spawner: Spawner, _p: Peripherals) -> ! {
|
||||||
|
loop {
|
||||||
|
info!("Hello World!");
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
41
examples/stm32f3/src/bin/spi_dma.rs
Normal file
41
examples/stm32f3/src/bin/spi_dma.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
use core::fmt::Write;
|
||||||
|
use core::str::from_utf8;
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_stm32::spi::{Config, Spi};
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::spi::FullDuplex;
|
||||||
|
use example_common::*;
|
||||||
|
use heapless::String;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let mut spi = Spi::new(
|
||||||
|
p.SPI1,
|
||||||
|
p.PB3,
|
||||||
|
p.PB5,
|
||||||
|
p.PB4,
|
||||||
|
p.DMA1_CH3,
|
||||||
|
p.DMA1_CH2,
|
||||||
|
Hertz(1_000_000),
|
||||||
|
Config::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for n in 0u32.. {
|
||||||
|
let mut write: String<128> = String::new();
|
||||||
|
let mut read = [0; 128];
|
||||||
|
core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap();
|
||||||
|
spi.read_write(&mut read[0..write.len()], write.as_bytes())
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
info!("read via spi+dma: {}", from_utf8(&read).unwrap());
|
||||||
|
}
|
||||||
|
}
|
30
examples/stm32f3/src/bin/usart_dma.rs
Normal file
30
examples/stm32f3/src/bin/usart_dma.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
use core::fmt::Write;
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_stm32::dma::NoDma;
|
||||||
|
use embassy_stm32::usart::{Config, Uart};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::uart::Write as _;
|
||||||
|
use example_common::*;
|
||||||
|
use heapless::String;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let config = Config::default();
|
||||||
|
let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, p.DMA1_CH4, NoDma, config);
|
||||||
|
|
||||||
|
for n in 0u32.. {
|
||||||
|
let mut s: String<128> = String::new();
|
||||||
|
core::write!(&mut s, "Hello DMA World {}!\r\n", n).unwrap();
|
||||||
|
|
||||||
|
unwrap!(usart.write(s.as_bytes()).await);
|
||||||
|
info!("wrote DMA");
|
||||||
|
}
|
||||||
|
}
|
19
examples/stm32f3/src/example_common.rs
Normal file
19
examples/stm32f3/src/example_common.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#![macro_use]
|
||||||
|
|
||||||
|
use defmt_rtt as _; // global logger
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
pub use defmt::*;
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
defmt::timestamp! {
|
||||||
|
"{=u64}",
|
||||||
|
{
|
||||||
|
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
// NOTE(no-CAS) `timestamps` runs with interrupts disabled
|
||||||
|
let n = COUNT.load(Ordering::Relaxed);
|
||||||
|
COUNT.store(n + 1, Ordering::Relaxed);
|
||||||
|
n as u64
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5506d27471c7e3297450127c3279f3dab96c94f8
|
Subproject commit f275c3f5ef23339e068f38e1790584277a24623f
|
|
@ -5,6 +5,7 @@ use std::{iter::FilterMap, path::Path, slice::Iter};
|
||||||
const SUPPORTED_FAMILIES: &[&str] = &[
|
const SUPPORTED_FAMILIES: &[&str] = &[
|
||||||
"stm32f0",
|
"stm32f0",
|
||||||
"stm32f1",
|
"stm32f1",
|
||||||
|
"stm32f3",
|
||||||
"stm32f4",
|
"stm32f4",
|
||||||
"stm32f7",
|
"stm32f7",
|
||||||
"stm32g0",
|
"stm32g0",
|
||||||
|
|
Loading…
Reference in a new issue