stm32f4xx/clocks/hse.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 Tock Contributors 2022.
4
5//! HSE (high-speed external) clock driver for the STM32F4xx family. [^doc_ref]
6//!
7//! # Usage
8//!
9//! For the purposes of brevity, any error checking has been removed. In real applications, always
10//! check the return values of the [Hse] methods.
11//!
12//! First, get a reference to the [Hse] struct:
13//! ```rust,ignore
14//! let hse = &base_peripherals.clocks.hse;
15//! ```
16//!
17//! ## Start the clock
18//!
19//! ```rust,ignore
20//! hse.enable(stm32f429zi::rcc::HseMode::BYPASS);
21//! ```
22//!
23//! ## Set the clock frequency
24//! ```rust,ignore
25//! hse.set_frequency_mhz(8);
26//! ```
27//!
28//! ## Stop the clock
29//!
30//! ```rust,ignore
31//! hse.disable();
32//! ```
33//!
34//! ## Check if the clock is enabled
35//! ```rust,ignore
36//! if hse.is_enabled() {
37//! /* Do something */
38//! } else {
39//! /* Do something */
40//! }
41//! ```
42//!
43//! ## Get the frequency of the clock
44//! ```rust,ignore
45//! let hse_frequency_mhz = hse.get_frequency_mhz().unwrap();
46//! ```
47//!
48//! [^doc_ref]: See 6.2.1 in the documentation.
49
50use crate::rcc::HseMode;
51use crate::rcc::Rcc;
52
53use kernel::debug;
54use kernel::utilities::cells::OptionalCell;
55use kernel::ErrorCode;
56
57/// Main HSE clock structure
58pub struct Hse<'a> {
59 rcc: &'a Rcc,
60 hse_frequency_mhz: OptionalCell<usize>,
61}
62
63impl<'a> Hse<'a> {
64 /// Create a new instance of the HSE clock.
65 ///
66 /// # Parameters
67 ///
68 /// + rcc: an instance of [crate::rcc]
69 ///
70 /// # Returns
71 ///
72 /// An instance of the HSE clock.
73 pub(in crate::clocks) fn new(rcc: &'a Rcc) -> Self {
74 Self {
75 rcc,
76 hse_frequency_mhz: OptionalCell::empty(),
77 }
78 }
79
80 /// Start the HSE clock.
81 ///
82 /// # Errors
83 ///
84 /// + [Err]\([ErrorCode::BUSY]\): disabling the HSE clock took to long. Retry to ensure it is running
85 pub fn enable(&self, source: HseMode) -> Result<(), ErrorCode> {
86 if source == HseMode::BYPASS {
87 self.rcc.enable_hse_clock_bypass();
88 }
89
90 self.rcc.enable_hse_clock();
91
92 for _ in 0..100 {
93 if self.rcc.is_ready_hse_clock() {
94 return Ok(());
95 }
96 }
97
98 Err(ErrorCode::BUSY)
99 }
100
101 /// Stop the HSE clock.
102 ///
103 /// # Errors
104 ///
105 /// + [Err]\([ErrorCode::FAIL]\): if the HSE clock is configured as the system clock.
106 /// + [Err]\([ErrorCode::BUSY]\): disabling the HSE clock took to long. Retry to ensure it is
107 /// not running.
108 pub fn disable(&self) -> Result<(), ErrorCode> {
109 if self.rcc.is_hse_clock_system_clock() {
110 return Err(ErrorCode::FAIL);
111 }
112
113 self.rcc.disable_hse_clock();
114
115 for _ in 0..10 {
116 if !self.rcc.is_ready_hse_clock() {
117 return Ok(());
118 }
119 }
120
121 Err(ErrorCode::BUSY)
122 }
123
124 /// Check whether the HSE clock is enabled or not.
125 ///
126 /// # Returns
127 ///
128 /// + [false]: the HSE clock is not enabled
129 /// + [true]: the HSE clock is enabled
130 pub fn is_enabled(&self) -> bool {
131 self.rcc.is_enabled_hse_clock()
132 }
133
134 /// Get the frequency in MHz of the HSE clock.
135 ///
136 /// # Returns
137 ///
138 /// + [Some]\(frequency_mhz\): if the HSE clock is enabled.
139 /// + [None]: if the HSE clock is disabled.
140 pub fn get_frequency_mhz(&self) -> Option<usize> {
141 if self.is_enabled() {
142 self.hse_frequency_mhz.get()
143 } else {
144 None
145 }
146 }
147
148 /// Set the frequency in MHz of the HSE clock.
149 ///
150 /// # Parameters
151 ///
152 /// + frequency: HSE frequency in MHz
153 pub fn set_frequency_mhz(&self, frequency: usize) {
154 self.hse_frequency_mhz.set(frequency);
155 }
156}
157
158/// Tests for the HSE clock
159///
160/// This module ensures that the HSE clock works as expected. If changes are brought to the HSE
161/// clock, ensure to run all the tests to see if anything is broken.
162///
163/// # Usage
164///
165/// First, import the [crate::clocks::hse] module in the desired board main file:
166///
167/// ```rust,ignore
168/// use stm32f429zi::clocks::hse;
169/// ```
170///
171/// Then, to run the tests, put the following line before [kernel::process::load_processes]:
172///
173/// ```rust,ignore
174/// hse::tests::run(&peripherals.stm32f4.clocks.hse);
175/// ```
176///
177/// If everything works as expected, the following message should be printed on the kernel console:
178///
179/// ```text
180/// ===============================================
181/// Testing HSE...
182/// Finished testing HSE. Everything is alright!
183/// ===============================================
184/// ```
185///
186/// **NOTE:** All these tests assume default boot configuration.
187pub mod tests {
188 use super::{debug, Hse, HseMode};
189
190 /// Run the entire test suite.
191 pub fn run(hse: &Hse) {
192 debug!("");
193 debug!("===============================================");
194 debug!("Testing HSE...");
195
196 // By default, the HSE clock is disabled
197 assert!(!hse.is_enabled());
198
199 // HSE frequency is None
200 assert_eq!(None, hse.get_frequency_mhz());
201
202 // HSE should be enabled
203 assert_eq!(Ok(()), hse.enable(HseMode::BYPASS));
204
205 // HSE frequency is 8MHz
206 assert_eq!(Some(8), hse.get_frequency_mhz());
207
208 // Nothing should happen if the HSE clock is being enabled when already running
209 assert_eq!(Ok(()), hse.enable(HseMode::BYPASS));
210
211 // It is possible to disable the HSE clock since it is not the system clock source
212 assert_eq!(Ok(()), hse.disable());
213
214 debug!("Finished testing HSE. Everything is alright!");
215 debug!("===============================================");
216 debug!("");
217 }
218}