678: Add minimal F2 family support r=Dirbaio a=Gekkio

Here's the bare minimum to support F2 family (207/217/205/215). A lot is missing in RCC (e.g. PLL support), but this is enough to have a working blinky example. The example is set up for a NUCLEO-F207ZG board which I don't have, but I've tested it on my custom board with a F215 and different pinout 😅 

After looking at other RCC implementation, I noticed there's two main API styles: a "low-level" API (e.g. L0) where the `Config` struct has dividers and other low-level "knobs", and a "high-level" API (e.g. F0) where it has desired clock frequencies and the RCC implementation figures out how to achieve them. Which one is preferred? Personally I like the low-level API slightly more, because it gives you the most control and it would be easy to also provide some functions to calculate the required parameters based on desired clock frequencies.

F2 has a nasty errata: a delay or DSB instruction must be added after every RCC peripheral clock enable. I've added this workaround to build.rs, but am not sure if this is the best approach. Any comments?

I'm planning to add PLL support too once I know which kind of API is preferred. Would you prefer a separate pull request for that, or should I continue working on this one?

Co-authored-by: Joonas Javanainen <joonas.javanainen@gmail.com>
This commit is contained in:
bors[bot] 2022-03-27 17:18:30 +00:00 committed by GitHub
commit a211003021
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 441 additions and 4 deletions

2
ci.sh
View File

@ -53,6 +53,7 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
@ -63,6 +64,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/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/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
--- 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/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \

View File

@ -58,5 +58,7 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \

View File

@ -265,6 +265,44 @@ stm32f107rb = [ "stm32-metapac/stm32f107rb" ]
stm32f107rc = [ "stm32-metapac/stm32f107rc" ]
stm32f107vb = [ "stm32-metapac/stm32f107vb" ]
stm32f107vc = [ "stm32-metapac/stm32f107vc" ]
stm32f205rb = [ "stm32-metapac/stm32f205rb" ]
stm32f205rc = [ "stm32-metapac/stm32f205rc" ]
stm32f205re = [ "stm32-metapac/stm32f205re" ]
stm32f205rf = [ "stm32-metapac/stm32f205rf" ]
stm32f205rg = [ "stm32-metapac/stm32f205rg" ]
stm32f205vb = [ "stm32-metapac/stm32f205vb" ]
stm32f205vc = [ "stm32-metapac/stm32f205vc" ]
stm32f205ve = [ "stm32-metapac/stm32f205ve" ]
stm32f205vf = [ "stm32-metapac/stm32f205vf" ]
stm32f205vg = [ "stm32-metapac/stm32f205vg" ]
stm32f205zc = [ "stm32-metapac/stm32f205zc" ]
stm32f205ze = [ "stm32-metapac/stm32f205ze" ]
stm32f205zf = [ "stm32-metapac/stm32f205zf" ]
stm32f205zg = [ "stm32-metapac/stm32f205zg" ]
stm32f207ic = [ "stm32-metapac/stm32f207ic" ]
stm32f207ie = [ "stm32-metapac/stm32f207ie" ]
stm32f207if = [ "stm32-metapac/stm32f207if" ]
stm32f207ig = [ "stm32-metapac/stm32f207ig" ]
stm32f207vc = [ "stm32-metapac/stm32f207vc" ]
stm32f207ve = [ "stm32-metapac/stm32f207ve" ]
stm32f207vf = [ "stm32-metapac/stm32f207vf" ]
stm32f207vg = [ "stm32-metapac/stm32f207vg" ]
stm32f207zc = [ "stm32-metapac/stm32f207zc" ]
stm32f207ze = [ "stm32-metapac/stm32f207ze" ]
stm32f207zf = [ "stm32-metapac/stm32f207zf" ]
stm32f207zg = [ "stm32-metapac/stm32f207zg" ]
stm32f215re = [ "stm32-metapac/stm32f215re" ]
stm32f215rg = [ "stm32-metapac/stm32f215rg" ]
stm32f215ve = [ "stm32-metapac/stm32f215ve" ]
stm32f215vg = [ "stm32-metapac/stm32f215vg" ]
stm32f215ze = [ "stm32-metapac/stm32f215ze" ]
stm32f215zg = [ "stm32-metapac/stm32f215zg" ]
stm32f217ie = [ "stm32-metapac/stm32f217ie" ]
stm32f217ig = [ "stm32-metapac/stm32f217ig" ]
stm32f217ve = [ "stm32-metapac/stm32f217ve" ]
stm32f217vg = [ "stm32-metapac/stm32f217vg" ]
stm32f217ze = [ "stm32-metapac/stm32f217ze" ]
stm32f217zg = [ "stm32-metapac/stm32f217zg" ]
stm32f301c6 = [ "stm32-metapac/stm32f301c6" ]
stm32f301c8 = [ "stm32-metapac/stm32f301c8" ]
stm32f301k6 = [ "stm32-metapac/stm32f301k6" ]

