Skip to main content

hal_mik32/
rcc.rs

1/// Configuration and initialization module for the MIK32 microcontroller's clock and power management system.
2///
3/// This module provides structures and functions to configure various clock sources and their dividers:
4/// - HSI32M: 32 MHz internal oscillator
5/// - OSC32M: 32 MHz external oscillator
6/// - LSI32K: 32 kHz internal oscillator
7/// - OSC32K: 32 kHz external oscillator
8///
9/// # Structures
10///
11/// - [`FreqMonitor`]: Controls frequency monitoring settings including clock source selection,
12///   automatic frequency switching, and reference clock selection.
13/// - [`Config`]: Main configuration structure for clock initialization with support for:
14///   - Individual clock source enable/disable
15///   - Calibration values for internal oscillators
16///   - AHB and APB bus clock dividers
17///   - RTC clock source selection
18///
19/// # Usage
20///
21/// Create a `Config` instance, customize as needed, and call `Config::init()`:
22///
23/// ```ignore
24/// let config = Config::default();
25/// Config::init(config);
26/// let sys_clock = system_clock();
27/// ```
28///
29/// # Clock Frequency Calculation
30///
31/// - FAHB = FSYS / (DIV_AHB + 1)
32/// - FAPM_M = FAPB / (DIV_APM_M + 1)
33/// - FAPM_P = FAPB / (DIV_APM_P + 1)
34use crate::clock::Hertz;
35
36use mik32_pac::pm::ahb_mux::{AhbClkMux, ForceMux};
37use mik32_pac::pm::cpu_rtc_clk_mux::CpuRtcClkMux;
38use mik32_pac::wake_up::clocks_bu::RtcClkMux;
39use mik32_pac::wake_up::clocks_sys::Force32kClk;
40use mik32_pac::{Peripherals, Pm, WakeUp};
41
42pub const HSI32M_FREQ: Hertz = Hertz(32_000_000); // Частота от внутреннего источника тактирования (32 МГц)
43pub const OSC32M_FREQ: Hertz = Hertz(32_000_000); // Частота от внешнего источника тактирования (32 МГц)
44pub const LSI32K_FREQ: Hertz = Hertz(32_768); // Частота от внутреннего источника тактирования (32 кГц)
45pub const OSC32K_FREQ: Hertz = Hertz(32_768); // Частота от внешнего источника тактирования (32 кГц)
46
47#[derive(Debug, Clone, Copy)]
48pub struct Clocks {
49    ahb: Hertz,
50    ahb_div: u32,
51}
52
53impl Clocks {
54    pub fn new(ahb: Hertz, ahb_div: u32) -> Self {
55        Self { ahb, ahb_div }
56    }
57
58    pub fn ahbclk(&self) -> Hertz {
59        self.ahb
60    }
61
62    pub fn ahb_div_clk(&self) -> u32 {
63        self.ahb_div
64    }
65}
66
67pub struct FreqMonitor {
68    pub sys: AhbClkMux,            // Выбор источника тактирования
69    pub force_osc_sys: ForceMux,   // Автоматическая смена частоты
70    pub force32k_clk: Force32kClk, // Принудительное переключение на опорный источник для монитора частоты
71}
72
73impl Default for FreqMonitor {
74    fn default() -> Self {
75        Self {
76            sys: AhbClkMux::Osc32m,
77            force_osc_sys: ForceMux::Unfixed,
78            force32k_clk: Force32kClk::Automatic,
79        }
80    }
81}
82
83pub struct RCC {
84    pub hsi32m: bool,              // Внутренний генератор 32 МГц
85    pub osc32m: bool,              // Внешний генератор 32 МГц
86    pub lsi32k: bool,              // Внутренний генератор 32 кГц
87    pub osc32k: bool,              // Внешний генератор 32 кГц
88    pub freq_monitor: FreqMonitor, // Монитор частоты
89
90    // Задает значение делителя шины AHB.
91    // Частота шины AHB (FAHB) рассчитывается, как FSYS/( DIV_AHB+1)ы
92    pub ahb_div: u8,
93
94    // Задает значение делителя шины APB_M.
95    // Частота шины APB_M (FAPM_M) рассчитывается, как FAPB/( Div_APM_M+1)
96    pub apb_m_div: u8,
97
98    // Задает значение делителя шины APB_P.
99    // Частота шины APB_P (FAPM_P) рассчитывается, как FAPB/( Div_APM_P+1)
100    pub apb_p_div: u8,
101
102    pub hsi32m_calibration_value: u8,
103    pub lsi32k_calibration_value: u8,
104
105    // Выбор приоритетного источника тактирования часов реального времени:
106    // 0x0 – автоматический выбор. При наличии обоих источников
107    // 32кГц выбирается внутренний LSI32K
108    // nValue on reset: 0
109    pub rtcclk: RtcClkMux,
110    pub rtccpuclk: CpuRtcClkMux,
111
112    pub clocks: Clocks,
113}
114
115impl Default for RCC {
116    fn default() -> Self {
117        Self {
118            hsi32m: true, // Включим внутренний генератор 32 МГц
119            osc32m: true, // Включим внешний генератор 32 МГц
120            lsi32k: true, // Включим внутренний генератор 32 кГц
121            osc32k: true, // Включим внешний генератор 32 кГц
122            freq_monitor: FreqMonitor::default(),
123            ahb_div: 0,
124            apb_m_div: 0,
125            apb_p_div: 0,
126            hsi32m_calibration_value: 128,
127            lsi32k_calibration_value: 8,
128            rtcclk: RtcClkMux::Automatic,
129            rtccpuclk: CpuRtcClkMux::Osc32k,
130            clocks: Clocks::new(OSC32M_FREQ, 0), // TODO: брать частоту из конфигурации тактирования
131        }
132    }
133}
134
135impl RCC {
136    pub fn init(config: &RCC) {
137        let wu = unsafe { WakeUp::steal() };
138        let pm = unsafe { Pm::steal() };
139
140        wu.clocks_sys().modify(|_, w| {
141            w
142                // Включим внутренний генератор 32 МГц
143                .hsi32m_en()
144                .enable()
145                // Включим внешний генератор 32 МГц
146                .osc32m_en()
147                .enable()
148        });
149
150        wu.clocks_bu().modify(|_, w| {
151            w
152                // Включим внутренний генератор 32 кГц
153                .lsi32k_en()
154                .enable()
155                // Включим внутренний генератор 32 кГц
156                .osc32k_en()
157                .enable()
158        });
159
160        // Устанавливаем поправочные коэффициенты внутреннего генератора 32 МГц (HSI32M)
161        wu.clocks_sys()
162            .modify(|_, w| unsafe { w.adj_hsi32m().bits(config.hsi32m_calibration_value) });
163
164        // Устанавливаем поправочные коэффициенты внутреннего генератора 32 кГц
165        wu.clocks_bu()
166            .modify(|_, w| unsafe { w.adj_lsi32k().bits(config.lsi32k_calibration_value) });
167
168        // Установим принудительное переключение на опорный источник для монитора частоты
169        wu.clocks_sys()
170            .modify(|_, w| match config.freq_monitor.force32k_clk {
171                Force32kClk::Automatic => w.force_32k_clk().automatic(),
172                Force32kClk::Lsi32k => w.force_32k_clk().lsi32k(),
173                Force32kClk::Osc32k => w.force_32k_clk().osc32k(),
174            });
175
176        // Разрешаем или нет автоматическую смену тактирования
177        pm.ahb_mux()
178            .modify(|_, w| match config.freq_monitor.force_osc_sys {
179                ForceMux::Unfixed => w.force_mux().unfixed(),
180                ForceMux::Fixed => w.force_mux().fixed(),
181            });
182
183        // Выбор источника тактирования
184        pm.ahb_mux().modify(|_, w| match config.freq_monitor.sys {
185            AhbClkMux::Osc32m => w.ahb_clk_mux().osc32m(),
186            AhbClkMux::Hsi32m => w.ahb_clk_mux().hsi32m(),
187            AhbClkMux::Osc32k => w.ahb_clk_mux().osc32k(),
188            AhbClkMux::Lsi32k => w.ahb_clk_mux().lsi32k(),
189        });
190
191        // Зададим значение делителя шины AHB
192        pm.div_ahb()
193            .modify(|_, w| unsafe { w.bits(config.ahb_div as u32) });
194
195        // Зададим значение делителя шины APB
196        pm.div_apb_m()
197            .modify(|_, w| unsafe { w.bits(config.apb_m_div as u32) });
198
199        // Установим регистр управления тактированием батарейного домена
200        wu.clocks_bu().modify(|_, w| match config.rtcclk {
201            RtcClkMux::Automatic => w.rtc_clk_mux().automatic(),
202            RtcClkMux::Lsi32k => w.rtc_clk_mux().lsi32k(),
203            RtcClkMux::Osc32k => w.rtc_clk_mux().osc32k(),
204        });
205        wu.rtc_control().reset();
206
207        // Выбор источника тактирования RTC для системного таймера в составе ядра
208        pm.cpu_rtc_clk_mux().modify(|_, w| match config.rtccpuclk {
209            CpuRtcClkMux::Osc32k => w.cpu_rtc_clk_mux().osc32k(),
210            CpuRtcClkMux::Lsi32k => w.cpu_rtc_clk_mux().osc32k(),
211        });
212
213        // Отключим внешний источник тактирования на 32 МГц
214        if !config.osc32m {
215            wu.clocks_sys().modify(|_, w| w.osc32m_en().disable());
216        }
217
218        // Отключим внутренний источник тактирования на 32 МГц
219        if !config.hsi32m {
220            wu.clocks_sys().modify(|_, w| w.hsi32m_en().disable());
221        }
222
223        // Отключим внешний источник тактирования на 32 кГц
224        if !config.osc32k {
225            wu.clocks_bu().modify(|_, w| w.osc32k_en().disable());
226        }
227
228        // Отключим внутренний источник тактирования на 32 кГц
229        if !config.lsi32k {
230            wu.clocks_bu().modify(|_, w| w.lsi32k_en().disable());
231        }
232    }
233}
234
235/// Returns the current system clock frequency in Hertz.
236///
237/// This function queries the active clock source selected in the Power Manager (PM)
238/// AHB multiplexer and returns the corresponding frequency.
239///
240/// # Clock Sources
241/// The MIK32 microcontroller supports four clock sources:
242/// - **OSC32M** (32 MHz external oscillator): 32,000,000 Hz
243/// - **OSC32K** (32.768 kHz external oscillator): 32,768 Hz
244/// - **HSI32M** (32 MHz internal oscillator): 32,000,000 Hz
245/// - **LSI32K** (32.768 kHz internal oscillator): 32,768 Hz (fallback default)
246///
247/// # Returns
248/// The frequency of the currently active clock source as a [`Hertz`] value.
249///
250/// # Safety Note
251/// This function uses `Peripherals::steal()` to access the PM peripheral, which is unsafe
252/// because it bypasses Rust's borrow checker. Multiple concurrent calls are theoretically
253/// possible, but the read operation is atomic so this is safe in practice for read-only access.
254///
255/// # See Also
256/// - [`Config::init()`] for setting the clock source
257pub fn system_clock() -> Hertz {
258    let p = unsafe { Peripherals::steal() };
259
260    if p.pm.ahb_mux().read().ahb_clk_mux().is_osc32m() {
261        OSC32M_FREQ
262    } else if p.pm.ahb_mux().read().ahb_clk_mux().is_osc32k() {
263        OSC32K_FREQ
264    } else if p.pm.ahb_mux().read().ahb_clk_mux().is_hsi32m() {
265        HSI32M_FREQ
266    } else {
267        LSI32K_FREQ
268    }
269}