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}