1 | use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Key, Osel, Pol, TampalrmType}; |
2 | |
3 | use super::RtcCalibrationCyclePeriod; |
4 | use crate::pac::rtc::Rtc; |
5 | use crate::peripherals::RTC; |
6 | use crate::rtc::SealedInstance; |
7 | |
8 | impl super::Rtc { |
9 | /// Applies the RTC config |
10 | /// It this changes the RTC clock source the time will be reset |
11 | pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { |
12 | self.write(true, |rtc| { |
13 | rtc.cr().modify(|w| { |
14 | w.set_bypshad(true); |
15 | w.set_fmt(Fmt::TWENTY_FOUR_HOUR); |
16 | w.set_osel(Osel::DISABLED); |
17 | w.set_pol(Pol::HIGH); |
18 | }); |
19 | |
20 | rtc.prer().modify(|w| { |
21 | w.set_prediv_s(sync_psc); |
22 | w.set_prediv_a(async_psc); |
23 | }); |
24 | |
25 | // TODO: configuration for output pins |
26 | rtc.cr().modify(|w| { |
27 | w.set_out2en(false); |
28 | w.set_tampalrm_type(TampalrmType::PUSH_PULL); |
29 | w.set_tampalrm_pu(false); |
30 | }); |
31 | }); |
32 | } |
33 | |
34 | const RTC_CALR_MIN_PPM: f32 = -487.1; |
35 | const RTC_CALR_MAX_PPM: f32 = 488.5; |
36 | const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537; |
37 | |
38 | /// Calibrate the clock drift. |
39 | /// |
40 | /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range. |
41 | /// |
42 | /// ### Note |
43 | /// |
44 | /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` |
45 | /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). |
46 | pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) { |
47 | if clock_drift < Self::RTC_CALR_MIN_PPM { |
48 | clock_drift = Self::RTC_CALR_MIN_PPM; |
49 | } else if clock_drift > Self::RTC_CALR_MAX_PPM { |
50 | clock_drift = Self::RTC_CALR_MAX_PPM; |
51 | } |
52 | |
53 | clock_drift /= Self::RTC_CALR_RESOLUTION_PPM; |
54 | |
55 | self.write(false, |rtc| { |
56 | rtc.calr().write(|w| { |
57 | match period { |
58 | RtcCalibrationCyclePeriod::Seconds8 => { |
59 | w.set_calw8(Calw8::EIGHT_SECONDS); |
60 | } |
61 | RtcCalibrationCyclePeriod::Seconds16 => { |
62 | w.set_calw16(Calw16::SIXTEEN_SECONDS); |
63 | } |
64 | RtcCalibrationCyclePeriod::Seconds32 => { |
65 | // Set neither `calw8` nor `calw16` to use 32 seconds |
66 | } |
67 | } |
68 | |
69 | // Extra pulses during calibration cycle period: CALP * 512 - CALM |
70 | // |
71 | // CALP sets whether pulses are added or omitted. |
72 | // |
73 | // CALM contains how many pulses (out of 512) are masked in a |
74 | // given calibration cycle period. |
75 | if clock_drift > 0.0 { |
76 | // Maximum (about 512.2) rounds to 512. |
77 | clock_drift += 0.5; |
78 | |
79 | // When the offset is positive (0 to 512), the opposite of |
80 | // the offset (512 - offset) is masked, i.e. for the |
81 | // maximum offset (512), 0 pulses are masked. |
82 | w.set_calp(Calp::INCREASE_FREQ); |
83 | w.set_calm(512 - clock_drift as u16); |
84 | } else { |
85 | // Minimum (about -510.7) rounds to -511. |
86 | clock_drift -= 0.5; |
87 | |
88 | // When the offset is negative or zero (-511 to 0), |
89 | // the absolute offset is masked, i.e. for the minimum |
90 | // offset (-511), 511 pulses are masked. |
91 | w.set_calp(Calp::NO_CHANGE); |
92 | w.set_calm((clock_drift * -1.0) as u16); |
93 | } |
94 | }); |
95 | }) |
96 | } |
97 | |
98 | pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R |
99 | where |
100 | F: FnOnce(crate::pac::rtc::Rtc) -> R, |
101 | { |
102 | let r = RTC::regs(); |
103 | // Disable write protection. |
104 | // This is safe, as we're only writin the correct and expected values. |
105 | r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); |
106 | r.wpr().write(|w| w.set_key(Key::DEACTIVATE2)); |
107 | |
108 | if init_mode && !r.icsr().read().initf() { |
109 | r.icsr().modify(|w| w.set_init(true)); |
110 | // wait till init state entered |
111 | // ~2 RTCCLK cycles |
112 | while !r.icsr().read().initf() {} |
113 | } |
114 | |
115 | let result = f(r); |
116 | |
117 | if init_mode { |
118 | r.icsr().modify(|w| w.set_init(false)); // Exits init mode |
119 | } |
120 | |
121 | // Re-enable write protection. |
122 | // This is safe, as the field accepts the full range of 8-bit values. |
123 | r.wpr().write(|w| w.set_key(Key::ACTIVATE)); |
124 | |
125 | result |
126 | } |
127 | } |
128 | |
129 | impl SealedInstance for crate::peripherals::RTC { |
130 | const BACKUP_REGISTER_COUNT: usize = 32; |
131 | |
132 | #[cfg (feature = "low-power" )] |
133 | cfg_if::cfg_if!( |
134 | if #[cfg(stm32g4)] { |
135 | const EXTI_WAKEUP_LINE: usize = 20; |
136 | } else if #[cfg(stm32g0)] { |
137 | const EXTI_WAKEUP_LINE: usize = 19; |
138 | } else if #[cfg(any(stm32l5, stm32h5))] { |
139 | const EXTI_WAKEUP_LINE: usize = 17; |
140 | } |
141 | ); |
142 | |
143 | #[cfg (feature = "low-power" )] |
144 | cfg_if::cfg_if!( |
145 | if #[cfg(stm32g4)] { |
146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; |
147 | } else if #[cfg(any(stm32g0, stm32u0))] { |
148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; |
149 | } else if #[cfg(any(stm32l5, stm32h5, stm32u5))] { |
150 | type WakeupInterrupt = crate::interrupt::typelevel::RTC; |
151 | } |
152 | ); |
153 | |
154 | fn read_backup_register(_rtc: Rtc, register: usize) -> Option<u32> { |
155 | #[allow (clippy::if_same_then_else)] |
156 | if register < Self::BACKUP_REGISTER_COUNT { |
157 | //Some(rtc.bkpr()[register].read().bits()) |
158 | None // RTC3 backup registers come from the TAMP peripheral, not RTC. Not() even in the L412 PAC |
159 | } else { |
160 | None |
161 | } |
162 | } |
163 | |
164 | fn write_backup_register(_rtc: Rtc, register: usize, _value: u32) { |
165 | if register < Self::BACKUP_REGISTER_COUNT { |
166 | // RTC3 backup registers come from the TAMP peripheral, not RTC. Not() even in the L412 PAC |
167 | //self.rtc.bkpr()[register].write(|w| w.bits(value)) |
168 | } |
169 | } |
170 | } |
171 | |