Merge #482
482: Add MCO peripheral. r=Dirbaio a=matoushybl This PR adds an abstraction over STM32 RCC feature called MCO (Microcontroller Clock Output). The clock output can bind to several clock sources and then can be scaled using a prescaler. Given that from the embassy ecosystem the RCC is generaly invisible to the user, the MCO was implemented as a separate peripheral bound to the pin where the clock should appear. Co-authored-by: Matous Hybl <hyblmatous@gmail.com>
This commit is contained in:
commit
8193885cb5
@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -14,14 +15,25 @@ fn main() {
|
|||||||
struct Peripheral {
|
struct Peripheral {
|
||||||
kind: String,
|
kind: String,
|
||||||
name: String,
|
name: String,
|
||||||
|
version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut peripheral_version_mapping = HashMap::<String, String>::new();
|
||||||
|
stm32_metapac::peripheral_versions!(
|
||||||
|
($peri:ident, $version:ident) => {
|
||||||
|
peripheral_version_mapping.insert(stringify!($peri).to_string(), stringify!($version).to_string());
|
||||||
|
println!("cargo:rustc-cfg={}", stringify!($peri));
|
||||||
|
println!("cargo:rustc-cfg={}_{}", stringify!($peri), stringify!($version));
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
let mut peripherals: Vec<Peripheral> = Vec::new();
|
let mut peripherals: Vec<Peripheral> = Vec::new();
|
||||||
stm32_metapac::peripherals!(
|
stm32_metapac::peripherals!(
|
||||||
($kind:ident, $name:ident) => {
|
($kind:ident, $name:ident) => {
|
||||||
peripherals.push(Peripheral{
|
peripherals.push(Peripheral{
|
||||||
kind: stringify!($kind).to_string(),
|
kind: stringify!($kind).to_string(),
|
||||||
name: stringify!($name).to_string(),
|
name: stringify!($name).to_string(),
|
||||||
|
version: peripheral_version_mapping[&stringify!($kind).to_ascii_lowercase()].clone()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
@ -43,7 +55,13 @@ fn main() {
|
|||||||
|
|
||||||
// We *shouldn't* have singletons for these, but the HAL currently requires
|
// We *shouldn't* have singletons for these, but the HAL currently requires
|
||||||
// singletons, for using with RccPeripheral to enable/disable clocks to them.
|
// singletons, for using with RccPeripheral to enable/disable clocks to them.
|
||||||
//"rcc" => {}
|
"rcc" => {
|
||||||
|
if p.version == "h7" {
|
||||||
|
singletons.push("MCO1".to_string());
|
||||||
|
singletons.push("MCO2".to_string());
|
||||||
|
}
|
||||||
|
singletons.push(p.name.clone());
|
||||||
|
}
|
||||||
//"dbgmcu" => {}
|
//"dbgmcu" => {}
|
||||||
//"syscfg" => {}
|
//"syscfg" => {}
|
||||||
//"dma" => {}
|
//"dma" => {}
|
||||||
@ -78,13 +96,6 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
stm32_metapac::peripheral_versions!(
|
|
||||||
($peri:ident, $version:ident) => {
|
|
||||||
println!("cargo:rustc-cfg={}", stringify!($peri));
|
|
||||||
println!("cargo:rustc-cfg={}_{}", stringify!($peri), stringify!($version));
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut s = chip_name.split('_');
|
let mut s = chip_name.split('_');
|
||||||
let mut chip_name: String = s.next().unwrap().to_string();
|
let mut chip_name: String = s.next().unwrap().to_string();
|
||||||
let core_name = if let Some(c) = s.next() {
|
let core_name = if let Some(c) = s.next() {
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use embassy::util::Unborrow;
|
use embassy::util::Unborrow;
|
||||||
|
use embassy_hal_common::unborrow;
|
||||||
|
use stm32_metapac::rcc::vals::{Mco1, Mco2};
|
||||||
|
|
||||||
|
use crate::gpio::sealed::Pin as __GpioPin;
|
||||||
|
use crate::gpio::Pin;
|
||||||
use crate::pac::rcc::vals::Timpre;
|
use crate::pac::rcc::vals::Timpre;
|
||||||
use crate::pac::{RCC, SYSCFG};
|
use crate::pac::{RCC, SYSCFG};
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
@ -508,6 +512,181 @@ impl<'d> Rcc<'d> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub enum McoClock {
|
||||||
|
Disabled,
|
||||||
|
Bypassed,
|
||||||
|
Divided(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoClock {
|
||||||
|
fn into_raw(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
McoClock::Disabled => 0,
|
||||||
|
McoClock::Bypassed => 1,
|
||||||
|
McoClock::Divided(divisor) => {
|
||||||
|
if *divisor > 15 {
|
||||||
|
panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.")
|
||||||
|
}
|
||||||
|
*divisor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Mco1Source {
|
||||||
|
Hsi,
|
||||||
|
Lse,
|
||||||
|
Hse,
|
||||||
|
Pll1Q,
|
||||||
|
Hsi48,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mco1Source {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Hsi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait McoSource {
|
||||||
|
type Raw;
|
||||||
|
|
||||||
|
fn into_raw(&self) -> Self::Raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoSource for Mco1Source {
|
||||||
|
type Raw = Mco1;
|
||||||
|
fn into_raw(&self) -> Self::Raw {
|
||||||
|
match self {
|
||||||
|
Mco1Source::Hsi => Mco1::HSI,
|
||||||
|
Mco1Source::Lse => Mco1::LSE,
|
||||||
|
Mco1Source::Hse => Mco1::HSE,
|
||||||
|
Mco1Source::Pll1Q => Mco1::PLL1_Q,
|
||||||
|
Mco1Source::Hsi48 => Mco1::HSI48,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Mco2Source {
|
||||||
|
SysClk,
|
||||||
|
Pll2Q,
|
||||||
|
Hse,
|
||||||
|
Pll1Q,
|
||||||
|
Csi,
|
||||||
|
Lsi,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mco2Source {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::SysClk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoSource for Mco2Source {
|
||||||
|
type Raw = Mco2;
|
||||||
|
fn into_raw(&self) -> Self::Raw {
|
||||||
|
match self {
|
||||||
|
Mco2Source::SysClk => Mco2::SYSCLK,
|
||||||
|
Mco2Source::Pll2Q => Mco2::PLL2_P,
|
||||||
|
Mco2Source::Hse => Mco2::HSE,
|
||||||
|
Mco2Source::Pll1Q => Mco2::PLL1_P,
|
||||||
|
Mco2Source::Csi => Mco2::CSI,
|
||||||
|
Mco2Source::Lsi => Mco2::LSI,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait McoInstance {
|
||||||
|
type Source;
|
||||||
|
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait McoPin<T: McoInstance>: Pin {
|
||||||
|
fn configure(&mut self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||||
|
|
||||||
|
pub trait McoPin<T: McoInstance>: sealed::McoPin<T> + 'static {}
|
||||||
|
|
||||||
|
macro_rules! impl_peri {
|
||||||
|
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
||||||
|
impl sealed::McoInstance for peripherals::$peri {
|
||||||
|
type Source = $source;
|
||||||
|
|
||||||
|
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
||||||
|
RCC.cfgr().modify(|w| {
|
||||||
|
w.$set_source(source);
|
||||||
|
w.$set_prescaler(prescaler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoInstance for peripherals::$peri {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
|
||||||
|
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
|
||||||
|
|
||||||
|
macro_rules! impl_pin {
|
||||||
|
($peri:ident, $pin:ident, $af:expr) => {
|
||||||
|
impl McoPin<peripherals::$peri> for peripherals::$pin {}
|
||||||
|
|
||||||
|
impl sealed::McoPin<peripherals::$peri> for peripherals::$pin {
|
||||||
|
fn configure(&mut self) {
|
||||||
|
critical_section::with(|_| unsafe {
|
||||||
|
self.set_as_af($af, crate::gpio::sealed::AFType::OutputPushPull);
|
||||||
|
self.block().ospeedr().modify(|w| {
|
||||||
|
w.set_ospeedr(
|
||||||
|
self.pin() as usize,
|
||||||
|
crate::pac::gpio::vals::Ospeedr::VERYHIGHSPEED,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::pac::peripheral_pins!(
|
||||||
|
($inst:ident, rcc, RCC, $pin:ident, MCO_1, $af:expr) => {
|
||||||
|
impl_pin!(MCO1, $pin, $af);
|
||||||
|
};
|
||||||
|
($inst:ident, rcc, RCC, $pin:ident, MCO_2, $af:expr) => {
|
||||||
|
impl_pin!(MCO2, $pin, $af);
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct Mco<'d, T: McoInstance> {
|
||||||
|
phantom: PhantomData<&'d mut T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||||
|
pub fn new(
|
||||||
|
_peri: impl Unborrow<Target = T> + 'd,
|
||||||
|
pin: impl Unborrow<Target = impl McoPin<T>> + 'd,
|
||||||
|
source: impl McoSource<Raw = T::Source>,
|
||||||
|
prescaler: McoClock,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(pin);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
pin.configure();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 mut power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal(), false);
|
||||||
|
32
examples/stm32h7/src/bin/mco.rs
Normal file
32
examples/stm32h7/src/bin/mco.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#![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::rcc::{Mco, Mco1Source, McoClock};
|
||||||
|
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.PB14, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(8));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
info!("high");
|
||||||
|
unwrap!(led.set_high());
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
|
||||||
|
info!("low");
|
||||||
|
unwrap!(led.set_low());
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user