View File

@ -166,6 +166,15 @@ fn main() {
None => TokenStream::new(),
};
let after_enable = if chip_name.starts_with("stm32f2") {
// Errata: ES0005 - 2.1.11 Delay after an RCC peripheral clock enabling
quote! {
cortex_m::asm::dsb();
}
} else {
TokenStream::new()
};
let pname = format_ident!("{}", p.name);
let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase());
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
@ -180,7 +189,8 @@ fn main() {
}
fn enable() {
critical_section::with(|_| unsafe {
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true))
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
#after_enable
})
}
fn disable() {

303
embassy-stm32/src/rcc/f2.rs Normal file
View File

@ -0,0 +1,303 @@
use core::ops::Div;
use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI: Hertz = Hertz(16_000_000);
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE(Hertz, HSESrc),
HSI,
}
/// HSE clock source
#[derive(Clone, Copy)]
pub enum HSESrc {
/// Crystal/ceramic resonator
Crystal,
/// External clock source, HSE bypassed
Bypass,
}
/// AHB prescaler
#[derive(Clone, Copy, PartialEq)]
pub enum AHBPrescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
Div64,
Div128,
Div256,
Div512,
}
impl Div<AHBPrescaler> for Hertz {
type Output = Hertz;
fn div(self, rhs: AHBPrescaler) -> Self::Output {
let divisor = match rhs {
AHBPrescaler::NotDivided => 1,
AHBPrescaler::Div2 => 2,
AHBPrescaler::Div4 => 4,
AHBPrescaler::Div8 => 8,
AHBPrescaler::Div16 => 16,
AHBPrescaler::Div64 => 64,
AHBPrescaler::Div128 => 128,
AHBPrescaler::Div256 => 256,
AHBPrescaler::Div512 => 512,
};
Hertz(self.0 / divisor)
}
}
/// APB prescaler
#[derive(Clone, Copy)]
pub enum APBPrescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
}
impl Div<APBPrescaler> for Hertz {
type Output = Hertz;
fn div(self, rhs: APBPrescaler) -> Self::Output {
let divisor = match rhs {
APBPrescaler::NotDivided => 1,
APBPrescaler::Div2 => 2,
APBPrescaler::Div4 => 4,
APBPrescaler::Div8 => 8,
APBPrescaler::Div16 => 16,
};
Hertz(self.0 / divisor)
}
}
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,
}
}
}
/// Voltage Range
///
/// Represents the system supply voltage range
#[derive(Copy, Clone, PartialEq)]
pub enum VoltageRange {
/// 1.8 to 3.6 V
Min1V8,
/// 2.1 to 3.6 V
Min2V1,
/// 2.4 to 3.6 V
Min2V4,
/// 2.7 to 3.6 V
Min2V7,
}
impl VoltageRange {
const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> {
let ahb_freq = ahb_freq.0;
// Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock
// frequency
match self {
VoltageRange::Min1V8 => {
if ahb_freq <= 16_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 32_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 48_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 64_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 80_000_000 {
Some(Latency::WS4)
} else if ahb_freq <= 96_000_000 {
Some(Latency::WS5)
} else if ahb_freq <= 112_000_000 {
Some(Latency::WS6)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS7)
} else {
None
}
}
VoltageRange::Min2V1 => {
if ahb_freq <= 18_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 36_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 54_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 72_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 90_000_000 {
Some(Latency::WS4)
} else if ahb_freq <= 108_000_000 {
Some(Latency::WS5)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS6)
} else {
None
}
}
VoltageRange::Min2V4 => {
if ahb_freq <= 24_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 48_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 72_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 96_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS4)
} else {
None
}
}
VoltageRange::Min2V7 => {
if ahb_freq <= 30_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 60_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 90_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS3)
} else {
None
}
}
}
}
}
/// Clocks configuration
pub struct Config {
pub mux: ClockSrc,
pub voltage: VoltageRange,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
voltage: VoltageRange::Min1V8,
mux: ClockSrc::HSI,
ahb_pre: AHBPrescaler::NotDivided,
apb1_pre: APBPrescaler::NotDivided,
apb2_pre: APBPrescaler::NotDivided,
}
}
}
#[inline]
unsafe fn enable_hse(source: HSESrc) {
RCC.cr().write(|w| {
w.set_hsebyp(match source {
HSESrc::Bypass => true,
HSESrc::Crystal => false,
});
w.set_hseon(true)
});
while !RCC.cr().read().hserdy() {}
}
pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw) = match config.mux {
ClockSrc::HSI => {
// Enable HSI
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
(HSI, Sw::HSI)
}
ClockSrc::HSE(freq, source) => {
enable_hse(source);
(freq, Sw::HSE)
}
};
// RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
// max output to be 120 MHz, so there's no way to get higher frequencies
assert!(sys_clk <= Hertz(120_000_000));
let ahb_freq = sys_clk / config.ahb_pre;
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(ahb_freq <= Hertz(120_000_000));
let flash_ws = config.voltage.wait_states(ahb_freq).expect("Invalid HCLK");
FLASH.acr().modify(|w| w.set_latency(flash_ws));
RCC.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(config.ahb_pre.into());
w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(config.apb2_pre.into());
});
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, Hertz(freq.0 * 2))
}
};
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(apb1_freq <= Hertz(30_000_000));
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, Hertz(freq.0 * 2))
}
};
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(apb2_freq <= Hertz(60_000_000));
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
ahb3: ahb_freq,
apb1: apb1_freq,
apb1_tim: apb1_tim_freq,
apb2: apb2_freq,
apb2_tim: apb2_tim_freq,
});
}

