From 8b743e5aa3be8a9e78cf0dbb72593955f7d15df8 Mon Sep 17 00:00:00 2001 From: James Waples Date: Sun, 7 Feb 2021 09:44:12 +0000 Subject: [PATCH] Add MCO support --- examples/mco.rs | 40 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/mco.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ src/rcc.rs | 30 ++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 examples/mco.rs create mode 100644 src/mco.rs diff --git a/examples/mco.rs b/examples/mco.rs new file mode 100644 index 00000000..9fd672dd --- /dev/null +++ b/examples/mco.rs @@ -0,0 +1,40 @@ +//! Configure the MCO (Microcontroller Clock Output) to give a 2MHz signal on PA8 and PA9, sourced +//! from the internal HSI16 16MHz oscillator. + +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +extern crate panic_halt; + +use cortex_m_rt::entry; +use stm32l0xx_hal::{ + pac::{ + self, + rcc::cfgr::{MCOPRE_A, MCOSEL_A}, + }, + prelude::*, + rcc::Config, +}; + +#[entry] +fn main() -> ! { + let dp = pac::Peripherals::take().unwrap(); + + // Configure the 16MHz internal clock + let mut rcc = dp.RCC.freeze(Config::hsi16()); + + let gpioa = dp.GPIOA.split(&mut rcc); + + // Source MCO from HSI16, configure prescaler to divide by 8 to get 2MHz output. + rcc.configure_mco(MCOSEL_A::HSI16, MCOPRE_A::DIV8, (gpioa.pa8, gpioa.pa9)); + + // Individual pins can also be set by passing them directly: + // rcc.enable_mco(MCOSEL_A::HSI16, MCOPRE_A::DIV8, gpioa.pa8); + + // Or for larger devices, all 3 MCO pins can be configured: + // rcc.configure_mco(MCOSEL_A::HSI16, MCOPRE_A::DIV8, (gpioa.pa8, gpioa.pa9, gpiob.pb13)); + + // Probe PA8 or PA9 to see generated 2MHz MCO signal. + loop {} +} diff --git a/src/lib.rs b/src/lib.rs index 92a4f690..8af51d19 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ pub mod gpio; ))] pub mod i2c; pub mod lptim; +pub mod mco; pub mod prelude; pub mod pwm; pub mod pwr; diff --git a/src/mco.rs b/src/mco.rs new file mode 100644 index 00000000..55b25982 --- /dev/null +++ b/src/mco.rs @@ -0,0 +1,52 @@ +//! MCO (Microcontroller Clock Output) +//! +//! MCO is available on PA8 or PA9. See "Table 16. Alternate function port A" in the datasheet. + +use crate::gpio::{gpioa, gpiob, AltMode, Analog}; + +pub trait Pin { + fn into_mco(self); +} + +impl Pin for gpioa::PA8 { + fn into_mco(self) { + self.set_alt_mode(AltMode::AF0); + } +} + +impl Pin for gpioa::PA9 { + fn into_mco(self) { + self.set_alt_mode(AltMode::AF0); + } +} + +impl Pin for gpiob::PB13 { + fn into_mco(self) { + self.set_alt_mode(AltMode::AF2); + } +} + +// Blanket impls to allow configuration of all MCO pins. +impl Pin for (P1, P2) +where + P1: Pin, + P2: Pin, +{ + fn into_mco(self) { + self.0.into_mco(); + self.1.into_mco(); + } +} + +impl Pin for (P1, P2, P3) +where + P1: Pin, + P2: Pin, + P3: Pin, +{ + fn into_mco(self) { + self.0.into_mco(); + self.1.into_mco(); + self.2.into_mco(); + } +} diff --git a/src/rcc.rs b/src/rcc.rs index 7f708db4..7dd10114 100755 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -1,3 +1,5 @@ +use crate::mco; +use crate::pac::rcc::cfgr::{MCOPRE_A, MCOSEL_A}; use crate::pac::RCC; use crate::time::{Hertz, U32Ext}; @@ -226,6 +228,28 @@ impl Rcc { } } +impl Rcc { + /// Configure MCO (Microcontroller Clock Output). + pub fn configure_mco

( + &mut self, + source: MCOSEL_A, + prescaler: MCOPRE_A, + output_pin: P, + ) -> MCOEnabled + where + P: mco::Pin, + { + output_pin.into_mco(); + + self.rb.cfgr.modify(|_, w| { + w.mcosel().variant(source); + w.mcopre().variant(prescaler) + }); + + MCOEnabled(()) + } +} + /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration pub trait RccExt { fn freeze(self, config: Config) -> Rcc; @@ -425,3 +449,9 @@ impl Clocks { /// You can get an instance of this struct by calling [`Rcc::enable_hsi48`]. #[derive(Clone, Copy)] pub struct HSI48(()); + +/// Token that exists only if MCO (Microcontroller Clock Out) has been enabled. +/// +/// You can get an instance of this struct by calling [`Rcc::configure_mco`]. +#[derive(Clone, Copy)] +pub struct MCOEnabled(());