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