View File

@ -5,6 +5,7 @@ use core::mem::MaybeUninit;
#[cfg_attr(rcc_f0, path = "f0.rs")]
#[cfg_attr(rcc_f1, path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")]
#[cfg_attr(rcc_f3, path = "f3.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")]
#[cfg_attr(rcc_f7, path = "f7.rs")]
@ -39,11 +40,11 @@ pub struct Clocks {
// AHB
pub ahb1: Hertz,
#[cfg(any(
rcc_l4, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5
rcc_l4, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5
))]
pub ahb2: Hertz,
#[cfg(any(
rcc_l4, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5
rcc_l4, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5
))]
pub ahb3: Hertz,
#[cfg(any(rcc_h7, rcc_h7ab))]

View File

@ -0,0 +1,6 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace STM32F207ZGTx with your chip as listed in `probe-run --list-chips`
runner = "probe-run --chip STM32F207ZGTx"
[build]
target = "thumbv7m-none-eabi"

View File

@ -0,0 +1,21 @@
[package]
authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
edition = "2018"
name = "embassy-stm32f2-examples"
version = "0.1.0"
resolver = "2"
[dependencies]
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
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"

View 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");
}

View 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::time::{Duration, Timer};
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::Peripherals;
use example_common::*;
#[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) {
info!("Hello World!");
let mut led = Output::new(p.PB14, Level::High, Speed::Low);
loop {
info!("high");
led.set_high();
Timer::after(Duration::from_millis(1000)).await;
info!("low");
led.set_low();
Timer::after(Duration::from_millis(1000)).await;
}
}

View 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
}
}

View File

@ -5,6 +5,7 @@ use std::{iter::FilterMap, path::Path, slice::Iter};
const SUPPORTED_FAMILIES: &[&str] = &[
"stm32f0",
"stm32f1",
"stm32f2",
"stm32f3",
"stm32f4",
"stm32f7",
@ -52,7 +53,7 @@ impl FilterSupported for &[(String, Vec<String>)] {
///
/// This function is slow because all the yaml files are parsed.
pub fn chip_names_and_cores() -> Vec<(String, Vec<String>)> {
glob::glob("../stm32-data/data/chips/*.yaml")
glob::glob("../stm32-data/data/chips/*.json")
.unwrap()
.filter_map(|entry| entry.map_err(|e| eprintln!("{:?}", e)).ok())
.filter_map(|entry| {