Skip to content

Commit 254965d

Browse files
committed
rcc: Add MCO configuration
1 parent 6fa92c1 commit 254965d

File tree

2 files changed

+256
-2
lines changed

2 files changed

+256
-2
lines changed

src/rcc.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ use crate::time::Hertz;
144144
use log::debug;
145145

146146
mod core_clocks;
147+
mod mco;
147148
mod pll;
148149
pub mod rec;
149150
mod reset_reason;
@@ -153,6 +154,8 @@ pub use pll::{PllConfig, PllConfigStrategy};
153154
pub use rec::{LowPowerMode, PeripheralREC, ResetEnable};
154155
pub use reset_reason::ResetReason;
155156

157+
use mco::{MCO1Config, MCO2Config, MCO1, MCO2};
158+
156159
/// Configuration of the core clocks
157160
pub struct Config {
158161
hse: Option<u32>,
@@ -167,6 +170,8 @@ pub struct Config {
167170
rcc_pclk3: Option<u32>,
168171
#[cfg(feature = "rm0481")]
169172
rcc_pclk4: Option<u32>,
173+
mco1: MCO1Config,
174+
mco2: MCO2Config,
170175
pll1: PllConfig,
171176
pll2: PllConfig,
172177
#[cfg(feature = "rm0481")]
@@ -196,6 +201,8 @@ impl RccExt for RCC {
196201
rcc_pclk3: None,
197202
#[cfg(feature = "rm0481")]
198203
rcc_pclk4: None,
204+
mco1: MCO1Config::default(),
205+
mco2: MCO2Config::default(),
199206
pll1: PllConfig::default(),
200207
pll2: PllConfig::default(),
201208
#[cfg(feature = "rm0481")]
@@ -547,6 +554,13 @@ impl Rcc {
547554
// We do not reset RCC here. This routine must assert when
548555
// the previous state of the RCC peripheral is unacceptable.
549556

557+
// config modifications ----------------------------------------
558+
// (required for self-consistency and usability)
559+
560+
// if needed for mco, set sys_ck / pll1_p / pll1_q / pll2_p
561+
self.mco1_setup();
562+
self.mco2_setup();
563+
550564
// sys_ck from PLL if needed, else HSE or HSI
551565
let (sys_ck, sys_use_pll1_p) = self.sys_ck_setup();
552566

@@ -627,6 +641,30 @@ impl Rcc {
627641
(ppre3, ppre3_bits): (self, rcc_hclk, rcc_pclk3),
628642
}
629643

644+
// Calculate MCO dividers and real MCO frequencies
645+
let mco1_in = match self.config.mco1.source {
646+
// We set the required clock earlier, so can unwrap() here.
647+
MCO1::Hsi => HSI,
648+
MCO1::Lse => unimplemented!(),
649+
MCO1::Hse => self.config.hse.unwrap(),
650+
MCO1::Pll1Q => pll1_q_ck.unwrap().raw(),
651+
MCO1::Hsi48 => HSI48,
652+
};
653+
let (mco_1_pre, mco1_ck) =
654+
self.config.mco1.calculate_prescaler(mco1_in);
655+
656+
let mco2_in = match self.config.mco2.source {
657+
// We set the required clock earlier, so can unwrap() here.
658+
MCO2::Sysclk => sys_ck.raw(),
659+
MCO2::Pll2P => pll2_p_ck.unwrap().raw(),
660+
MCO2::Hse => self.config.hse.unwrap(),
661+
MCO2::Pll1P => pll1_p_ck.unwrap().raw(),
662+
MCO2::Csi => CSI,
663+
MCO2::Lsi => LSI,
664+
};
665+
let (mco_2_pre, mco2_ck) =
666+
self.config.mco2.calculate_prescaler(mco2_in);
667+
630668
// Start switching clocks here! ----------------------------------------
631669

632670
// Flash setup
@@ -640,6 +678,21 @@ impl Rcc {
640678
rcc.cr().modify(|_, w| w.hsi48on().on());
641679
while rcc.cr().read().hsi48rdy().is_not_ready() {}
642680

681+
// Set the MCO outputs.
682+
//
683+
// It is highly recommended to configure these bits only after
684+
// reset, before enabling the external oscillators and the PLLs.
685+
rcc.cfgr1().modify(|_, w| {
686+
w.mco1sel()
687+
.variant(self.config.mco1.source)
688+
.mco1pre()
689+
.bits(mco_1_pre)
690+
.mco2sel()
691+
.variant(self.config.mco2.source)
692+
.mco2pre()
693+
.bits(mco_2_pre)
694+
});
695+
643696
// HSE
644697
let hse_ck = match self.config.hse {
645698
Some(hse) => {
@@ -834,8 +887,8 @@ impl Rcc {
834887
hse_ck,
835888
lse_ck,
836889
audio_ck,
837-
mco1_ck: None,
838-
mco2_ck: None,
890+
mco1_ck,
891+
mco2_ck,
839892
pll1_p_ck,
840893
pll1_q_ck,
841894
pll1_r_ck,

src/rcc/mco.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
//! Micro-Controller Out (MCO) pins
2+
3+
use super::Rcc;
4+
use crate::time::Hertz;
5+
6+
pub use crate::stm32::rcc::cfgr1::MCO1SEL as MCO1;
7+
pub use crate::stm32::rcc::cfgr1::MCO2SEL as MCO2;
8+
9+
/// Clock settings for Micro-Controller Out 1 (MCO1)
10+
pub struct MCO1Config {
11+
pub(super) source: MCO1,
12+
pub(super) frequency: Option<u32>,
13+
}
14+
impl Default for MCO1Config {
15+
fn default() -> MCO1Config {
16+
Self {
17+
source: MCO1::Hsi,
18+
frequency: None,
19+
}
20+
}
21+
}
22+
23+
/// Clock settings for Micro-Controller Out 2 (MCO2)
24+
pub struct MCO2Config {
25+
pub(super) source: MCO2,
26+
frequency: Option<u32>,
27+
}
28+
impl Default for MCO2Config {
29+
fn default() -> MCO2Config {
30+
Self {
31+
source: MCO2::Sysclk,
32+
frequency: None,
33+
}
34+
}
35+
}
36+
37+
macro_rules! calculate_prescaler {
38+
() => {
39+
/// Calculates the prescaler and the resulting clock frequency
40+
pub(super) fn calculate_prescaler(
41+
&self,
42+
in_ck: u32,
43+
) -> (u8, Option<Hertz>) {
44+
// Running?
45+
if let Some(freq) = self.frequency {
46+
// Calculate prescaler
47+
let prescaler = match (in_ck + freq - 1) / freq {
48+
0 => unreachable!(),
49+
x @ 1..=15 => x,
50+
_ => {
51+
panic!("Clock is too fast to achieve {} Hz MCO!", freq)
52+
}
53+
};
54+
55+
(prescaler as u8, Some(Hertz::from_raw(in_ck / prescaler)))
56+
} else {
57+
// Disabled
58+
(0, None)
59+
}
60+
}
61+
};
62+
}
63+
impl MCO1Config {
64+
calculate_prescaler!();
65+
}
66+
impl MCO2Config {
67+
calculate_prescaler!();
68+
}
69+
70+
impl Rcc {
71+
/// Checks the MCO1 setup and sets further requirements in `config` if they
72+
/// are currently set to `None`
73+
///
74+
/// # Panics
75+
///
76+
/// Panics if the MCO1 setup is invalid, or if it is inconsistent with the
77+
/// rest of the `config`
78+
pub(super) fn mco1_setup(&mut self) {
79+
// HSI always runs
80+
81+
// LSE unimplemented
82+
83+
// HSE must be explicitly stated
84+
if self.config.mco1.source == MCO1::Hse {
85+
assert!(
86+
self.config.hse.is_some(),
87+
"HSE is required for MCO1. Explicitly state its frequency with `use_hse`"
88+
);
89+
}
90+
91+
// Set pll1_q_ck based on requirement
92+
if self.config.mco1.source == MCO1::Pll1Q
93+
&& self.config.pll1.q_ck.is_none()
94+
{
95+
self.config.pll1.q_ck = self.config.mco1.frequency;
96+
}
97+
98+
// HSI48 always runs
99+
}
100+
101+
/// Checks the MCO2 setup and sets further requirements in `config` if they
102+
/// are currently set to `None`
103+
///
104+
/// # Panics
105+
///
106+
/// Panics if the MCO2 setup is invalid, or if it is inconsistent with the
107+
/// rest of the `config`
108+
pub(super) fn mco2_setup(&mut self) {
109+
// Set sysclk based on requirement
110+
if self.config.mco2.source == MCO2::Sysclk
111+
&& self.config.sys_ck.is_none()
112+
{
113+
self.config.sys_ck = self.config.mco2.frequency;
114+
}
115+
116+
// Set pll2_p_ck based on requirement
117+
if self.config.mco2.source == MCO2::Pll2P
118+
&& self.config.pll2.p_ck.is_none()
119+
{
120+
self.config.pll2.p_ck = self.config.mco2.frequency;
121+
}
122+
123+
// HSE must be explicitly stated
124+
if self.config.mco2.source == MCO2::Hse {
125+
assert!(
126+
self.config.hse.is_some(),
127+
"HSE is required for MCO2. Explicitly state its frequency with `use_hse`"
128+
);
129+
}
130+
131+
// Set pll1_p_ck based on requirement
132+
if self.config.mco2.source == MCO2::Pll1P
133+
&& self.config.pll1.p_ck.is_none()
134+
{
135+
self.config.pll1.p_ck = self.config.mco2.frequency;
136+
}
137+
138+
// CSI always runs
139+
140+
// LSI unimplemented
141+
}
142+
}
143+
144+
macro_rules! mco1_setters {
145+
($($mco_setter:ident: $source:ident $doc:expr),+) => {
146+
/// Setters for Micro-Controller Out 1 (MCO1)
147+
impl Rcc {
148+
$(
149+
/// Set the MCO1 output frequency. The clock is sourced from
150+
#[doc=$doc]
151+
///
152+
/// This only enables the signal within the RCC block, it does
153+
/// not enable the MCO1 output pin itself (use the GPIO for
154+
/// that).
155+
#[must_use]
156+
pub fn $mco_setter(mut self, freq: Hertz) -> Self {
157+
self.config.mco1.source = MCO1::$source;
158+
self.config.mco1.frequency = Some(freq.raw());
159+
self
160+
}
161+
)+
162+
}
163+
}
164+
}
165+
mco1_setters! {
166+
mco1_from_hsi: Hsi "the HSI",
167+
//mco1_from_lse: Lse "the LSE", UNIMPLEMENTED
168+
mco1_from_hse: Hse "the HSE",
169+
mco1_from_pll1_q_ck: Pll1Q "pll1_q_ck",
170+
mco1_from_hsi48: Hsi48 "HSI48"
171+
}
172+
173+
macro_rules! mco2_setters {
174+
($($mco_setter:ident: $source:ident $doc:expr),+) => {
175+
/// Setters for Micro-Controller Out 2 (MCO2)
176+
impl Rcc {
177+
$(
178+
/// Set the MCO2 output frequency. The clock is sourced from
179+
#[doc=$doc]
180+
///
181+
/// This only enables the signal within the RCC block, it does
182+
/// not enable the MCO2 output pin itself (use the GPIO for
183+
/// that).
184+
#[must_use]
185+
pub fn $mco_setter(mut self, freq: Hertz) -> Self {
186+
self.config.mco2.source = MCO2::$source;
187+
self.config.mco2.frequency = Some(freq.raw());
188+
self
189+
}
190+
)+
191+
}
192+
}
193+
}
194+
mco2_setters! {
195+
mco2_from_sys_ck: Sysclk "sys_ck",
196+
mco2_from_pll2_p_ck: Pll2P "pll2_p_ck",
197+
mco2_from_hse: Hse "the HSE",
198+
mco2_from_pll1_p_ck: Pll1P "pll1_p_ck",
199+
mco2_from_csi: Csi "CSI",
200+
mco2_from_lsi: Lsi "the LSI"
201+
}

0 commit comments

Comments
 (0)