stm32l4x6_hal/rcc/
clocking.rs

1//! The `clocking` module contains representations of the various objects in the STM32L4x6
2//! clock tree (see Reference Manual Figs 15 and 16) useful for wiring them up.
3//!
4//! There are two main concepts: sources (enum variants) and clocks (structs). For example, the
5//! SYSCLK clock can be driven by any one of four sources: HSI, MSI, HSI, and PLLCLK. This
6//! knowledge is encoded in the SysClkSources enum.
7//!
8//! Each enum variant contains information about the clock it represents. Some clocks are
9//! configurable, and thus have fields, and some are not. For example, the LSI
10//! (LowSpeedInternalRC) clock is always 32 kHz, but the MSI (MediumSpeedInternalRC) clock can
11//! be configured and thus has a frequency component.
12//!
13//! To use them, compose them and feed them to, e.g., sysclk.
14//!
15//! ```rust
16//! let mut rcc = RCC.constrain();
17//! let msi_clk = clocking::MediumSpeedInternalRC::new(8_000_000, false);
18//! let sys_clk_src = clocking::SysClkSource::MSI(msi_clk);
19//! let cfgr = rcc.cfgr.sysclk(sys_clk_src);
20//! ```
21//!
22//! The PLL is a bit more complex because it _is_ a source (`PLLClkOutput`) and also _requires_
23//! a source (`PLLClkSource`), but you compose the types similarly.
24
25use super::rcc;
26
27/// Clocks (OSCs or RCs) that can be used as inputs to peripherals
28///
29/// This trait isn't actually specified anywhere, and is used only by convention.
30pub trait InputClock {
31    /// Return the frequency of the clock (either calculated, configured, or intrinsic)
32    fn freq(&self) -> u32;
33}
34
35/// High-speed internal 16 MHz RC
36#[derive(Clone, Copy)]
37pub struct HighSpeedInternal16RC {
38    /// Force HSI16 ON even in Stop modes
39    pub always_on: bool,
40    /// When the system wakeup clock is MSI, wake up the HSI16 in parallel to system wakeup.
41    pub auto_start: bool,
42}
43
44impl HighSpeedInternal16RC {
45    /// Applies the selection options to the configuration registers and turns the clock on
46    pub fn configure(&self, rcc: &rcc::RegisterBlock) -> (u32, u8) {
47        rcc.cr.modify(|_, w| w.hsion().set_bit().hsikeron().bit(self.always_on).hsiasfs().bit(self.auto_start));
48        while rcc.cr.read().hsirdy().bit_is_clear() {}
49        (16_000_000, 0b01)
50    }
51}
52
53/// Medium-speed internal 100 kHz - 48 MHz RC
54#[derive(Clone, Copy)]
55pub struct MediumSpeedInternalRC {
56    freq: u32,
57    auto_cal: bool,
58}
59
60impl MediumSpeedInternalRC {
61    /// Create a new MSI RC
62    ///
63    /// `freq` must be a valid MSI RC frequency range (see 6.2.3)
64    /// TODO make freq a repr(C) enum
65    pub fn new(freq: u32, auto_cal: bool) -> Self {
66        MediumSpeedInternalRC { freq, auto_cal }
67    }
68
69    /// Convert the freq range to MSIRANGE bits (6.4.1). Panics if `freq` is invalid.
70    pub fn bits(&self) -> u8 {
71        match self.freq {
72            100_000 => 0b0000,
73            200_000 => 0b0001,
74            400_000 => 0b0010,
75            800_000 => 0b0011,
76            1_000_000 => 0b0100,
77            2_000_000 => 0b0101,
78            4_000_000 => 0b0110,
79            8_000_000 => 0b0111,
80            16_000_000 => 0b1000,
81            24_000_000 => 0b1001,
82            32_000_000 => 0b1010,
83            48_000_000 => 0b1011,
84            _ => panic!("bad MSI speed value!"),
85        }
86    }
87
88    /// Configures the MSI to the specified frequency, and enables hardware
89    /// auto-calibration if requested by enabling (and waiting for) the LSE.
90    pub fn configure(&self, rcc: &rcc::RegisterBlock) -> (u32, u8) {
91        rcc.cr.modify(|_, w| unsafe { w.msirange().bits(self.bits()).msirgsel().set_bit() });
92        while rcc.cr.read().msirdy().bit_is_clear() {}
93
94        if self.auto_cal {
95            // FIXME This... may not work? I'm not sure if I've got a board problem or using
96            // the LSE requires some precondition I'm missing. In either case, LSERDY is never
97            // set by the hardware, so auto_cal doesn't succeed.
98            rcc.apb1enr1.modify(|_, w| w.pwren().set_bit());
99
100            rcc.bdcr.modify(|_, w| w.lseon().clear_bit());
101            while rcc.bdcr.read().lserdy().bit_is_set() {}
102            rcc.bdcr.modify(|_, w| unsafe { w.lsedrv().bits(0b11).lseon().set_bit() });
103            while rcc.bdcr.read().lserdy().bit_is_clear() {}
104            rcc.cr.modify(|_, w| w.msipllen().set_bit());
105        }
106        (self.freq(), 0b00)
107    }
108}
109
110impl InputClock for MediumSpeedInternalRC {
111    fn freq(&self) -> u32 {
112        self.freq
113    }
114}
115
116/// High-speed external 4-48 MHz oscillator
117#[derive(Clone, Copy)]
118pub struct HighSpeedExternalOSC(pub u32);
119
120impl InputClock for HighSpeedExternalOSC {
121    fn freq(&self) -> u32 {
122        self.0
123    }
124}
125
126impl HighSpeedExternalOSC {
127    /// Turns on the HSE oscillator.
128    ///
129    /// (Should this also configure the pin?)
130    pub fn configure(&self, rcc: &rcc::RegisterBlock) -> (u32, u8) {
131        rcc.cr.modify(|_, w| w.hseon().set_bit());
132        while rcc.cr.read().hserdy().bit_is_clear() {}
133        (self.freq(), 0b10)
134    }
135}
136
137/// Selectable input clocks to the RTC
138#[repr(C)]
139#[derive(Copy, Clone)]
140pub enum RtcClkSource {
141    /// RTC off
142    None,
143    /// Internal 32 kHz RC
144    LSI,
145    /// External 32.768 kHz oscillator
146    LSE,
147    /// High-speed external oscillator, prescaled by (a fixed value of) 32
148    HSEDiv32,
149}
150
151impl RtcClkSource {
152    /// Returns the output frequency of the RtcClkSource based on its input.
153    pub fn freq(&self, hse: Option<HighSpeedExternalOSC>) -> Option<u32> {
154        match *self {
155            RtcClkSource::None => None,
156            RtcClkSource::LSI => Some(32_000),
157            RtcClkSource::LSE => Some(32_768),
158            RtcClkSource::HSEDiv32 => {
159                if let Some(clk) = hse {
160                    Some(clk.freq() / 32)
161                } else {
162                    None
163                }
164            },
165        }
166    }
167
168    /// Return bits for setting RTCSEL (see 6.2.14)
169    pub fn bits(&self) -> u8 {
170        *self as u8
171    }
172}
173
174/// Selectable clocks for the SYSCLK signal (HCLK bus)
175#[derive(Clone, Copy)]
176pub enum SysClkSource {
177    /// High speed internal 16 MHz RC
178    HSI16(HighSpeedInternal16RC),
179    /// Medium speed internal 100kHz-48MHz RC
180    MSI(MediumSpeedInternalRC),
181    /// High-speed external oscillator
182    HSE(HighSpeedExternalOSC),
183    /// PLLCLK signal (output of PLL)
184    PLL(PLLClkOutput),
185}
186
187impl InputClock for SysClkSource {
188    fn freq(&self) -> u32 {
189        match *self {
190            SysClkSource::HSI16(_) => 16_000_000,
191            SysClkSource::MSI(s) => s.freq(),
192            SysClkSource::HSE(s) => s.freq(),
193            SysClkSource::PLL(s) => s.freq(),
194        }
195    }
196}
197
198/// PLLCLK output of PLL module
199#[derive(Clone, Copy)]
200pub struct PLLClkOutput {
201    /// The input source of the PLL module
202    pub src: PLLClkSource,
203    /// The initial prescaler value into all PLLs
204    pub m: u8,
205    n: u8,
206    r: u8,
207    f: u32,
208}
209
210impl PLLClkOutput {
211    /// Create a new PLL clock source to use as an input.
212    ///
213    /// The arguments refer to the scale factors described in Figs. 15 and 16 of the reference
214    /// manual, and end up in the PLLM, PLLN, and PLLR fields of the PLLCFGR register.
215    ///
216    /// Panics if the configuration is invalid, especially if the output frequency is >80 MHz
217    pub fn new(src: PLLClkSource, m: u8, n: u8, r: u8) -> Self {
218        assert!(m > 0 && m < 9);
219        assert!(n > 7 && n < 87);
220        assert!(r == 2 || r == 4 || r == 6 || r == 8);
221        let f = src.freq() / m as u32 * n as u32 / r as u32;
222        assert!(f < super::SYS_CLOCK_MAX);
223
224        PLLClkOutput { src, m, n, r, f }
225    }
226
227    /// Configure the PLL to enable the PLLCLK output. This explicitly does not (yet?)
228    /// support any PLL other than `PLL`, and no other outputs than `PLLCLK`, so this is
229    /// not suitable for driving e.g. USB.
230    pub fn configure(&self, rcc: &rcc::RegisterBlock) -> (u32, u8) {
231        let pllsrc_bits = self.src.configure(rcc);
232        rcc.cr.modify(|_, w| w.pllon().clear_bit());
233        while rcc.cr.read().pllrdy().bit_is_set() {}
234        rcc.pllcfgr
235            .modify(|_, w| unsafe { w.pllsrc().bits(pllsrc_bits).pllm().bits(self.m - 1).plln().bits(self.n).pllr().bits(self.r) });
236        rcc.cr.modify(|_, w| w.pllon().set_bit());
237        while rcc.cr.read().pllrdy().bit_is_clear() {}
238        rcc.pllcfgr.modify(|_, w| w.pllren().set_bit());
239        (self.freq(), 0b11)
240    }
241}
242
243impl InputClock for PLLClkOutput {
244    fn freq(&self) -> u32 {
245        self.f
246    }
247}
248
249//
250/// PLLADC2CLK output of PLLSAI2
251// #[derive(Clone, Copy)]
252// pub struct PLLADC2Clk {
253// src: PLLClkSource,
254// ...,
255// }
256//
257
258/// Selectable PLL module input sources
259#[derive(Clone, Copy)]
260pub enum PLLClkSource {
261    /// PLL off
262    None,
263    /// MSI16
264    MSI(MediumSpeedInternalRC),
265    /// HSI16
266    HSI16(HighSpeedInternal16RC),
267    /// HSE
268    HSE(HighSpeedExternalOSC),
269}
270
271impl PLLClkSource {
272    /// This configures the input to the PLL. It's usually only called by
273    /// PLLClkOutput::configure.
274    pub fn configure(&self, rcc: &rcc::RegisterBlock) -> u8 {
275        match self {
276            PLLClkSource::None => 0b00,
277            PLLClkSource::MSI(s) => {
278                let _c = s.configure(rcc);
279                0b01
280            },
281            PLLClkSource::HSI16(s) => {
282                let _c = s.configure(rcc);
283                0b10
284            },
285            PLLClkSource::HSE(s) => {
286                let _c = s.configure(rcc);
287                0b11
288            },
289        }
290    }
291}
292
293impl InputClock for PLLClkSource {
294    fn freq(&self) -> u32 {
295        match *self {
296            PLLClkSource::None => 0,
297            PLLClkSource::MSI(s) => s.freq(),
298            PLLClkSource::HSI16(_) => 16_000_000,
299            PLLClkSource::HSE(s) => s.freq(),
300        }
301    }
302}