embassy/embassy-stm32/src/rtc/v2.rs

305 lines
10 KiB
Rust
Raw Normal View History

use stm32_metapac::rtc::vals::{Init, Osel, Pol};
use super::{sealed, RtcClockSource, RtcConfig};
use crate::pac::rtc::Rtc;
use crate::peripherals::RTC;
use crate::rtc::sealed::Instance;
impl super::Rtc {
fn unlock_registers() {
2023-06-19 03:07:26 +02:00
#[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
let cr = crate::pac::PWR.cr();
#[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
let cr = crate::pac::PWR.cr1();
2023-04-18 02:07:58 +02:00
2023-06-19 03:07:26 +02:00
// TODO: Missing from PAC for l0 and f0?
#[cfg(not(any(rtc_v2f0, rtc_v2l0)))]
{
if !cr.read().dbp() {
cr.modify(|w| w.set_dbp(true));
while !cr.read().dbp() {}
}
2023-06-19 03:07:26 +02:00
}
}
2023-08-10 03:15:14 +02:00
#[allow(dead_code)]
#[cfg(all(feature = "time", stm32wb))]
// start the wakeup alarm with the given duration
pub(crate) fn start_wakeup_alarm(duration: embassy_time::Duration) {
use embassy_time::TICK_HZ;
use stm32_metapac::rtc::vals::Wucksel;
use crate::interrupt::typelevel::Interrupt;
use crate::rcc::get_freqs;
let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64;
// Choose the lowest prescaler available
#[cfg(stm32wb)]
let rtc_hz = rtc_hz / 2;
let rtc_ticks = duration.as_ticks() * rtc_hz / TICK_HZ;
let rtc_ticks = if rtc_ticks > u16::MAX as u64 {
u16::MAX
} else {
rtc_ticks as u16
};
while !RTC::regs().isr().read().wutf() {}
RTC::regs().isr().modify(|w| w.set_wutf(false));
RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks));
crate::interrupt::typelevel::RTC_WKUP::unpend();
unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
RTC::regs().cr().modify(|w| {
// Choose the lowest prescaler available
#[cfg(stm32wb)]
w.set_wucksel(Wucksel::DIV2);
w.set_wutie(true);
w.set_wute(true);
});
}
#[allow(dead_code)]
#[cfg(all(feature = "time", stm32wb))]
// stop the wakeup alarm and return the time remaining
pub(crate) fn stop_wakeup_alarm() -> embassy_time::Duration {
use embassy_time::{Duration, TICK_HZ};
use crate::interrupt::typelevel::Interrupt;
use crate::rcc::get_freqs;
crate::interrupt::typelevel::RTC_WKUP::disable();
RTC::regs().cr().modify(|w| {
w.set_wute(false);
});
let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64;
// Choose the lowest prescaler available
#[cfg(stm32wb)]
let rtc_hz = rtc_hz / 2;
let rtc_ticks = RTC::regs().wutr().read().wut();
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ / rtc_hz)
}
#[allow(dead_code)]
pub(crate) fn set_clock_source(clock_source: RtcClockSource) {
#[cfg(not(rtc_v2wb))]
use stm32_metapac::rcc::vals::Rtcsel;
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
let cr = crate::pac::RCC.bdcr();
#[cfg(any(rtc_v2l0, rtc_v2l1))]
let cr = crate::pac::RCC.csr();
Self::unlock_registers();
cr.modify(|w| {
// Select RTC source
#[cfg(not(rtc_v2wb))]
w.set_rtcsel(Rtcsel::from_bits(clock_source as u8));
#[cfg(rtc_v2wb)]
w.set_rtcsel(clock_source as u8);
});
}
2023-04-18 02:07:58 +02:00
pub(super) fn enable() {
2023-06-19 03:07:26 +02:00
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
let reg = crate::pac::RCC.bdcr().read();
#[cfg(any(rtc_v2l0, rtc_v2l1))]
let reg = crate::pac::RCC.csr().read();
2023-04-18 02:07:58 +02:00
2023-06-19 03:07:26 +02:00
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))]
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
2023-04-18 02:07:58 +02:00
if !reg.rtcen() {
Self::unlock_registers();
2023-04-18 02:07:58 +02:00
#[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))]
2023-06-19 03:07:26 +02:00
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
let cr = crate::pac::RCC.bdcr();
#[cfg(any(rtc_v2l0, rtc_v2l1))]
let cr = crate::pac::RCC.csr();
cr.modify(|w| {
// Reset
2023-04-18 02:07:58 +02:00
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
2023-06-19 03:07:26 +02:00
w.set_bdrst(false);
w.set_rtcen(true);
w.set_rtcsel(reg.rtcsel());
2023-06-19 03:07:26 +02:00
// Restore bcdr
#[cfg(any(rtc_v2l4, rtc_v2wb))]
w.set_lscosel(reg.lscosel());
#[cfg(any(rtc_v2l4, rtc_v2wb))]
w.set_lscoen(reg.lscoen());
w.set_lseon(reg.lseon());
#[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
2023-08-06 18:58:28 +02:00
}
2023-08-06 18:58:28 +02:00
/// Applies the RTC config
/// It this changes the RTC clock source the time will be reset
pub(super) fn configure(&mut self, rtc_config: RtcConfig) {
2023-06-19 03:07:26 +02:00
self.write(true, |rtc| {
rtc.cr().modify(|w| {
#[cfg(rtc_v2f2)]
w.set_fmt(false);
#[cfg(not(rtc_v2f2))]
w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);
w.set_osel(Osel::DISABLED);
w.set_pol(Pol::HIGH);
});
rtc.prer().modify(|w| {
w.set_prediv_s(rtc_config.sync_prescaler);
w.set_prediv_a(rtc_config.async_prescaler);
});
});
}
/// Calibrate the clock drift.
///
/// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
///
/// ### Note
///
/// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
/// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
#[cfg(not(rtc_v2f2))]
pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) {
const RTC_CALR_MIN_PPM: f32 = -487.1;
const RTC_CALR_MAX_PPM: f32 = 488.5;
const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
if clock_drift < RTC_CALR_MIN_PPM {
clock_drift = RTC_CALR_MIN_PPM;
} else if clock_drift > RTC_CALR_MAX_PPM {
clock_drift = RTC_CALR_MAX_PPM;
}
clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM;
self.write(false, |rtc| {
2023-06-19 03:07:26 +02:00
rtc.calr().write(|w| {
match period {
super::RtcCalibrationCyclePeriod::Seconds8 => {
w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
}
2023-06-19 03:07:26 +02:00
super::RtcCalibrationCyclePeriod::Seconds16 => {
w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
}
super::RtcCalibrationCyclePeriod::Seconds32 => {
// Set neither `calw8` nor `calw16` to use 32 seconds
}
2023-06-19 03:07:26 +02:00
}
// Extra pulses during calibration cycle period: CALP * 512 - CALM
//
// CALP sets whether pulses are added or omitted.
//
// CALM contains how many pulses (out of 512) are masked in a
// given calibration cycle period.
if clock_drift > 0.0 {
// Maximum (about 512.2) rounds to 512.
clock_drift += 0.5;
// When the offset is positive (0 to 512), the opposite of
// the offset (512 - offset) is masked, i.e. for the
// maximum offset (512), 0 pulses are masked.
w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
w.set_calm(512 - clock_drift as u16);
} else {
// Minimum (about -510.7) rounds to -511.
clock_drift -= 0.5;
// When the offset is negative or zero (-511 to 0),
// the absolute offset is masked, i.e. for the minimum
// offset (-511), 511 pulses are masked.
w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
w.set_calm((clock_drift * -1.0) as u16);
}
});
})
}
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
where
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
{
let r = RTC::regs();
// Disable write protection.
// This is safe, as we're only writin the correct and expected values.
2023-06-19 03:07:26 +02:00
r.wpr().write(|w| w.set_key(0xca));
r.wpr().write(|w| w.set_key(0x53));
// true if initf bit indicates RTC peripheral is in init mode
if init_mode && !r.isr().read().initf() {
// to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
r.isr().modify(|w| w.set_init(Init::INITMODE));
// wait till init state entered
// ~2 RTCCLK cycles
while !r.isr().read().initf() {}
}
let result = f(&r);
2023-06-19 03:07:26 +02:00
if init_mode {
r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
}
2023-06-19 03:07:26 +02:00
// Re-enable write protection.
// This is safe, as the field accepts the full range of 8-bit values.
r.wpr().write(|w| w.set_key(0xff));
result
}
}
2023-04-19 03:35:43 +02:00
impl sealed::Instance for crate::peripherals::RTC {
const BACKUP_REGISTER_COUNT: usize = 20;
2023-06-19 03:07:26 +02:00
fn enable_peripheral_clk() {
2023-04-19 03:35:43 +02:00
#[cfg(any(rtc_v2l4, rtc_v2wb))]
{
// enable peripheral clock for communication
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
2023-04-19 03:35:43 +02:00
// read to allow the pwr clock to enable
crate::pac::PWR.cr1().read();
}
#[cfg(any(rtc_v2f2))]
{
crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true));
crate::pac::PWR.cr().read();
}
}
2023-04-18 00:02:40 +02:00
2023-04-19 03:35:43 +02:00
fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
if register < Self::BACKUP_REGISTER_COUNT {
2023-06-19 03:07:26 +02:00
Some(rtc.bkpr(register).read().bkp())
2023-04-19 03:35:43 +02:00
} else {
None
}
}
2023-04-18 00:02:40 +02:00
2023-04-19 03:35:43 +02:00
fn write_backup_register(rtc: &Rtc, register: usize, value: u32) {
if register < Self::BACKUP_REGISTER_COUNT {
2023-06-19 03:07:26 +02:00
rtc.bkpr(register).write(|w| w.set_bkp(value));
2023-04-19 03:35:43 +02:00
}
2023-04-18 00:02:40 +02:00
}
}