| 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 | |