stm32f4xx/clocks/clocks.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright OxidOS Automotive SRL.
4//
5// Author: Ioan-Cristian CÎRSTEA <ioan.cirstea@oxidos.io>
6
7//! STM32F4xx clock driver
8//!
9//! This crate provides drivers for various clocks: HSI, PLL, system, AHB, APB1 and APB2.
10//! This documentation applies to the system clock, AHB, APB1 and APB2. For in-detail documentation
11//! for HSI and PLL, check their documentation.
12//!
13//! # Features
14//!
15//! - [x] Dynamic system source
16//! - [x] Hardware limits verification for AHB, APB1 and APB2.
17//! - [x] Prescaler configuration for AHB, APB1 and APB2.
18//! - [x] Support for MCO1
19//!
20//! # Limitations
21//!
22//! - [ ] Precision of 1MHz
23//! - [ ] No support for MCO2
24//!
25//! # Usage [^usage_note]
26//!
27//! First, import the following enums:
28//!
29//! ```rust,ignore
30//! // Assuming a STM32F429 chip. Change this to correspond to the chip model.
31//! use stm32f429zi::rcc::APBPrescaler;
32//! use stm32f429zi::rcc::AHBPrescaler;
33//! use stm32f429zi::rcc::SysClockSource;
34//! ```
35//!
36//! A reference to the [crate::clocks::Clocks] is needed:
37//!
38//! ```rust,ignore
39//! // Add this in board main.rs
40//! let clocks = &peripherals.stm32f4.clocks;
41//! ```
42//!
43//! ## Retrieve the AHB frequency:
44//!
45//! ```rust,ignore
46//! let ahb_frequency = clocks.get_ahb_frequency_mhz();
47//! debug!("Current AHB frequency is {}MHz", ahb_frequency);
48//! ```
49//!
50//! ## Retrieve the AHB prescaler:
51//!
52//! ```rust,ignore
53//! let ahb_prescaler = clocks.get_ahb_prescaler();
54//! debug!("Current AHB prescaler is {:?}", ahb_prescaler);
55//! ```
56//!
57//! NOTE: If one wishes to get the usize equivalent value of [crate::clocks::Clocks::get_ahb_prescaler], to use in
58//! computations for example, they must use [crate::rcc::AHBPrescaler].into() method:
59//!
60//! ```rust,ignore
61//! let ahb_prescaler_usize: usize = clocks.get_ahb_prescaler().into();
62//! if ahb_prescaler_usize > 8 {
63//! /* Do something */
64//! }
65//! ```
66//!
67//! ## Set the AHB prescaler
68//!
69//! ```rust,ignore
70//! clocks.set_ahb_prescaler(AHBPrescaler::DivideBy4);
71//! ```
72//!
73//! ## APB1 and APB2 prescalers
74//!
75//! APB1 and APB2 prescalers are configured in a similar way as AHB prescaler, except that the
76//! corresponding enum is APBPrescaler.
77//!
78//! ## Retrieve the system clock frequency:
79//!
80//! ```rust,ignore
81//! let sys_frequency = clocks.get_sys_clock_frequency_mhz();
82//! debug!("Current system clock frequency is {}MHz", sys_frequency);
83//! ```
84//!
85//! ## Retrieve the system clock source:
86//!
87//! ```rust,ignore
88//! let sys_source = clocks.get_sys_clock_source();
89//! debug!("Current system clock source is {:?}", sys_source);
90//! ```
91//!
92//! ## Change the system clock source to PLL:
93//!
94//! Changing the system clock source is a fastidious task because of AHB, APB1 and APB2 limits,
95//! which are chip-dependent. This example assumes a STM32F429 chip.
96//!
97//! First, get a reference to the PLL
98//!
99//! ```rust,ignore
100//! let pll = &peripherals.stm32f4.clocks.pll;
101//! ```
102//!
103//! Then, configure its frequency and enable it
104//! ```rust,ignore
105//! pll.set_frequency_mhz(50);
106//! pll.enable();
107//! ```
108//!
109//! STM32F429 maximum APB1 frequency is 45MHz, which is computed as following:
110//! freq_APB1 = freq_sys / AHB_prescaler / APB1_prescaler
111//! Default prescaler values are 1, which gives an frequency of 50MHz without modifying the
112//! APB1 prescaler. As such, the APB1 prescaler must be changed.
113//!
114//! ```rust,ignore
115//! clocks.set_apb1_prescaler(APBPrescaler::DivideBy2);
116//! ```
117//!
118//! Since the APB1 frequency limit is satisfied now, the system clock source can be safely changed.
119//!
120//! ```rust,ignore
121//! clocks.set_sys_clock_source(SysClockSource::PLL);
122//! ```
123//!
124//! ## Another example of changing the system clock to PLL for STM32F429:
125//!
126//! As before, Pll clock is configured and enabled.
127//!
128//! ```rust,ignore
129//! pll.set_frequency_mhz(100);
130//! pll.enable();
131//! ```
132//!
133//! Because of the high frequency of the PLL clock, both APB1 and APB2 prescalers must be
134//! configured.
135//!
136//! ```rust,ignore
137//! clocks.set_apb1_prescaler(APBPrescaler::DivideBy4);
138//! clocks.set_apb2_prescaler(APBPrescaler::DivideBy2);
139//! ```
140//!
141//! As an alternative, the AHB prescaler could be configured to change both APB1 and APB2
142//! frequencies.
143//!
144//! ```rust,ignore
145//! // Changing it to 2 wouldn't work, because it would give a frequency of 50MHz for the APB1.
146//! clocks.set_ahb_prescaler(APBPrescaler::DivideBy4);
147//! ```
148//!
149//! Now, it's safe to change the system clock source:
150//!
151//! ```rust,ignore
152//! clocks.set_sys_clock_source(SysClockSource::PLL);
153//! ```
154//!
155//! [^usage_note]: For the purpose of brevity, any error checking has been removed.
156
157use crate::chip_specific::ChipSpecs as ChipSpecsTrait;
158use crate::clocks::hse::Hse;
159use crate::clocks::hsi::Hsi;
160use crate::clocks::hsi::HSI_FREQUENCY_MHZ;
161use crate::clocks::pll::Pll;
162use crate::flash::Flash;
163use crate::rcc::AHBPrescaler;
164use crate::rcc::APBPrescaler;
165use crate::rcc::MCO1Divider;
166use crate::rcc::MCO1Source;
167use crate::rcc::PllSource;
168use crate::rcc::Rcc;
169use crate::rcc::SysClockSource;
170
171use kernel::debug;
172use kernel::utilities::cells::OptionalCell;
173use kernel::ErrorCode;
174
175/// Main struct for configuring on-board clocks.
176pub struct Clocks<'a, ChipSpecs> {
177 rcc: &'a Rcc,
178 flash: OptionalCell<&'a Flash<ChipSpecs>>,
179 /// High speed internal clock
180 pub hsi: Hsi<'a>,
181 /// High speed external clock
182 pub hse: Hse<'a>,
183 /// Main phase loop-lock clock
184 pub pll: Pll<'a, ChipSpecs>,
185}
186
187impl<'a, ChipSpecs: ChipSpecsTrait> Clocks<'a, ChipSpecs> {
188 // The constructor must be called when the default peripherals are created
189 pub fn new(rcc: &'a Rcc) -> Self {
190 Self {
191 rcc,
192 flash: OptionalCell::empty(),
193 hsi: Hsi::new(rcc),
194 hse: Hse::new(rcc),
195 pll: Pll::new(rcc),
196 }
197 }
198
199 // This method should be called when the dependencies are resolved
200 pub(crate) fn set_flash(&self, flash: &'a Flash<ChipSpecs>) {
201 self.flash.set(flash);
202 }
203
204 /// Set the AHB prescaler
205 ///
206 /// AHB bus, core, memory, DMA, Cortex System timer and FCLK Cortex free-running clock
207 /// frequencies are equal to the system clock frequency divided by the AHB prescaler.
208 ///
209 /// # Errors:
210 ///
211 /// + [Err]\([ErrorCode::FAIL]\) if changing the AHB prescaler doesn't preserve APB frequency
212 /// constraints
213 /// + [Err]\([ErrorCode::BUSY]\) if changing the AHB prescaler took too long. Retry.
214 pub fn set_ahb_prescaler(&self, prescaler: AHBPrescaler) -> Result<(), ErrorCode> {
215 // Changing the AHB prescaler affects the APB frequencies. A check must be done to ensure
216 // that the constraints are still valid
217 let divider: usize = prescaler.into();
218 let new_ahb_frequency = self.get_sys_clock_frequency_mhz() / divider;
219 if !self.check_apb1_frequency_limit(new_ahb_frequency)
220 || !self.check_apb2_frequency_limit(new_ahb_frequency)
221 {
222 return Err(ErrorCode::FAIL);
223 }
224
225 self.rcc.set_ahb_prescaler(prescaler);
226
227 for _ in 0..16 {
228 if self.get_ahb_prescaler() == prescaler {
229 return Ok(());
230 }
231 }
232
233 Err(ErrorCode::BUSY)
234 }
235
236 /// Get the current configured AHB prescaler
237 pub fn get_ahb_prescaler(&self) -> AHBPrescaler {
238 self.rcc.get_ahb_prescaler()
239 }
240
241 /// Get the frequency of the AHB
242 pub fn get_ahb_frequency_mhz(&self) -> usize {
243 let ahb_divider: usize = self.get_ahb_prescaler().into();
244 self.get_sys_clock_frequency_mhz() / ahb_divider
245 }
246
247 // APB1 frequency must not be higher than the maximum allowable frequency. This method is
248 // called when the system clock source is changed. The ahb_frequency_mhz is the
249 // hypothetical future frequency.
250 fn check_apb1_frequency_limit(&self, ahb_frequency_mhz: usize) -> bool {
251 ahb_frequency_mhz
252 <= ChipSpecs::APB1_FREQUENCY_LIMIT_MHZ
253 * Into::<usize>::into(self.rcc.get_apb1_prescaler())
254 }
255
256 /// Set the APB1 prescaler.
257 ///
258 /// The APB1 peripheral clock frequency is equal to the AHB frequency divided by the APB1
259 /// prescaler.
260 ///
261 /// # Errors:
262 ///
263 /// + [Err]\([ErrorCode::FAIL]\) if the desired prescaler would break the APB1 frequency limit
264 /// + [Err]\([ErrorCode::BUSY]\) if setting the prescaler took too long. Retry.
265 pub fn set_apb1_prescaler(&self, prescaler: APBPrescaler) -> Result<(), ErrorCode> {
266 let ahb_frequency = self.get_ahb_frequency_mhz();
267 let divider: usize = prescaler.into();
268 if ahb_frequency / divider > ChipSpecs::APB1_FREQUENCY_LIMIT_MHZ {
269 return Err(ErrorCode::FAIL);
270 }
271
272 self.rcc.set_apb1_prescaler(prescaler);
273
274 for _ in 0..16 {
275 if self.rcc.get_apb1_prescaler() == prescaler {
276 return Ok(());
277 }
278 }
279
280 Err(ErrorCode::BUSY)
281 }
282
283 /// Get the current configured APB1 prescaler
284 pub fn get_apb1_prescaler(&self) -> APBPrescaler {
285 self.rcc.get_apb1_prescaler()
286 }
287
288 /// Get the current APB1 frequency
289 pub fn get_apb1_frequency_mhz(&self) -> usize {
290 // Every enum variant can be converted into a usize
291 let divider: usize = self.rcc.get_apb1_prescaler().into();
292 self.get_ahb_frequency_mhz() / divider
293 }
294
295 // Same as for APB1, APB2 has a frequency limit that must be enforced by software
296 fn check_apb2_frequency_limit(&self, ahb_frequency_mhz: usize) -> bool {
297 ahb_frequency_mhz
298 <= ChipSpecs::APB2_FREQUENCY_LIMIT_MHZ
299 * Into::<usize>::into(self.rcc.get_apb2_prescaler())
300 }
301
302 /// Set the APB2 prescaler.
303 ///
304 /// The APB2 peripheral clock frequency is equal to the AHB frequency divided by the APB2
305 /// prescaler.
306 ///
307 /// # Errors:
308 ///
309 /// + [Err]\([ErrorCode::FAIL]\) if the desired prescaler would break the APB2 frequency limit
310 /// + [Err]\([ErrorCode::BUSY]\) if setting the prescaler took too long. Retry.
311 pub fn set_apb2_prescaler(&self, prescaler: APBPrescaler) -> Result<(), ErrorCode> {
312 let current_ahb_frequency = self.get_ahb_frequency_mhz();
313 let divider: usize = prescaler.into();
314 if current_ahb_frequency / divider > ChipSpecs::APB2_FREQUENCY_LIMIT_MHZ {
315 return Err(ErrorCode::FAIL);
316 }
317
318 self.rcc.set_apb2_prescaler(prescaler);
319
320 for _ in 0..16 {
321 if self.rcc.get_apb2_prescaler() == prescaler {
322 return Ok(());
323 }
324 }
325
326 Err(ErrorCode::BUSY)
327 }
328
329 /// Get the current configured APB2 prescaler
330 pub fn get_apb2_prescaler(&self) -> APBPrescaler {
331 self.rcc.get_apb2_prescaler()
332 }
333
334 /// Get the current APB2 frequency
335 pub fn get_apb2_frequency_mhz(&self) -> usize {
336 // Every enum variant can be converted into a usize
337 let divider: usize = self.rcc.get_apb2_prescaler().into();
338 self.get_ahb_frequency_mhz() / divider
339 }
340
341 /// Set the system clock source
342 ///
343 /// # Errors:
344 ///
345 /// + [Err]\([ErrorCode::FAIL]\) if the source is not enabled.
346 /// + [Err]\([ErrorCode::SIZE]\) if the source frequency surpasses the system clock frequency
347 /// limit, or the APB1 and APB2 limits are not satisfied.
348 /// + [Err]\([ErrorCode::BUSY]\) if the source switching took too long. Retry.
349 pub fn set_sys_clock_source(&self, source: SysClockSource) -> Result<(), ErrorCode> {
350 // Immediately return if the required source is already configured as the system clock
351 // source. Should this maybe be Err(ErrorCode::ALREADY)?
352 if source == self.get_sys_clock_source() {
353 return Ok(());
354 }
355
356 // Ensure the source is enabled before configuring it as the system clock source
357 if let false = match source {
358 SysClockSource::HSI => self.hsi.is_enabled(),
359 SysClockSource::HSE => self.hse.is_enabled(),
360 SysClockSource::PLL => self.pll.is_enabled(),
361 } {
362 return Err(ErrorCode::FAIL);
363 }
364
365 let current_frequency = self.get_sys_clock_frequency_mhz();
366
367 // Get the frequency of the source to be configured
368 let alternate_frequency = match source {
369 // The unwrap can't fail because the source clock status was checked before
370 SysClockSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
371 SysClockSource::HSE => self.hse.get_frequency_mhz().unwrap(),
372 SysClockSource::PLL => self.pll.get_frequency_mhz().unwrap(),
373 };
374
375 // Check the alternate frequency is not higher than the system clock limit
376 if alternate_frequency > ChipSpecs::SYS_CLOCK_FREQUENCY_LIMIT_MHZ {
377 return Err(ErrorCode::SIZE);
378 }
379
380 // Retrieve the currently configured AHB prescaler
381 let ahb_divider: usize = self.get_ahb_prescaler().into();
382 // Compute the possible future AHB frequency
383 let ahb_frequency = alternate_frequency / ahb_divider;
384
385 // APB1 frequency must not exceed APB1_FREQUENCY_LIMIT_MHZ
386 if !self.check_apb1_frequency_limit(ahb_frequency) {
387 return Err(ErrorCode::SIZE);
388 }
389
390 // APB2 frequency must not exceed APB2_FREQUENCY_LIMIT_MHZ
391 if !self.check_apb2_frequency_limit(ahb_frequency) {
392 return Err(ErrorCode::SIZE);
393 }
394
395 // The documentation recommends the following sequence when changing the system clock
396 // frequency:
397 //
398 // + if the desired frequency is higher than the current frequency, first change flash
399 // latency, then set the new system clock source.
400 // + if the desired frequency is lower than the current frequency, first change the system
401 // clock source, then set the flash latency
402 if alternate_frequency > current_frequency {
403 self.flash
404 .unwrap_or_panic()
405 .set_latency(alternate_frequency)?;
406 }
407 self.rcc.set_sys_clock_source(source);
408 if alternate_frequency < current_frequency {
409 self.flash
410 .unwrap_or_panic()
411 .set_latency(alternate_frequency)?;
412 }
413
414 // If this point is reached, everything worked as expected
415 Ok(())
416 }
417
418 /// Get the current system clock source
419 pub fn get_sys_clock_source(&self) -> SysClockSource {
420 self.rcc.get_sys_clock_source()
421 }
422
423 /// Get the current system clock frequency in MHz
424 pub fn get_sys_clock_frequency_mhz(&self) -> usize {
425 match self.get_sys_clock_source() {
426 // These unwraps can't panic because set_sys_clock_frequency ensures that the source is
427 // enabled. Also, Hsi and Pll structs ensure that the clocks can't be disabled when
428 // they are configured as the system clock
429 SysClockSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
430 SysClockSource::HSE => self.hse.get_frequency_mhz().unwrap(),
431 SysClockSource::PLL => self.pll.get_frequency_mhz().unwrap(),
432 }
433 }
434
435 /// Get the current system clock frequency in MHz from RCC registers instead of the cached
436 /// value. Used for debug only.
437 pub fn _get_sys_clock_frequency_mhz_no_cache(&self) -> usize {
438 match self.get_sys_clock_source() {
439 // These unwraps can't panic because set_sys_clock_frequency ensures that the source is
440 // enabled. Also, Hsi and Pll structs ensure that the clocks can't be disabled when
441 // they are configured as the system clock
442 SysClockSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
443 SysClockSource::HSE => self.hse.get_frequency_mhz().unwrap(),
444 SysClockSource::PLL => {
445 let pll_source_frequency = match self.rcc.get_pll_clocks_source() {
446 PllSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
447 PllSource::HSE => self.hse.get_frequency_mhz().unwrap(),
448 };
449 self.pll
450 .get_frequency_mhz_no_cache(pll_source_frequency)
451 .unwrap()
452 }
453 }
454 }
455
456 /// Set the frequency of the PLL clock.
457 ///
458 /// # Parameters
459 ///
460 /// + pll_source: PLL source clock (HSI or HSE)
461 ///
462 /// + desired_frequency_mhz: the desired frequency in MHz. Supported values: 24-216MHz for
463 /// STM32F401 and 13-216MHz for all the other chips
464 ///
465 /// # Errors
466 ///
467 /// + [Err]\([ErrorCode::INVAL]\): if the desired frequency can't be achieved
468 /// + [Err]\([ErrorCode::FAIL]\): if the PLL clock is already enabled. It must be disabled before
469 pub fn set_pll_frequency_mhz(
470 &self,
471 pll_source: PllSource,
472 desired_frequency_mhz: usize,
473 ) -> Result<(), ErrorCode> {
474 let source_frequency = match pll_source {
475 PllSource::HSI => HSI_FREQUENCY_MHZ,
476 PllSource::HSE => self.hse.get_frequency_mhz().unwrap(),
477 };
478 self.pll
479 .set_frequency_mhz(pll_source, source_frequency, desired_frequency_mhz)
480 }
481
482 /// Set the clock source for the microcontroller clock output 1 (MCO1)
483 ///
484 /// # Errors:
485 ///
486 /// + [Err]\([ErrorCode::FAIL]\) if the source apart from HSI is already enabled.
487 pub fn set_mco1_clock_source(&self, source: MCO1Source) -> Result<(), ErrorCode> {
488 match source {
489 MCO1Source::HSE => {
490 if !self.hse.is_enabled() {
491 return Err(ErrorCode::FAIL);
492 }
493 }
494 MCO1Source::PLL => {
495 if self.pll.is_enabled() {
496 return Err(ErrorCode::FAIL);
497 }
498 }
499 _ => (),
500 }
501
502 self.rcc.set_mco1_clock_source(source);
503
504 Ok(())
505 }
506
507 /// Get the clock source of the MCO1
508 pub fn get_mco1_clock_source(&self) -> MCO1Source {
509 self.rcc.get_mco1_clock_source()
510 }
511
512 /// Set MCO1 divider
513 ///
514 /// # Errors:
515 ///
516 /// + [Err]\([ErrorCode::FAIL]\) if the configured source apart from HSI is already enabled.
517 pub fn set_mco1_clock_divider(&self, divider: MCO1Divider) -> Result<(), ErrorCode> {
518 match self.get_mco1_clock_source() {
519 MCO1Source::PLL => {
520 if self.pll.is_enabled() {
521 return Err(ErrorCode::FAIL);
522 }
523 }
524 MCO1Source::HSI => (),
525 MCO1Source::HSE => (),
526 }
527
528 self.rcc.set_mco1_clock_divider(divider);
529
530 Ok(())
531 }
532
533 /// Get MCO1 divider
534 pub fn get_mco1_clock_divider(&self) -> MCO1Divider {
535 self.rcc.get_mco1_clock_divider()
536 }
537}
538
539/// Stm32f4Clocks trait
540///
541/// This can be used to control clocks without the need to keep a reference of the chip specific
542/// Clocks struct, for instance by peripherals
543pub trait Stm32f4Clocks {
544 /// Get RCC instance
545 fn get_rcc(&self) -> &Rcc;
546
547 /// Get current AHB clock (HCLK) frequency in Hz
548 fn get_ahb_frequency(&self) -> usize;
549
550 // Extend this to expose additional clock resources
551}
552
553impl<'a, ChipSpecs: ChipSpecsTrait> Stm32f4Clocks for Clocks<'a, ChipSpecs> {
554 fn get_rcc(&self) -> &'a Rcc {
555 self.rcc
556 }
557
558 fn get_ahb_frequency(&self) -> usize {
559 self.get_ahb_frequency_mhz() * 1_000_000
560 }
561}
562
563/// Tests for clocks functionalities
564///
565/// These tests ensure the clocks are properly working. If any changes are made to the clock
566/// module, make sure to run these tests.
567///
568/// # Usage
569///
570/// First, import the [crate::clocks] module inside the board main file:
571///
572/// ```rust,ignore
573/// // This example assumes a STM32F429 chip
574/// use stm32f429zi::clocks;
575/// ```
576///
577/// To run all the available tests, add this line before **kernel::process::load_processes()**:
578///
579/// ```rust,ignore
580/// clocks::tests::run_all(&peripherals.stm32f4.clocks);
581/// ```
582///
583/// If everything works as expected, the following message should be printed on the kernel console:
584///
585/// ```text
586/// ===============================================
587/// Testing clocks...
588///
589/// ===============================================
590/// Testing HSI...
591/// Finished testing HSI. Everything is alright!
592/// ===============================================
593///
594///
595/// ===============================================
596/// Testing PLL...
597/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
598/// Testing PLL configuration...
599/// Finished testing PLL configuration.
600/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
601/// Testing PLL struct...
602/// Finished testing PLL struct.
603/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
604/// Finished testing PLL. Everything is alright!
605/// ===============================================
606///
607///
608/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
609/// Testing clocks struct...
610/// Finished testing clocks struct. Everything is alright!
611/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
612///
613/// Finished testing clocks. Everything is alright!
614/// ===============================================
615/// ```
616///
617/// There is also the possibility to run a part of the test suite. Check the functions present in
618/// this module for more details.
619///
620/// # Errors
621///
622/// If there are any errors, open an issue ticket at <https://github.com/tock/tock>. Please provide the
623/// output of the test execution.
624pub mod tests {
625 use super::{
626 debug, AHBPrescaler, APBPrescaler, ChipSpecsTrait, Clocks, ErrorCode, MCO1Divider,
627 MCO1Source, PllSource, SysClockSource, HSI_FREQUENCY_MHZ,
628 };
629
630 const LOW_FREQUENCY: usize = 25;
631 #[cfg(not(any(
632 feature = "stm32f401",
633 feature = "stm32f410",
634 feature = "stm32f411",
635 feature = "stm32f412",
636 feature = "stm32f413",
637 feature = "stm32f423"
638 )))]
639 const HIGH_FREQUENCY: usize = 112;
640 #[cfg(any(
641 feature = "stm32f401",
642 feature = "stm32f410",
643 feature = "stm32f411",
644 feature = "stm32f412",
645 feature = "stm32f413",
646 feature = "stm32f423"
647 ))]
648 const HIGH_FREQUENCY: usize = 80;
649
650 fn set_default_configuration<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
651 assert_eq!(Ok(()), clocks.set_sys_clock_source(SysClockSource::HSI));
652 assert_eq!(Ok(()), clocks.pll.disable());
653 assert_eq!(Ok(()), clocks.set_ahb_prescaler(AHBPrescaler::DivideBy1));
654 assert_eq!(Ok(()), clocks.set_apb1_prescaler(APBPrescaler::DivideBy1));
655 assert_eq!(Ok(()), clocks.set_apb2_prescaler(APBPrescaler::DivideBy1));
656 assert_eq!(HSI_FREQUENCY_MHZ, clocks.get_sys_clock_frequency_mhz());
657 assert_eq!(HSI_FREQUENCY_MHZ, clocks.get_apb1_frequency_mhz());
658 assert_eq!(HSI_FREQUENCY_MHZ, clocks.get_apb2_frequency_mhz());
659 }
660
661 // This macro ensure that the system clock frequency goes back to the default value to prevent
662 // changing the UART baud rate
663 macro_rules! check_and_panic {
664 ($left:expr, $right:expr, $clocks: ident) => {
665 match (&$left, &$right) {
666 (left_val, right_val) => {
667 if *left_val != *right_val {
668 set_default_configuration($clocks);
669 assert_eq!($left, $right);
670 }
671 }
672 };
673 };
674 }
675
676 /// Test for the AHB and APB prescalers
677 ///
678 /// # Usage
679 ///
680 /// First, import the clock module:
681 ///
682 /// ```rust,ignore
683 /// // This test assumes a STM32F429 chip
684 /// use stm32f429zi::clocks;
685 /// ```
686 ///
687 /// Then run the test:
688 ///
689 /// ```rust,ignore
690 /// clocks::test::test_prescalers(&peripherals.stm32f4.clocks);
691 /// ```
692 pub fn test_prescalers<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
693 // This test requires a bit of setup. A system clock running at HIGH_FREQUENCY is configured.
694 check_and_panic!(
695 Ok(()),
696 clocks
697 .pll
698 .set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, HIGH_FREQUENCY),
699 clocks
700 );
701 check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
702 check_and_panic!(
703 Ok(()),
704 clocks.set_apb1_prescaler(APBPrescaler::DivideBy4),
705 clocks
706 );
707 check_and_panic!(
708 Ok(()),
709 clocks.set_apb2_prescaler(APBPrescaler::DivideBy2),
710 clocks
711 );
712 check_and_panic!(
713 Ok(()),
714 clocks.set_sys_clock_source(SysClockSource::PLL),
715 clocks
716 );
717
718 // Trying to reduce the APB scaler to an invalid value should fail
719 check_and_panic!(
720 Err(ErrorCode::FAIL),
721 clocks.set_apb1_prescaler(APBPrescaler::DivideBy1),
722 clocks
723 );
724 // The following assert will pass on these models because of the low system clock
725 // frequency limit
726 #[cfg(not(any(
727 feature = "stm32f401",
728 feature = "stm32f410",
729 feature = "stm32f411",
730 feature = "stm32f412",
731 feature = "stm32f413",
732 feature = "stm32f423"
733 )))]
734 check_and_panic!(
735 Err(ErrorCode::FAIL),
736 clocks.set_apb2_prescaler(APBPrescaler::DivideBy1),
737 clocks
738 );
739 // Any failure in changing the APB prescalers must preserve their values
740 check_and_panic!(APBPrescaler::DivideBy4, clocks.get_apb1_prescaler(), clocks);
741 check_and_panic!(APBPrescaler::DivideBy2, clocks.get_apb2_prescaler(), clocks);
742
743 // Increasing the AHB prescaler should allow decreasing APB prescalers
744 check_and_panic!(
745 Ok(()),
746 clocks.set_ahb_prescaler(AHBPrescaler::DivideBy4),
747 clocks
748 );
749 check_and_panic!(
750 Ok(()),
751 clocks.set_apb1_prescaler(APBPrescaler::DivideBy1),
752 clocks
753 );
754 check_and_panic!(
755 Ok(()),
756 clocks.set_apb2_prescaler(APBPrescaler::DivideBy1),
757 clocks
758 );
759
760 // Now, decreasing the AHB prescaler would result in the violation of APB constraints
761 check_and_panic!(
762 Err(ErrorCode::FAIL),
763 clocks.set_ahb_prescaler(AHBPrescaler::DivideBy1),
764 clocks
765 );
766 // Any failure in changing the AHB prescaler must preserve its value
767 check_and_panic!(AHBPrescaler::DivideBy4, clocks.get_ahb_prescaler(), clocks);
768
769 // Revert to default configuration
770 set_default_configuration(clocks);
771 }
772
773 /// Test for the [crate::clocks::Clocks] struct
774 ///
775 /// # Usage
776 ///
777 /// First, import the clock module:
778 ///
779 /// ```rust,ignore
780 /// // This test assumes a STM32F429 chip
781 /// use stm32f429zi::clocks;
782 /// ```
783 ///
784 /// Then run the test:
785 ///
786 /// ```rust,ignore
787 /// clocks::test::test_clocks_struct(&peripherals.stm32f4.clocks);
788 /// ```
789 pub fn test_clocks_struct<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
790 debug!("");
791 debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
792 debug!("Testing clocks struct...");
793
794 // By default, the HSI clock is the system clock
795 check_and_panic!(SysClockSource::HSI, clocks.get_sys_clock_source(), clocks);
796
797 // HSI frequency is 16MHz
798 check_and_panic!(
799 HSI_FREQUENCY_MHZ,
800 clocks.get_sys_clock_frequency_mhz(),
801 clocks
802 );
803
804 // APB1 default prescaler is 1
805 check_and_panic!(APBPrescaler::DivideBy1, clocks.get_apb1_prescaler(), clocks);
806
807 // APB1 default frequency is 16MHz
808 check_and_panic!(HSI_FREQUENCY_MHZ, clocks.get_apb1_frequency_mhz(), clocks);
809
810 // APB2 default prescaler is 1
811 check_and_panic!(APBPrescaler::DivideBy1, clocks.get_apb1_prescaler(), clocks);
812
813 // APB2 default frequency is 16MHz
814 check_and_panic!(HSI_FREQUENCY_MHZ, clocks.get_apb2_frequency_mhz(), clocks);
815
816 // Attempting to change the system clock source with a disabled source
817 check_and_panic!(
818 Err(ErrorCode::FAIL),
819 clocks.set_sys_clock_source(SysClockSource::PLL),
820 clocks
821 );
822
823 // Attempting to set twice the same system clock source is fine
824 check_and_panic!(
825 Ok(()),
826 clocks.set_sys_clock_source(SysClockSource::HSI),
827 clocks
828 );
829
830 // Change the system clock source to a low frequency so that APB prescalers don't need to be
831 // changed
832 check_and_panic!(
833 Ok(()),
834 clocks
835 .pll
836 .set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, LOW_FREQUENCY),
837 clocks
838 );
839 check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
840 check_and_panic!(
841 Ok(()),
842 clocks.set_sys_clock_source(SysClockSource::PLL),
843 clocks
844 );
845 check_and_panic!(SysClockSource::PLL, clocks.get_sys_clock_source(), clocks);
846
847 // Now the system clock frequency is equal to 25MHz
848 check_and_panic!(LOW_FREQUENCY, clocks.get_sys_clock_frequency_mhz(), clocks);
849
850 // APB1 and APB2 frequencies must also be 25MHz
851 check_and_panic!(LOW_FREQUENCY, clocks.get_apb1_frequency_mhz(), clocks);
852 check_and_panic!(LOW_FREQUENCY, clocks.get_apb2_frequency_mhz(), clocks);
853
854 // Attempting to disable PLL when it is configured as the system clock must fail
855 check_and_panic!(Err(ErrorCode::FAIL), clocks.pll.disable(), clocks);
856 // Same for the HSI since it is used indirectly as a system clock through PLL
857 check_and_panic!(Err(ErrorCode::FAIL), clocks.hsi.disable(), clocks);
858
859 // Revert to default system clock configuration
860 set_default_configuration(clocks);
861
862 // Attempting to change the system clock frequency without correctly configuring the APB1
863 // prescaler (freq_APB1 <= APB1_FREQUENCY_LIMIT_MHZ) and APB2 prescaler
864 // (freq_APB2 <= APB2_FREQUENCY_LIMIT_MHZ) must fail
865 check_and_panic!(Ok(()), clocks.pll.disable(), clocks);
866 check_and_panic!(
867 Ok(()),
868 clocks
869 .pll
870 .set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, HIGH_FREQUENCY),
871 clocks
872 );
873 check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
874 check_and_panic!(
875 Err(ErrorCode::SIZE),
876 clocks.set_sys_clock_source(SysClockSource::PLL),
877 clocks
878 );
879
880 // Even if the APB1 prescaler is changed to 2, it must fail
881 // (HIGH_FREQUENCY / 2 > APB1_FREQUENCY_LIMIT_MHZ)
882 check_and_panic!(
883 Ok(()),
884 clocks.set_apb1_prescaler(APBPrescaler::DivideBy2),
885 clocks
886 );
887 #[cfg(not(any(
888 feature = "stm32f401",
889 feature = "stm32f410",
890 feature = "stm32f411",
891 feature = "stm32f412",
892 feature = "stm32f413",
893 feature = "stm32f423"
894 )))]
895 check_and_panic!(
896 Err(ErrorCode::SIZE),
897 clocks.set_sys_clock_source(SysClockSource::PLL),
898 clocks
899 );
900
901 // Configuring APB1 prescaler to 4 is fine, but APB2 prescaler is still wrong
902 check_and_panic!(
903 Ok(()),
904 clocks.set_apb1_prescaler(APBPrescaler::DivideBy4),
905 clocks
906 );
907 #[cfg(not(any(
908 feature = "stm32f401",
909 feature = "stm32f410",
910 feature = "stm32f411",
911 feature = "stm32f412",
912 feature = "stm32f413",
913 feature = "stm32f423"
914 )))]
915 check_and_panic!(
916 Err(ErrorCode::SIZE),
917 clocks.set_sys_clock_source(SysClockSource::PLL),
918 clocks
919 );
920
921 // Configuring APB2 prescaler to 2
922 check_and_panic!(
923 Ok(()),
924 clocks.set_apb2_prescaler(APBPrescaler::DivideBy2),
925 clocks
926 );
927
928 // Now the system clock source can be changed
929 check_and_panic!(
930 Ok(()),
931 clocks.set_sys_clock_source(SysClockSource::PLL),
932 clocks
933 );
934 check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_apb1_frequency_mhz(), clocks);
935 check_and_panic!(HIGH_FREQUENCY / 2, clocks.get_apb2_frequency_mhz(), clocks);
936
937 // Revert to default system clock configuration
938 set_default_configuration(clocks);
939
940 // This time, configure the AHB prescaler instead of APB prescalers
941 check_and_panic!(
942 Ok(()),
943 clocks.set_ahb_prescaler(AHBPrescaler::DivideBy4),
944 clocks
945 );
946 check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
947 check_and_panic!(
948 Ok(()),
949 clocks.set_sys_clock_source(SysClockSource::PLL),
950 clocks
951 );
952 check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_ahb_frequency_mhz(), clocks);
953 check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_apb1_frequency_mhz(), clocks);
954 check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_apb2_frequency_mhz(), clocks);
955
956 // Revert to default configuration
957 set_default_configuration(clocks);
958
959 debug!("Finished testing clocks struct. Everything is alright!");
960 debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
961 debug!("");
962 }
963
964 /// Test for the microcontroller clock outputs
965 ///
966 /// # Usage
967 ///
968 /// First, import the clock module:
969 ///
970 /// ```rust,ignore
971 /// // This test assumes a STM32F429 chip
972 /// use stm32f429zi::clocks;
973 /// ```
974 ///
975 /// Then run the test:
976 ///
977 /// ```rust,ignore
978 /// clocks::test::test_mco(&peripherals.stm32f4.clocks);
979 /// ```
980 pub fn test_mco<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
981 debug!("");
982 debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
983 debug!("Testing MCOs...");
984
985 // Set MCO1 source to PLL
986 assert_eq!(Ok(()), clocks.set_mco1_clock_source(MCO1Source::PLL));
987
988 // Set MCO1 divider to 3
989 assert_eq!(
990 Ok(()),
991 clocks.set_mco1_clock_divider(MCO1Divider::DivideBy3)
992 );
993
994 // Enable PLL
995 assert_eq!(Ok(()), clocks.pll.enable());
996
997 // Attempting to change the divider while the PLL is running must fail
998 assert_eq!(
999 Err(ErrorCode::FAIL),
1000 clocks.set_mco1_clock_divider(MCO1Divider::DivideBy2)
1001 );
1002
1003 // Switch back to HSI
1004 assert_eq!(Ok(()), clocks.set_mco1_clock_source(MCO1Source::HSI));
1005
1006 // Attempting to change the source to PLL when it is already enabled must fail
1007 assert_eq!(
1008 Err(ErrorCode::FAIL),
1009 clocks.set_mco1_clock_source(MCO1Source::PLL)
1010 );
1011
1012 debug!("Finished testing MCOs. Everything is alright!");
1013 debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
1014 debug!("");
1015 }
1016
1017 /// Run the entire test suite for all clocks
1018 pub fn run_all<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
1019 debug!("");
1020 debug!("===============================================");
1021 debug!("Testing clocks...");
1022
1023 crate::clocks::hsi::tests::run(&clocks.hsi);
1024 crate::clocks::pll::tests::run(&clocks.pll);
1025 test_prescalers(clocks);
1026 test_clocks_struct(clocks);
1027 test_mco(clocks);
1028
1029 debug!("Finished testing clocks. Everything is alright!");
1030 debug!("===============================================");
1031 debug!("");
1032 }
1033}