Skip to main content

hal_mik32/
rcc.rs

1//! Управление тактированием и сбросами MIK32.
2//!
3//! Модуль настраивает источники системной частоты, делители шин AHB/APB,
4//! источники RTC и калибровочные значения внутренних генераторов.
5//! `RCC::init` применяет конфигурацию и возвращает рассчитанное дерево частот.
6//!
7//! Расчет частот:
8//! - `sysclk` = выбранный источник `AHB_CLK_MUX`
9//! - `ahbclk` = `sysclk / (DIV_AHB + 1)`
10//! - `apb_m_clk` = `ahbclk / (DIV_APB_M + 1)`
11//! - `apb_p_clk` = `ahbclk / (DIV_APB_P + 1)`
12
13use crate::clock::Hertz;
14
15use mik32_pac::pm::ahb_mux::{AhbClkMux, ForceMux};
16use mik32_pac::pm::cpu_rtc_clk_mux::CpuRtcClkMux;
17use mik32_pac::wake_up::clocks_bu::RtcClkMux;
18use mik32_pac::wake_up::clocks_sys::Force32kClk;
19use mik32_pac::{Peripherals, Pm, WakeUp};
20
21pub const HSI32M_FREQ: Hertz = Hertz(32_000_000);
22pub const OSC32M_FREQ: Hertz = Hertz(32_000_000);
23pub const LSI32K_FREQ: Hertz = Hertz(32_768);
24pub const OSC32K_FREQ: Hertz = Hertz(32_768);
25
26const CLOCKSWITCH_TIMEOUT_VALUE: u32 = 500_000;
27const SWITCH_SETTLE_CYCLES: u32 = 100;
28const LSI32K_CALIBRATION_MAX: u8 = 15;
29
30/// Рассчитанные частоты после применения RCC-конфигурации.
31///
32/// Значения в этой структуре используются периферийными драйверами для
33/// вычисления делителей, таймингов и baudrate. Хранятся как итоговые частоты
34/// шин, так и сырые значения делителей, записанные в регистры.
35#[derive(Debug, Clone, Copy)]
36pub struct Clocks {
37    /// Частота выбранного системного источника до делителя AHB.
38    sys: Hertz,
39    /// Частота шины AHB после `DIV_AHB`.
40    ahb: Hertz,
41    /// Частота домена APB_M после `DIV_APB_M`.
42    apb_m: Hertz,
43    /// Частота домена APB_P после `DIV_APB_P`.
44    apb_p: Hertz,
45    /// Сырое значение регистра `DIV_AHB`.
46    ahb_div: u32,
47    /// Сырое значение регистра `DIV_APB_M`.
48    apb_m_div: u32,
49    /// Сырое значение регистра `DIV_APB_P`.
50    apb_p_div: u32,
51}
52
53impl Clocks {
54    /// Создает дерево частот с заданным системным источником и делителем AHB.
55    ///
56    /// Делители APB считаются равными нулю, то есть APB_M/APB_P работают на
57    /// частоте AHB.
58    pub const fn new(sys: Hertz, ahb_div: u32) -> Self {
59        Self::from_config(sys, ahb_div, 0, 0)
60    }
61
62    /// Создает дерево частот из системной частоты и сырых значений делителей.
63    pub const fn from_config(sys: Hertz, ahb_div: u32, apb_m_div: u32, apb_p_div: u32) -> Self {
64        let ahb = Hertz(sys.0 / (ahb_div + 1));
65        Self {
66            sys,
67            ahb,
68            apb_m: Hertz(ahb.0 / (apb_m_div + 1)),
69            apb_p: Hertz(ahb.0 / (apb_p_div + 1)),
70            ahb_div,
71            apb_m_div,
72            apb_p_div,
73        }
74    }
75
76    /// Частота выбранного системного источника до делителя AHB.
77    pub fn sysclk(&self) -> Hertz {
78        self.sys
79    }
80
81    /// Частота шины AHB.
82    pub fn ahbclk(&self) -> Hertz {
83        self.ahb
84    }
85
86    /// Частота домена APB_M.
87    pub fn apb_m_clk(&self) -> Hertz {
88        self.apb_m
89    }
90
91    /// Частота домена APB_P.
92    pub fn apb_p_clk(&self) -> Hertz {
93        self.apb_p
94    }
95
96    /// Сырое значение делителя AHB.
97    pub fn ahb_div_clk(&self) -> u32 {
98        self.ahb_div
99    }
100
101    /// Сырое значение делителя APB_M.
102    pub fn apb_m_div_clk(&self) -> u32 {
103        self.apb_m_div
104    }
105
106    /// Сырое значение делителя APB_P.
107    pub fn apb_p_div_clk(&self) -> u32 {
108        self.apb_p_div
109    }
110}
111
112/// Логическое имя источника тактирования для ошибок конфигурации.
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum ClockSource {
115    /// Внутренний 32 МГц генератор.
116    Hsi32m,
117    /// Внешний 32 МГц генератор.
118    Osc32m,
119    /// Внутренний 32 кГц генератор.
120    Lsi32k,
121    /// Внешний 32 кГц генератор.
122    Osc32k,
123}
124
125/// Этап переключения тактирования, на котором источник не был обнаружен.
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum ClockSwitchStage {
128    /// Выбор опорного 32 кГц источника для монитора частоты.
129    FreqMonitorRef,
130    /// Выбор приоритетного системного источника.
131    SystemClock,
132    /// Выбор источника RTC в backup-домене.
133    RtcClock,
134    /// Выбор RTC-источника для системного таймера ядра.
135    CpuRtcClock,
136}
137
138/// Ошибки RCC-конфигурации.
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub enum Error {
141    /// Конфигурация выбирает источник, который затем должен быть выключен.
142    DisabledClockSelected(ClockSource),
143    /// Для автоматического или явного выбора 32 кГц источника не включен ни
144    /// один 32 кГц генератор.
145    No32kClockEnabled,
146    /// Источник включен в конфигурации, но монитор частоты не обнаружил его за
147    /// время ожидания.
148    ClockNotReady {
149        /// Этап настройки, на котором произошел таймаут.
150        stage: ClockSwitchStage,
151        /// Источник, который ожидался в `PM.FREQ_STATUS`.
152        source: ClockSource,
153    },
154    /// Значение калибровки не помещается в аппаратное поле регистра.
155    InvalidCalibration {
156        /// Генератор, калибровочное значение которого некорректно.
157        source: ClockSource,
158        /// Переданное значение.
159        value: u8,
160    },
161}
162
163/// Конфигурация монитора частоты и выбора системного источника.
164pub struct FreqMonitor {
165    /// Источник системной частоты, записываемый в `PM.AHB_MUX.AHB_CLK_MUX`.
166    pub sys: AhbClkMux,
167    /// Разрешение автоматического переключения при пропадании выбранного
168    /// источника тактирования.
169    pub force_osc_sys: ForceMux,
170    /// Опорный 32 кГц источник для монитора частоты.
171    pub force32k_clk: Force32kClk,
172}
173
174impl Default for FreqMonitor {
175    fn default() -> Self {
176        Self {
177            sys: AhbClkMux::Osc32m,
178            force_osc_sys: ForceMux::Unfixed,
179            force32k_clk: Force32kClk::Automatic,
180        }
181    }
182}
183
184/// Конфигурация RCC.
185///
186/// Поля `hsi32m`, `osc32m`, `lsi32k`, `osc32k` задают, останется ли
187/// соответствующий генератор включенным после инициализации. `RCC::init`
188/// сначала включает все генераторы, нужные для безопасного переключения, а в
189/// конце отключает те, которые пользователь пометил как `false`.
190pub struct RCC {
191    /// Оставить включенным внутренний 32 МГц генератор.
192    pub hsi32m: bool,
193    /// Оставить включенным внешний 32 МГц генератор.
194    pub osc32m: bool,
195    /// Оставить включенным внутренний 32 кГц генератор.
196    pub lsi32k: bool,
197    /// Оставить включенным внешний 32 кГц генератор.
198    pub osc32k: bool,
199    /// Выбор системного источника и поведение монитора частоты.
200    pub freq_monitor: FreqMonitor,
201    /// Сырое значение `DIV_AHB`: частота AHB = `sysclk / (ahb_div + 1)`.
202    pub ahb_div: u8,
203    /// Сырое значение `DIV_APB_M`: частота APB_M = `ahbclk / (apb_m_div + 1)`.
204    pub apb_m_div: u8,
205    /// Сырое значение `DIV_APB_P`: частота APB_P = `ahbclk / (apb_p_div + 1)`.
206    pub apb_p_div: u8,
207    /// Калибровочное значение внутреннего 32 МГц генератора.
208    pub hsi32m_calibration_value: u8,
209    /// Калибровочное значение внутреннего 32 кГц генератора.
210    pub lsi32k_calibration_value: u8,
211    /// Приоритетный источник RTC в backup-домене.
212    pub rtcclk: RtcClkMux,
213    /// Источник RTC для системного таймера ядра.
214    pub rtccpuclk: CpuRtcClkMux,
215    /// Расчетные частоты по умолчанию. Для актуальных частот используйте
216    /// результат `RCC::init` или функцию [`clocks`].
217    pub clocks: Clocks,
218}
219
220impl Default for RCC {
221    fn default() -> Self {
222        Self {
223            hsi32m: true,
224            osc32m: true,
225            lsi32k: true,
226            osc32k: true,
227            freq_monitor: FreqMonitor::default(),
228            ahb_div: 0,
229            apb_m_div: 0,
230            apb_p_div: 0,
231            hsi32m_calibration_value: 128,
232            lsi32k_calibration_value: 8,
233            rtcclk: RtcClkMux::Automatic,
234            rtccpuclk: CpuRtcClkMux::Osc32k,
235            clocks: Clocks::from_config(OSC32M_FREQ, 0, 0, 0),
236        }
237    }
238}
239
240impl RCC {
241    /// Применяет RCC-конфигурацию и возвращает рассчитанные частоты шин.
242    ///
243    /// Метод валидирует выбранные источники до записи регистров, чтобы не
244    /// отключить генератор, который используется системной частотой, RTC или
245    /// монитором частоты.
246    pub fn init(config: &RCC) -> Result<Clocks, Error> {
247        Self::validate(config)?;
248
249        let wu = unsafe { WakeUp::steal() };
250        let pm = unsafe { Pm::steal() };
251        let mut first_error = None;
252
253        // Включаем генераторы перед переключением mux'ов: это уменьшает шанс
254        // оставить систему без активного источника тактирования.
255        wu.clocks_sys()
256            .modify(|_, w| w.hsi32m_en().enable().osc32m_en().enable());
257
258        wu.clocks_bu()
259            .modify(|_, w| w.lsi32k_en().enable().osc32k_en().enable());
260
261        // Применяем калибровку внутренних генераторов до выбора их в качестве
262        // рабочего источника.
263        wu.clocks_sys()
264            .modify(|_, w| unsafe { w.adj_hsi32m().bits(config.hsi32m_calibration_value) });
265
266        wu.clocks_bu()
267            .modify(|_, w| unsafe { w.adj_lsi32k().bits(config.lsi32k_calibration_value) });
268
269        // Настраиваем опорный 32 кГц источник монитора частоты. Как и
270        // reference HAL, для явного выбора сначала ждем бит в PM.FREQ_STATUS и
271        // только потом пишем mux: так не назначается источник, который железо
272        // еще не видит.
273        match config.freq_monitor.force32k_clk {
274            Force32kClk::Automatic => {
275                wu.clocks_sys().modify(|_, w| w.force_32k_clk().automatic());
276            }
277            Force32kClk::Lsi32k => {
278                if Self::wait_freq_status(&pm, ClockSource::Lsi32k) {
279                    wu.clocks_sys().modify(|_, w| w.force_32k_clk().lsi32k());
280                    Self::switch_settle_delay();
281                } else {
282                    Self::record_error(
283                        &mut first_error,
284                        Error::ClockNotReady {
285                            stage: ClockSwitchStage::FreqMonitorRef,
286                            source: ClockSource::Lsi32k,
287                        },
288                    );
289                }
290            }
291            Force32kClk::Osc32k => {
292                if Self::wait_freq_status(&pm, ClockSource::Osc32k) {
293                    wu.clocks_sys().modify(|_, w| w.force_32k_clk().osc32k());
294                    Self::switch_settle_delay();
295                } else {
296                    Self::record_error(
297                        &mut first_error,
298                        Error::ClockNotReady {
299                            stage: ClockSwitchStage::FreqMonitorRef,
300                            source: ClockSource::Osc32k,
301                        },
302                    );
303                }
304            }
305        }
306
307        // Выбор системного источника. При таймауте C HAL все равно делает
308        // выбранный источник приоритетным, но принудительно оставляет
309        // автоматическое переключение включенным (`UNFIXED`), чтобы железо
310        // могло уйти на живой fallback-источник.
311        let system_source = Self::source_for_ahb(config.freq_monitor.sys);
312        if Self::wait_freq_status(&pm, system_source) {
313            pm.ahb_mux().modify(|_, w| {
314                let w = match config.freq_monitor.sys {
315                    AhbClkMux::Osc32m => w.ahb_clk_mux().osc32m(),
316                    AhbClkMux::Hsi32m => w.ahb_clk_mux().hsi32m(),
317                    AhbClkMux::Osc32k => w.ahb_clk_mux().osc32k(),
318                    AhbClkMux::Lsi32k => w.ahb_clk_mux().lsi32k(),
319                };
320
321                match config.freq_monitor.force_osc_sys {
322                    ForceMux::Unfixed => w.force_mux().unfixed(),
323                    ForceMux::Fixed => w.force_mux().fixed(),
324                }
325            });
326            Self::switch_settle_delay();
327        } else {
328            pm.ahb_mux().modify(|_, w| match config.freq_monitor.sys {
329                AhbClkMux::Osc32m => w.ahb_clk_mux().osc32m().force_mux().unfixed(),
330                AhbClkMux::Hsi32m => w.ahb_clk_mux().hsi32m().force_mux().unfixed(),
331                AhbClkMux::Osc32k => w.ahb_clk_mux().osc32k().force_mux().unfixed(),
332                AhbClkMux::Lsi32k => w.ahb_clk_mux().lsi32k().force_mux().unfixed(),
333            });
334            Self::record_error(
335                &mut first_error,
336                Error::ClockNotReady {
337                    stage: ClockSwitchStage::SystemClock,
338                    source: system_source,
339                },
340            );
341        }
342
343        pm.div_ahb()
344            .modify(|_, w| unsafe { w.bits(config.ahb_div as u32) });
345
346        pm.div_apb_m()
347            .modify(|_, w| unsafe { w.bits(config.apb_m_div as u32) });
348
349        pm.div_apb_p()
350            .modify(|_, w| unsafe { w.bits(config.apb_p_div as u32) });
351
352        // RTC в backup-домене и RTC-источник для системного таймера ядра
353        // настраиваются отдельными mux'ами. Для явного RTC mux reference HAL
354        // ждет детектирования источника, пишет mux и делает импульс сброса RTC.
355        // `Reg::reset()` здесь не подходит: PAC сбрасывает регистр в 0, а само
356        // железо запускает сброс RTC именно записью 1.
357        match config.rtcclk {
358            RtcClkMux::Automatic => {
359                wu.clocks_bu().modify(|_, w| w.rtc_clk_mux().automatic());
360            }
361            RtcClkMux::Lsi32k => {
362                if Self::wait_freq_status(&pm, ClockSource::Lsi32k) {
363                    wu.clocks_bu().modify(|_, w| w.rtc_clk_mux().lsi32k());
364                    Self::pulse_rtc_reset(&wu);
365                    Self::switch_settle_delay();
366                } else {
367                    Self::record_error(
368                        &mut first_error,
369                        Error::ClockNotReady {
370                            stage: ClockSwitchStage::RtcClock,
371                            source: ClockSource::Lsi32k,
372                        },
373                    );
374                }
375            }
376            RtcClkMux::Osc32k => {
377                if Self::wait_freq_status(&pm, ClockSource::Osc32k) {
378                    wu.clocks_bu().modify(|_, w| w.rtc_clk_mux().osc32k());
379                    Self::pulse_rtc_reset(&wu);
380                    Self::switch_settle_delay();
381                } else {
382                    Self::record_error(
383                        &mut first_error,
384                        Error::ClockNotReady {
385                            stage: ClockSwitchStage::RtcClock,
386                            source: ClockSource::Osc32k,
387                        },
388                    );
389                }
390            }
391        }
392
393        let cpu_rtc_source = Self::source_for_cpu_rtc(config.rtccpuclk);
394        if Self::wait_freq_status(&pm, cpu_rtc_source) {
395            pm.cpu_rtc_clk_mux().modify(|_, w| match config.rtccpuclk {
396                CpuRtcClkMux::Osc32k => w.cpu_rtc_clk_mux().osc32k(),
397                CpuRtcClkMux::Lsi32k => w.cpu_rtc_clk_mux().lsi32k(),
398            });
399            Self::switch_settle_delay();
400        } else {
401            Self::record_error(
402                &mut first_error,
403                Error::ClockNotReady {
404                    stage: ClockSwitchStage::CpuRtcClock,
405                    source: cpu_rtc_source,
406                },
407            );
408        }
409
410        // После переключения можно выключить генераторы, которые не нужны
411        // пользователю и не участвуют в выбранной конфигурации.
412        if !config.osc32m {
413            wu.clocks_sys().modify(|_, w| w.osc32m_en().disable());
414        }
415
416        if !config.hsi32m {
417            wu.clocks_sys().modify(|_, w| w.hsi32m_en().disable());
418        }
419
420        if !config.osc32k {
421            wu.clocks_bu().modify(|_, w| w.osc32k_en().disable());
422        }
423
424        if !config.lsi32k {
425            wu.clocks_bu().modify(|_, w| w.lsi32k_en().disable());
426        }
427
428        if let Some(error) = first_error {
429            Err(error)
430        } else {
431            Ok(Self::clocks(config))
432        }
433    }
434
435    /// Рассчитывает дерево частот из RCC-конфигурации без записи регистров.
436    pub fn clocks(config: &RCC) -> Clocks {
437        Clocks::from_config(
438            Self::sysclk_for(config.freq_monitor.sys),
439            config.ahb_div as u32,
440            config.apb_m_div as u32,
441            config.apb_p_div as u32,
442        )
443    }
444
445    /// Проверяет, что выбранные источники не будут выключены конфигурацией.
446    fn validate(config: &RCC) -> Result<(), Error> {
447        if config.lsi32k_calibration_value > LSI32K_CALIBRATION_MAX {
448            return Err(Error::InvalidCalibration {
449                source: ClockSource::Lsi32k,
450                value: config.lsi32k_calibration_value,
451            });
452        }
453
454        Self::ensure_enabled(config, Self::source_for_ahb(config.freq_monitor.sys))?;
455
456        match config.freq_monitor.force32k_clk {
457            Force32kClk::Automatic => Self::ensure_any_32k_enabled(config)?,
458            Force32kClk::Lsi32k => Self::ensure_enabled(config, ClockSource::Lsi32k)?,
459            Force32kClk::Osc32k => Self::ensure_enabled(config, ClockSource::Osc32k)?,
460        }
461
462        match config.rtcclk {
463            RtcClkMux::Automatic => Self::ensure_any_32k_enabled(config)?,
464            RtcClkMux::Lsi32k => Self::ensure_enabled(config, ClockSource::Lsi32k)?,
465            RtcClkMux::Osc32k => Self::ensure_enabled(config, ClockSource::Osc32k)?,
466        }
467
468        match config.rtccpuclk {
469            CpuRtcClkMux::Osc32k => Self::ensure_enabled(config, ClockSource::Osc32k)?,
470            CpuRtcClkMux::Lsi32k => Self::ensure_enabled(config, ClockSource::Lsi32k)?,
471        }
472
473        Ok(())
474    }
475
476    fn ensure_any_32k_enabled(config: &RCC) -> Result<(), Error> {
477        if config.lsi32k || config.osc32k {
478            Ok(())
479        } else {
480            Err(Error::No32kClockEnabled)
481        }
482    }
483
484    fn ensure_enabled(config: &RCC, source: ClockSource) -> Result<(), Error> {
485        let enabled = match source {
486            ClockSource::Hsi32m => config.hsi32m,
487            ClockSource::Osc32m => config.osc32m,
488            ClockSource::Lsi32k => config.lsi32k,
489            ClockSource::Osc32k => config.osc32k,
490        };
491
492        if enabled {
493            Ok(())
494        } else {
495            Err(Error::DisabledClockSelected(source))
496        }
497    }
498
499    fn source_for_ahb(source: AhbClkMux) -> ClockSource {
500        match source {
501            AhbClkMux::Osc32m => ClockSource::Osc32m,
502            AhbClkMux::Hsi32m => ClockSource::Hsi32m,
503            AhbClkMux::Osc32k => ClockSource::Osc32k,
504            AhbClkMux::Lsi32k => ClockSource::Lsi32k,
505        }
506    }
507
508    fn source_for_cpu_rtc(source: CpuRtcClkMux) -> ClockSource {
509        match source {
510            CpuRtcClkMux::Osc32k => ClockSource::Osc32k,
511            CpuRtcClkMux::Lsi32k => ClockSource::Lsi32k,
512        }
513    }
514
515    fn wait_freq_status(pm: &Pm, source: ClockSource) -> bool {
516        for _ in 0..CLOCKSWITCH_TIMEOUT_VALUE {
517            if Self::freq_status_ready(pm, source) {
518                return true;
519            }
520        }
521
522        false
523    }
524
525    fn freq_status_ready(pm: &Pm, source: ClockSource) -> bool {
526        let status = pm.freq_status().read();
527
528        match source {
529            ClockSource::Hsi32m => status.mask_hsi32m().bit_is_set(),
530            ClockSource::Osc32m => status.mask_osc32m().bit_is_set(),
531            ClockSource::Lsi32k => status.mask_lsi32k().bit_is_set(),
532            ClockSource::Osc32k => status.mask_osc32k().bit_is_set(),
533        }
534    }
535
536    fn pulse_rtc_reset(wu: &WakeUp) {
537        wu.rtc_control().write(|w| unsafe { w.bits(1) });
538        wu.rtc_control().write(|w| unsafe { w.bits(0) });
539    }
540
541    fn switch_settle_delay() {
542        for _ in 0..SWITCH_SETTLE_CYCLES {
543            core::hint::spin_loop();
544        }
545    }
546
547    fn record_error(slot: &mut Option<Error>, error: Error) {
548        if slot.is_none() {
549            *slot = Some(error);
550        }
551    }
552
553    fn sysclk_for(source: AhbClkMux) -> Hertz {
554        match source {
555            AhbClkMux::Osc32m => OSC32M_FREQ,
556            AhbClkMux::Hsi32m => HSI32M_FREQ,
557            AhbClkMux::Osc32k => OSC32K_FREQ,
558            AhbClkMux::Lsi32k => LSI32K_FREQ,
559        }
560    }
561}
562
563/// Возвращает частоту текущего системного источника по состоянию регистров.
564pub fn system_clock() -> Hertz {
565    let p = unsafe { Peripherals::steal() };
566
567    match p.pm.ahb_mux().read().ahb_clk_mux().variant() {
568        AhbClkMux::Osc32m => OSC32M_FREQ,
569        AhbClkMux::Hsi32m => HSI32M_FREQ,
570        AhbClkMux::Osc32k => OSC32K_FREQ,
571        AhbClkMux::Lsi32k => LSI32K_FREQ,
572    }
573}
574
575/// Возвращает текущее дерево частот по состоянию регистров.
576pub fn clocks() -> Clocks {
577    let p = unsafe { Peripherals::steal() };
578
579    Clocks::from_config(
580        system_clock(),
581        p.pm.div_ahb().read().bits(),
582        p.pm.div_apb_m().read().bits(),
583        p.pm.div_apb_p().read().bits(),
584    )
585}