Skip to main content

gd32e2_hal/
rcu.rs

1//! Reset and clock unit: the system clock tree and per-peripheral clock gating.
2//!
3//! [`RcuExt::constrain`] hands out an [`UnfrozenRcu`], whose only method applies a
4//! [`ClockConfig`] and turns it into the [`Rcu`] every driver takes.
5//!
6//! ```ignore
7//! let mut fmc = dp.fmc.constrain();
8//! let config = ClockConfig::default()
9//!     .sysclk(SysClk::Pll(PllFreq::Mhz48))
10//!     .adc_sel(AdcSel::Prescaled(AdcPsc::Apb2Div8));
11//! let mut rcu = dp.rcu.constrain().freeze(&mut fmc, config);
12//! let clocks = rcu.clocks();
13//! ```
14//!
15//! Drivers switch their peripheral's clock on themselves, through [`Enable`].
16//!
17//! `HXTAL` and `LXTAL` are never started.
18
19use crate::fmc::Fmc;
20use crate::pac;
21use crate::time::Hertz;
22
23const IRC8M: u32 = 8_000_000;
24const IRC28M: u32 = 28_000_000;
25const LXTAL: u32 = 32_768;
26const PLL_SRC: u32 = IRC8M / 2;
27pub(crate) const IRC40K: u32 = 40_000;
28
29const IRC28MDIV_DIV1: bool = true;
30const IRC28MDIV_DIV2: bool = false;
31const ADCSEL_IRC28M: bool = false;
32const ADCSEL_PRESCALED: bool = true;
33const ADCPSC_MSB_APB2: bool = false;
34const ADCPSC_MSB_AHB: bool = true;
35
36/// Target system clock produced by the PLL, in 4 MHz steps up to the 72 MHz limit.
37#[derive(Clone, Copy, PartialEq, Eq)]
38#[cfg_attr(feature = "defmt", derive(defmt::Format))]
39#[allow(missing_docs)]
40pub enum PllFreq {
41    Mhz8 = 8_000_000,
42    Mhz12 = 12_000_000,
43    Mhz16 = 16_000_000,
44    Mhz20 = 20_000_000,
45    Mhz24 = 24_000_000,
46    Mhz28 = 28_000_000,
47    Mhz32 = 32_000_000,
48    Mhz36 = 36_000_000,
49    Mhz40 = 40_000_000,
50    Mhz44 = 44_000_000,
51    Mhz48 = 48_000_000,
52    Mhz52 = 52_000_000,
53    Mhz56 = 56_000_000,
54    Mhz60 = 60_000_000,
55    Mhz64 = 64_000_000,
56    Mhz68 = 68_000_000,
57    Mhz72 = 72_000_000,
58}
59
60/// Source of the system clock.
61///
62/// [`Irc8m`](Self::Irc8m) means the reset state rather than a switch back to it:
63/// [`freeze`](UnfrozenRcu::freeze) leaves `SCS` alone.
64#[derive(Clone, Copy, PartialEq, Eq)]
65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66pub enum SysClk {
67    /// IRC8M straight through at 8 MHz, PLL off.
68    Irc8m,
69    /// The PLL, at the frequency it is asked for.
70    Pll(PllFreq),
71}
72
73/// AHB prescaler: divides the system clock down to `hclk`.
74#[derive(Clone, Copy, PartialEq, Eq)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76#[allow(missing_docs)]
77pub enum AhbPsc {
78    Div1 = 1,
79    Div2 = 2,
80    Div4 = 4,
81    Div8 = 8,
82    Div16 = 16,
83    Div64 = 64,
84    Div128 = 128,
85    Div256 = 256,
86    Div512 = 512,
87}
88
89/// APB prescaler: divides `hclk` down to `pclk1` (APB1) or `pclk2` (APB2).
90#[derive(Clone, Copy, PartialEq, Eq)]
91#[cfg_attr(feature = "defmt", derive(defmt::Format))]
92#[allow(missing_docs)]
93pub enum ApbPsc {
94    Div1 = 1,
95    Div2 = 2,
96    Div4 = 4,
97    Div8 = 8,
98    Div16 = 16,
99}
100
101/// Divider for the prescaled `CK_ADC` branch, including which bus it taps.
102#[derive(Clone, Copy, PartialEq, Eq)]
103#[cfg_attr(feature = "defmt", derive(defmt::Format))]
104#[allow(missing_docs)]
105pub enum AdcPsc {
106    Apb2Div2,
107    Apb2Div4,
108    Apb2Div6,
109    Apb2Div8,
110    AhbDiv3,
111    AhbDiv5,
112    AhbDiv7,
113    AhbDiv9,
114}
115
116/// Divider on the internal 28 MHz oscillator feeding `CK_ADC`.
117#[derive(Clone, Copy, PartialEq, Eq)]
118#[cfg_attr(feature = "defmt", derive(defmt::Format))]
119#[allow(missing_docs)]
120pub enum Irc28mDiv {
121    Div1,
122    Div2,
123}
124
125/// Source of the ADC clock.
126#[derive(Clone, Copy, PartialEq, Eq)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128pub enum AdcSel {
129    /// Left as the reset state: IRC28M selected but not running, so `CK_ADC` is
130    /// 0 Hz and [`Clocks::ck_adc`] reads zero.
131    Off,
132    /// The dedicated internal 28 MHz oscillator, which [`UnfrozenRcu::freeze`] starts.
133    Irc28m(Irc28mDiv),
134    /// A prescaled tap off APB2 or AHB.
135    Prescaled(AdcPsc),
136}
137
138/// Source of the USART0 clock, independent of the APB2 bus clock.
139#[derive(Clone, Copy, PartialEq, Eq)]
140#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141pub enum Usart0Sel {
142    /// The APB2 bus clock — the reset default.
143    Apb2,
144    /// The system clock, unaffected by the bus prescalers.
145    Sysclk,
146    /// The 32.768 kHz crystal.
147    ///
148    /// Selecting this without starting `LXTAL` leaves USART0 unclocked, and its
149    /// blocking reads and writes will never return.
150    Lxtal,
151    /// The internal 8 MHz RC oscillator, independent of the system clock.
152    Irc8m,
153}
154
155/// Frozen clock frequencies, produced by [`UnfrozenRcu::freeze`].
156#[derive(Clone, Copy, PartialEq, Eq)]
157#[cfg_attr(feature = "defmt", derive(defmt::Format))]
158pub struct Clocks {
159    hclk: Hertz,
160    pclk1: Hertz,
161    pclk2: Hertz,
162    sysclk: Hertz,
163    usart0: Hertz,
164    ck_adc: Hertz,
165    pclk1_tim: Hertz,
166    pclk2_tim: Hertz,
167}
168
169impl Clocks {
170    /// AHB clock, which also clocks the core.
171    #[inline]
172    pub fn hclk(&self) -> Hertz {
173        self.hclk
174    }
175    /// APB1 bus clock.
176    #[inline]
177    pub fn pclk1(&self) -> Hertz {
178        self.pclk1
179    }
180    /// APB2 bus clock.
181    #[inline]
182    pub fn pclk2(&self) -> Hertz {
183        self.pclk2
184    }
185    /// System clock, before the AHB prescaler.
186    #[inline]
187    pub fn sysclk(&self) -> Hertz {
188        self.sysclk
189    }
190    /// Clock actually feeding USART0, per [`Usart0Sel`].
191    #[inline]
192    pub fn usart0(&self) -> Hertz {
193        self.usart0
194    }
195    /// Clock feeding the ADC. Zero while [`AdcSel::Off`] stands.
196    #[inline]
197    pub fn ck_adc(&self) -> Hertz {
198        self.ck_adc
199    }
200    /// Clock feeding the timers on APB1 (TIMER2, TIMER5, TIMER13).
201    ///
202    /// Equal to [`pclk1`](Self::pclk1) only when the APB1 prescaler is
203    /// [`ApbPsc::Div1`]; on every other divider the timers are fed twice the
204    /// bus clock, so the bus clock cannot be used in a timer period formula.
205    #[inline]
206    pub fn pclk1_tim(&self) -> Hertz {
207        self.pclk1_tim
208    }
209    /// Clock feeding the timers on APB2 (TIMER0, TIMER14, TIMER15, TIMER16).
210    ///
211    /// Same doubling rule as [`pclk1_tim`](Self::pclk1_tim).
212    #[inline]
213    pub fn pclk2_tim(&self) -> Hertz {
214        self.pclk2_tim
215    }
216}
217
218/// Builder for the clock tree, applied by [`freeze`](UnfrozenRcu::freeze).
219///
220/// [`Default`] is the reset state: undivided buses, IRC8M as the system clock,
221/// USART0 on APB2 and no ADC clock. Every field is written to its registers
222/// whether it was named or not.
223#[derive(Clone, Copy, PartialEq, Eq)]
224#[cfg_attr(feature = "defmt", derive(defmt::Format))]
225#[must_use]
226pub struct ClockConfig {
227    hclk: AhbPsc,
228    pclk1: ApbPsc,
229    pclk2: ApbPsc,
230    sysclk: SysClk,
231    usart0_sel: Usart0Sel,
232    adc_sel: AdcSel,
233}
234
235impl Default for ClockConfig {
236    fn default() -> Self {
237        Self {
238            hclk: AhbPsc::Div1,
239            pclk1: ApbPsc::Div1,
240            pclk2: ApbPsc::Div1,
241            sysclk: SysClk::Irc8m,
242            usart0_sel: Usart0Sel::Apb2,
243            adc_sel: AdcSel::Off,
244        }
245    }
246}
247
248impl ClockConfig {
249    const fn pll_mul(desired: PllFreq) -> u32 {
250        (desired as u32) / PLL_SRC
251    }
252
253    /// Sets the AHB prescaler, dividing `sysclk` down to `hclk`.
254    pub const fn hclk(mut self, psc: AhbPsc) -> Self {
255        self.hclk = psc;
256        self
257    }
258    /// Sets the APB1 prescaler, dividing `hclk` down to `pclk1`.
259    pub const fn pclk1(mut self, psc: ApbPsc) -> Self {
260        self.pclk1 = psc;
261        self
262    }
263    /// Sets the APB2 prescaler, dividing `hclk` down to `pclk2`.
264    pub const fn pclk2(mut self, psc: ApbPsc) -> Self {
265        self.pclk2 = psc;
266        self
267    }
268    /// Picks the system clock source.
269    pub const fn sysclk(mut self, src: SysClk) -> Self {
270        self.sysclk = src;
271        self
272    }
273    /// Picks the USART0 clock source.
274    pub const fn usart0_sel(mut self, src: Usart0Sel) -> Self {
275        self.usart0_sel = src;
276        self
277    }
278    /// Picks the ADC clock source and starts it if needed.
279    ///
280    /// Left at [`AdcSel::Off`] the ADC has no clock and [`Clocks::ck_adc`] stays
281    /// zero — constructing an [`Adc`](crate::adc::Adc) then panics.
282    pub const fn adc_sel(mut self, sel: AdcSel) -> Self {
283        self.adc_sel = sel;
284        self
285    }
286}
287
288/// The RCU before its clock tree is frozen, handed out by [`RcuExt::constrain`].
289pub struct UnfrozenRcu {
290    rcu: pac::Rcu,
291}
292
293impl UnfrozenRcu {
294    /// Applies `config` and hands back the RCU at the resulting frequencies.
295    ///
296    /// Flash wait states are raised from the new `hclk` *before* the system clock
297    /// switches over, so the flash is never read faster than it can respond.
298    #[allow(clippy::too_many_lines)]
299    pub fn freeze(self, fmc: &mut Fmc, config: ClockConfig) -> Rcu {
300        let sysclk = match config.sysclk {
301            SysClk::Irc8m => IRC8M,
302            SysClk::Pll(desired) => {
303                let mul = ClockConfig::pll_mul(desired);
304                self.rcu.cfg0().modify(|_, w| {
305                    let w = w.pllsel().irc8m_2();
306                    match mul {
307                        2 => w.pllmf().mul2().pllmf_msb().none(),
308                        3 => w.pllmf().mul3().pllmf_msb().none(),
309                        4 => w.pllmf().mul4().pllmf_msb().none(),
310                        5 => w.pllmf().mul5().pllmf_msb().none(),
311                        6 => w.pllmf().mul6().pllmf_msb().none(),
312                        7 => w.pllmf().mul7().pllmf_msb().none(),
313                        8 => w.pllmf().mul8().pllmf_msb().none(),
314                        9 => w.pllmf().mul9().pllmf_msb().none(),
315                        10 => w.pllmf().mul10().pllmf_msb().none(),
316                        11 => w.pllmf().mul11().pllmf_msb().none(),
317                        12 => w.pllmf().mul12().pllmf_msb().none(),
318                        13 => w.pllmf().mul13().pllmf_msb().none(),
319                        14 => w.pllmf().mul14().pllmf_msb().none(),
320                        15 => w.pllmf().mul15().pllmf_msb().none(),
321                        16 => w.pllmf().mul16().pllmf_msb().none(),
322                        17 => w.pllmf().mul2().pllmf_msb().plus15(),
323                        18 => w.pllmf().mul3().pllmf_msb().plus15(),
324                        _ => unreachable!(),
325                    }
326                });
327                self.rcu.ctl0().modify(|_, w| w.pllen().on());
328                while self.rcu.ctl0().read().pllstb().is_not_ready() {}
329                desired as u32
330            }
331        };
332
333        let ahb_psc = config.hclk;
334        let hclk = sysclk / (ahb_psc as u32);
335        let apb1_psc = config.pclk1;
336        let pclk1 = hclk / (apb1_psc as u32);
337        let apb2_psc = config.pclk2;
338        let pclk2 = hclk / (apb2_psc as u32);
339
340        let usart0_sel = config.usart0_sel;
341        let usart0 = match usart0_sel {
342            Usart0Sel::Apb2 => pclk2,
343            Usart0Sel::Sysclk => sysclk,
344            Usart0Sel::Lxtal => LXTAL,
345            Usart0Sel::Irc8m => IRC8M,
346        };
347        self.rcu.cfg2().modify(|_, w| match usart0_sel {
348            Usart0Sel::Apb2 => w.usart0sel().apb2(),
349            Usart0Sel::Sysclk => w.usart0sel().sys(),
350            Usart0Sel::Lxtal => w.usart0sel().lxtal(),
351            Usart0Sel::Irc8m => w.usart0sel().irc8m(),
352        });
353
354        let ck_adc = match config.adc_sel {
355            AdcSel::Off => 0,
356            AdcSel::Irc28m(div) => {
357                self.rcu.ctl1().modify(|_, w| w.irc28men().on());
358                while self.rcu.ctl1().read().irc28mstb().is_not_ready() {}
359                self.rcu.cfg2().modify(|_, w| {
360                    let w = match div {
361                        Irc28mDiv::Div1 => w.irc28mdiv().bit(IRC28MDIV_DIV1),
362                        Irc28mDiv::Div2 => w.irc28mdiv().bit(IRC28MDIV_DIV2),
363                    };
364                    w.adcsel().bit(ADCSEL_IRC28M)
365                });
366                match div {
367                    Irc28mDiv::Div1 => IRC28M,
368                    Irc28mDiv::Div2 => IRC28M / 2,
369                }
370            }
371            AdcSel::Prescaled(psc) => {
372                // ADCPSC = 3-bit code split CFG0[15:14] + CFG2[31] (like PLLMF+MSB)
373                self.rcu.cfg0().modify(|_, w| match psc {
374                    AdcPsc::Apb2Div2 | AdcPsc::AhbDiv3 => w.adcpsc().div2(),
375                    AdcPsc::Apb2Div4 | AdcPsc::AhbDiv5 => w.adcpsc().div4(),
376                    AdcPsc::Apb2Div6 | AdcPsc::AhbDiv7 => w.adcpsc().div6(),
377                    AdcPsc::Apb2Div8 | AdcPsc::AhbDiv9 => w.adcpsc().div8(),
378                });
379                self.rcu.cfg2().modify(|_, w| {
380                    let w = match psc {
381                        AdcPsc::Apb2Div2
382                        | AdcPsc::Apb2Div4
383                        | AdcPsc::Apb2Div6
384                        | AdcPsc::Apb2Div8 => w.adcpsc().bit(ADCPSC_MSB_APB2),
385                        AdcPsc::AhbDiv3 | AdcPsc::AhbDiv5 | AdcPsc::AhbDiv7 | AdcPsc::AhbDiv9 => {
386                            w.adcpsc().bit(ADCPSC_MSB_AHB)
387                        }
388                    };
389                    w.adcsel().bit(ADCSEL_PRESCALED)
390                });
391                match psc {
392                    AdcPsc::Apb2Div2 => pclk2 / 2,
393                    AdcPsc::Apb2Div4 => pclk2 / 4,
394                    AdcPsc::Apb2Div6 => pclk2 / 6,
395                    AdcPsc::Apb2Div8 => pclk2 / 8,
396                    AdcPsc::AhbDiv3 => hclk / 3,
397                    AdcPsc::AhbDiv5 => hclk / 5,
398                    AdcPsc::AhbDiv7 => hclk / 7,
399                    AdcPsc::AhbDiv9 => hclk / 9,
400                }
401            }
402        };
403
404        fmc.set_ws(hclk);
405
406        self.rcu.cfg0().modify(|_, w| {
407            let w = match ahb_psc {
408                AhbPsc::Div1 => w.ahbpsc().div1(),
409                AhbPsc::Div2 => w.ahbpsc().div2(),
410                AhbPsc::Div4 => w.ahbpsc().div4(),
411                AhbPsc::Div8 => w.ahbpsc().div8(),
412                AhbPsc::Div16 => w.ahbpsc().div16(),
413                AhbPsc::Div64 => w.ahbpsc().div64(),
414                AhbPsc::Div128 => w.ahbpsc().div128(),
415                AhbPsc::Div256 => w.ahbpsc().div256(),
416                AhbPsc::Div512 => w.ahbpsc().div512(),
417            };
418            let w = match apb1_psc {
419                ApbPsc::Div1 => w.apb1psc().div1(),
420                ApbPsc::Div2 => w.apb1psc().div2(),
421                ApbPsc::Div4 => w.apb1psc().div4(),
422                ApbPsc::Div8 => w.apb1psc().div8(),
423                ApbPsc::Div16 => w.apb1psc().div16(),
424            };
425            let w = match apb2_psc {
426                ApbPsc::Div1 => w.apb2psc().div1(),
427                ApbPsc::Div2 => w.apb2psc().div2(),
428                ApbPsc::Div4 => w.apb2psc().div4(),
429                ApbPsc::Div8 => w.apb2psc().div8(),
430                ApbPsc::Div16 => w.apb2psc().div16(),
431            };
432            match config.sysclk {
433                SysClk::Pll(_) => w.scs().pll(),
434                // SCS is left alone: switching down after the wait states were
435                // set for a higher hclk would read the flash too fast.
436                SysClk::Irc8m => w,
437            }
438        });
439        let clocks = Clocks {
440            hclk: Hertz::from_raw(hclk),
441            pclk1: Hertz::from_raw(pclk1),
442            pclk2: Hertz::from_raw(pclk2),
443            sysclk: Hertz::from_raw(sysclk),
444            usart0: Hertz::from_raw(usart0),
445            ck_adc: Hertz::from_raw(ck_adc),
446            pclk1_tim: Hertz::from_raw(match apb1_psc {
447                ApbPsc::Div1 => hclk,              // == pclk1
448                _ => hclk / (apb1_psc as u32 / 2), // == pclk1 * 2
449            }),
450            pclk2_tim: Hertz::from_raw(match apb2_psc {
451                ApbPsc::Div1 => hclk,              // == pclk2
452                _ => hclk / (apb2_psc as u32 / 2), // == pclk2 * 2
453            }),
454        };
455        Rcu {
456            rcu: self.rcu,
457            clocks,
458        }
459    }
460}
461
462/// Divider on the PLL branch feeding `CK_OUT`, ahead of the source multiplexer.
463#[derive(Clone, Copy, PartialEq, Eq)]
464#[cfg_attr(feature = "defmt", derive(defmt::Format))]
465#[allow(missing_docs)]
466pub enum PllDiv {
467    Div1,
468    Div2,
469}
470
471/// Clock node to route out on the `CK_OUT` pin.
472///
473/// Selecting a source that isn't running leaves the pin quiet.
474#[derive(Clone, Copy, PartialEq, Eq)]
475#[cfg_attr(feature = "defmt", derive(defmt::Format))]
476pub enum CkOutSrc {
477    /// Nothing driven out.
478    None,
479    /// The internal RC oscillator dedicated to the ADC.
480    Irc14m,
481    /// The internal low-speed RC oscillator.
482    Lsi40k,
483    /// The external low-speed crystal.
484    Lxtal,
485    /// The system clock.
486    Sysclk,
487    /// The internal 8 MHz RC oscillator.
488    Irc8m,
489    /// The external high-speed crystal.
490    Hxtal,
491    /// The PLL output, through its own divider.
492    Pll(PllDiv),
493}
494
495/// Divider applied to `CK_OUT` after the source multiplexer, for any source.
496#[derive(Clone, Copy, PartialEq, Eq)]
497#[cfg_attr(feature = "defmt", derive(defmt::Format))]
498#[allow(missing_docs)]
499pub enum CkOutDiv {
500    Div1,
501    Div2,
502    Div4,
503    Div8,
504    Div16,
505    Div32,
506    Div64,
507    Div128,
508}
509
510/// What brought the chip up, as recorded in `RSTSCK`.
511///
512/// Several can stand after a single reset, and nothing clears them but
513/// [`clear_reset_flags`](Rcu::clear_reset_flags).
514#[derive(Clone, Copy, PartialEq, Eq)]
515#[cfg_attr(feature = "defmt", derive(defmt::Format))]
516pub enum ResetFlag {
517    /// Deep-sleep or standby reset.
518    LowPower,
519    /// The window watchdog timed out.
520    WindowWatchdog,
521    /// The free watchdog timed out.
522    FreeWatchdog,
523    /// A software reset was requested.
524    Software,
525    /// Power reset.
526    Power,
527    /// The external reset pin was pulled.
528    ExternalPin,
529    /// The option byte loader ran.
530    OptionByteLoader,
531}
532
533/// Owns the RCU peripheral with its clock tree already frozen; obtained from
534/// [`UnfrozenRcu::freeze`].
535pub struct Rcu {
536    rcu: pac::Rcu,
537    clocks: Clocks,
538}
539
540impl Rcu {
541    /// The frequencies the tree was frozen at.
542    pub fn clocks(&self) -> Clocks {
543        self.clocks
544    }
545
546    /// Routes an internal clock node out onto `PA8` (AF0) or `PA9` (AF5).
547    ///
548    /// The pin still has to be put into the matching alternate function. Applied
549    /// immediately and not recorded in [`Clocks`].
550    pub fn ck_out(&mut self, src: CkOutSrc, div: CkOutDiv) {
551        self.rcu.cfg0().modify(|_, w| {
552            let w = match div {
553                CkOutDiv::Div1 => w.ckoutdiv().div1(),
554                CkOutDiv::Div2 => w.ckoutdiv().div2(),
555                CkOutDiv::Div4 => w.ckoutdiv().div4(),
556                CkOutDiv::Div8 => w.ckoutdiv().div8(),
557                CkOutDiv::Div16 => w.ckoutdiv().div16(),
558                CkOutDiv::Div32 => w.ckoutdiv().div32(),
559                CkOutDiv::Div64 => w.ckoutdiv().div64(),
560                CkOutDiv::Div128 => w.ckoutdiv().div128(),
561            };
562            match src {
563                CkOutSrc::None => w.ckoutsel().none(),
564                CkOutSrc::Irc14m => w.ckoutsel().irc14m(),
565                CkOutSrc::Lsi40k => w.ckoutsel().lsi40k(),
566                CkOutSrc::Lxtal => w.ckoutsel().lxtal(),
567                CkOutSrc::Sysclk => w.ckoutsel().sysclk(),
568                CkOutSrc::Irc8m => w.ckoutsel().irc8m(),
569                CkOutSrc::Hxtal => w.ckoutsel().hxtal(),
570                CkOutSrc::Pll(d) => {
571                    let w = w.ckoutsel().pll();
572                    match d {
573                        PllDiv::Div1 => w.plldv().div1(),
574                        PllDiv::Div2 => w.plldv().div2(),
575                    }
576                }
577            }
578        });
579    }
580
581    /// Starts the internal 40 kHz oscillator and blocks until it is stable.
582    pub fn enable_irc40k(&mut self) {
583        self.rcu.rstsck().modify(|_, w| w.irc40ken().on());
584        while self.rcu.rstsck().read().irc40kstb().is_not_ready() {}
585    }
586    /// Stops the internal 40 kHz oscillator.
587    ///
588    /// FWDGT and the RTC run off it; both stop with it.
589    pub fn disable_irc40k(&mut self) {
590        self.rcu.rstsck().modify(|_, w| w.irc40ken().off());
591    }
592
593    /// Clocks SYSCFG and the comparator.
594    ///
595    /// `CFGCMPEN` gates both blocks at once.
596    pub fn enable_cfgcmp(&mut self) {
597        self.rcu.apb2en().modify(|_, w| w.cfgcmpen().enabled());
598    }
599    /// Stops the clock of SYSCFG and the comparator.
600    ///
601    /// One bit gates both blocks: SYSCFG stops too.
602    pub fn disable_cfgcmp(&mut self) {
603        self.rcu.apb2en().modify(|_, w| w.cfgcmpen().disabled());
604    }
605    /// Pulses the reset line of SYSCFG and the comparator.
606    ///
607    /// One bit resets both blocks: the EXTI source selection and the remaps in
608    /// SYSCFG return to their defaults too.
609    pub fn reset_cfgcmp(&mut self) {
610        self.rcu.apb2rst().modify(|_, w| w.cfgcmprst().reset());
611        self.rcu.apb2rst().modify(|_, w| w.cfgcmprst().clear_bit());
612    }
613
614    /// Whether `flag` took part in the last reset.
615    ///
616    /// Reads the flag as it stands; more than one can answer `true`.
617    pub fn reset_flag(&self, flag: ResetFlag) -> bool {
618        let rstsck = self.rcu.rstsck().read();
619        match flag {
620            ResetFlag::LowPower => rstsck.lprstf().is_reset(),
621            ResetFlag::WindowWatchdog => rstsck.wwdgtrstf().is_reset(),
622            ResetFlag::FreeWatchdog => rstsck.fwdgtrstf().is_reset(),
623            ResetFlag::Software => rstsck.swrstf().is_reset(),
624            ResetFlag::Power => rstsck.porrstf().is_reset(),
625            ResetFlag::ExternalPin => rstsck.eprstf().is_reset(),
626            ResetFlag::OptionByteLoader => rstsck.oblrstf().is_reset(),
627        }
628    }
629    /// Clears every reset flag at once; `RSTFC` has no per-flag granularity.
630    ///
631    /// Until this is called the flags accumulate across resets, so a later
632    /// reading cannot tell which reset set them.
633    pub fn clear_reset_flags(&mut self) {
634        self.rcu.rstsck().modify(|_, w| w.rstfc().clear());
635    }
636}
637
638/// Extension trait turning the raw RCU peripheral into the managed [`UnfrozenRcu`].
639pub trait RcuExt {
640    /// Consumes the raw peripheral and returns the managed wrapper, which
641    /// [`freeze`](UnfrozenRcu::freeze) turns into an [`Rcu`].
642    fn constrain(self) -> UnfrozenRcu;
643}
644
645impl RcuExt for pac::Rcu {
646    fn constrain(self) -> UnfrozenRcu {
647        UnfrozenRcu { rcu: self }
648    }
649}
650
651/// Clock gating for a peripheral, implemented per peripheral type.
652pub trait Enable {
653    /// Switches the peripheral's clock on.
654    fn enable(rcu: &mut Rcu);
655    /// Switches the peripheral's clock off.
656    fn disable(rcu: &mut Rcu);
657}
658
659/// Reset control for a peripheral, implemented per peripheral type.
660pub trait Reset {
661    /// Pulses the peripheral's reset line, returning its registers to defaults.
662    fn reset(rcu: &mut Rcu);
663}
664
665macro_rules! bus_en {
666    ($($Periph:ty => $en_reg:ident, $en_bit:ident,)+) => {
667        $(
668            impl Enable for $Periph {
669                fn enable(rcu: &mut Rcu) {
670                    rcu.rcu.$en_reg().modify(|_, w| w.$en_bit().enabled());
671                }
672                fn disable(rcu: &mut Rcu) {
673                    rcu.rcu.$en_reg().modify(|_, w| w.$en_bit().disabled());
674                }
675            }
676        )+
677    };
678}
679
680macro_rules! bus_rst {
681    ($($Periph:ty => $rst_reg:ident, $rst_bit:ident,)+) => {
682        $(
683            impl Reset for $Periph {
684                fn reset(rcu: &mut Rcu) {
685                    rcu.rcu.$rst_reg().modify(|_, w| w.$rst_bit().reset());
686                    rcu.rcu.$rst_reg().modify(|_, w| w.$rst_bit().clear_bit());
687                }
688            }
689        )+
690    };
691}
692
693bus_en! {
694    pac::Gpioa => ahben, paen,
695    pac::Gpiob => ahben, pben,
696    pac::Gpiof => ahben, pfen,
697    pac::Dma => ahben, dmaen,
698    pac::Crc => ahben, crcen,
699    pac::Usart1 => apb1en, usart1en,
700    pac::Spi1 => apb1en, spi1en,
701    pac::Timer2 => apb1en, timer2en,
702    pac::Timer5 => apb1en, timer5en,
703    pac::Timer13 => apb1en, timer13en,
704    pac::I2c0 => apb1en, i2c0en,
705    pac::I2c1 => apb1en, i2c1en,
706    pac::Wwdgt => apb1en, wwdgten,
707    pac::Usart0 => apb2en, usart0en,
708    pac::Adc => apb2en, adcen,
709    pac::Spi0 => apb2en, spi0en,
710    pac::Timer0 => apb2en, timer0en,
711    pac::Timer15 => apb2en, timer15en,
712    pac::Timer16 => apb2en, timer16en,
713}
714
715// TIMER14 is absent from the 20- and 24-pin parts whatever their flash size, so its
716// gate is the part's own row in build.rs rather than the flash code.
717#[cfg(has_timer14)]
718bus_en! {
719    pac::Timer14 => apb2en, timer14en,
720}
721
722// Port C bonds three pads on the 48-pin package and none anywhere else.
723#[cfg(pads_ge_48)]
724bus_en! {
725    pac::Gpioc => ahben, pcen,
726}
727
728bus_rst! {
729    pac::Gpioa => ahbrst, parst,
730    pac::Gpiob => ahbrst, pbrst,
731    pac::Gpiof => ahbrst, pfrst,
732    pac::Usart1 => apb1rst, usart1rst,
733    pac::Spi1 => apb1rst, spi1rst,
734    pac::Timer2 => apb1rst, timer2rst,
735    pac::Timer5 => apb1rst, timer5rst,
736    pac::Timer13 => apb1rst, timer13rst,
737    pac::I2c0 => apb1rst, i2c0rst,
738    pac::I2c1 => apb1rst, i2c1rst,
739    pac::Wwdgt => apb1rst, wwdgtrst,
740    pac::Usart0 => apb2rst, usart0rst,
741    pac::Adc => apb2rst, adcrst,
742    pac::Spi0 => apb2rst, spi0rst,
743    pac::Timer0 => apb2rst, timer0rst,
744    pac::Timer15 => apb2rst, timer15rst,
745    pac::Timer16 => apb2rst, timer16rst,
746}
747
748#[cfg(has_timer14)]
749bus_rst! {
750    pac::Timer14 => apb2rst, timer14rst,
751}
752
753#[cfg(pads_ge_48)]
754bus_rst! {
755    pac::Gpioc => ahbrst, pcrst,
756}