use cast::{f32, i32, u16, u32, u8};
use chrono::prelude::*;
use crate::exti::{Event as ExtiEvent, ExtiExt};
use crate::rcc::backup;
use crate::rcc::rec::ResetEnable;
use crate::rcc::CoreClocks;
use crate::stm32::{EXTI, RCC, RTC};
use crate::time::Hertz;
pub enum Event {
AlarmA,
AlarmB,
Wakeup,
Timestamp,
LseCss,
}
#[derive(Copy, Clone, PartialEq)]
pub enum DstState {
Standard = 0,
Dst = 1,
}
#[derive(Copy, Clone, PartialEq)]
pub enum RtcClock {
Lse {
freq: Hertz,
bypass: bool,
css: bool,
},
Lsi,
Hse { divider: u8 },
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum InitError {
RtcNotRunning,
ClockNotRunning,
ConfigMismatch,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum DstError {
ClockNotInitialized,
AlreadyDst,
AlreadyStandardTime,
CannotSubtract,
}
pub struct Rtc {
reg: RTC,
prec: backup::Rtc,
}
impl Rtc {
pub fn open_or_init(
rtc: RTC,
prec: backup::Rtc,
clock_source: RtcClock,
clocks: &CoreClocks,
) -> Self {
match Rtc::try_open(rtc, prec, clock_source, clocks) {
Ok(rtc) => rtc,
Err((rtc, prec, _err)) => {
Rtc::init(rtc, prec, clock_source, clocks)
}
}
}
pub fn try_open(
rtc: RTC,
prec: backup::Rtc,
clock_source: RtcClock,
clocks: &CoreClocks,
) -> Result<Self, (RTC, backup::Rtc, InitError)> {
if !prec.is_enabled() {
return Err((rtc, prec, InitError::RtcNotRunning));
}
let rcc = unsafe { &*RCC::ptr() };
let bdcr = rcc.bdcr.read();
let clock_source_matches =
match (clock_source, prec.get_kernel_clk_mux()) {
(RtcClock::Lsi, backup::RtcClkSel::LSI) => true,
(RtcClock::Hse { divider }, backup::RtcClkSel::HSE) => {
rcc.cfgr.read().rtcpre().bits() == divider
}
(RtcClock::Lse { bypass, css, .. }, backup::RtcClkSel::LSE) => {
bypass == bdcr.lsebyp().is_bypassed()
&& css == bdcr.lsecsson().is_security_on()
}
_ => false,
};
if !clock_source_matches {
return Err((rtc, prec, InitError::ConfigMismatch));
}
let clock_source_running = match clock_source {
RtcClock::Lsi => clocks.lsi_ck().is_some(),
RtcClock::Hse { .. } => clocks.hse_ck().is_some(),
RtcClock::Lse { .. } => bdcr.lserdy().is_ready(),
};
if !clock_source_running {
return Err((rtc, prec, InitError::ClockNotRunning));
}
Ok(Rtc { reg: rtc, prec })
}
pub fn init(
rtc: RTC,
prec: backup::Rtc,
clock_source: RtcClock,
clocks: &CoreClocks,
) -> Self {
let mut prec = prec.reset().enable();
let rcc = unsafe { &*RCC::ptr() };
let ker_ck = match clock_source {
RtcClock::Lse { bypass, freq, .. } => {
rcc.bdcr.modify(|_, w| w.lseon().on().lsebyp().bit(bypass));
while rcc.bdcr.read().lserdy().is_not_ready() {}
Some(freq)
}
RtcClock::Hse { divider } => {
assert!(divider < 64, "HSE Divider larger than 63");
rcc.cfgr.modify(|_, w| w.rtcpre().bits(divider));
clocks.hse_ck().map(|x| Hertz(x.0 / u32(divider)))
}
RtcClock::Lsi => clocks.lsi_ck(),
}
.expect("rtc_ker_ck not running")
.0;
assert!(ker_ck <= 1 << 22, "rtc_ker_ck too fast for prescaler");
prec.kernel_clk_mux(match clock_source {
RtcClock::Hse { .. } => backup::RtcClkSel::HSE,
RtcClock::Lsi => backup::RtcClkSel::LSI,
RtcClock::Lse { .. } => backup::RtcClkSel::LSE,
});
if let RtcClock::Lse { css: true, .. } = clock_source {
rcc.bdcr.modify(|_, w| w.lsecsson().security_on());
}
rtc.wpr.write(|w| unsafe { w.bits(0xCA) });
rtc.wpr.write(|w| unsafe { w.bits(0x53) });
rtc.isr.modify(|_, w| w.init().set_bit());
while rtc.isr.read().initf().bit_is_clear() {}
rtc.cr.modify(|_, w| w.bypshad().set_bit());
let total_div = ker_ck;
let a_pre_max = 1 << 7;
let s_pre_max = 1 << 15;
let (a_pre, s_pre) = if total_div <= a_pre_max {
(total_div, 1)
} else if total_div % a_pre_max == 0 {
(a_pre_max, total_div / a_pre_max)
} else {
let mut a_pre = a_pre_max;
while a_pre > 1 {
if total_div % a_pre == 0 {
break;
}
a_pre -= 1;
}
let s_pre = total_div / a_pre;
(a_pre, s_pre)
};
assert!(
a_pre <= a_pre_max && s_pre <= s_pre_max,
"Invalid RTC prescaler value"
);
rtc.prer.write(|w| {
w.prediv_s()
.bits(u16(s_pre - 1).unwrap())
.prediv_a()
.bits(u8(a_pre - 1).unwrap())
});
rtc.isr.modify(|_, w| w.init().clear_bit());
Rtc { reg: rtc, prec }
}
pub fn read_backup_reg(&self, reg: u8) -> u32 {
self.reg.bkpr[reg as usize].read().bkp().bits()
}
pub fn write_backup_reg(&mut self, reg: u8, value: u32) {
self.reg.bkpr[reg as usize].write(|w| w.bkp().bits(value));
}
pub fn set_date_time(&mut self, date_time: NaiveDateTime) {
self.reg.isr.modify(|_, w| w.init().set_bit());
while self.reg.isr.read().initf().bit_is_clear() {}
let hour = date_time.hour() as u8;
let ht = hour / 10;
let hu = hour % 10;
let minute = date_time.minute() as u8;
let mnt = minute / 10;
let mnu = minute % 10;
let second = date_time.second() as u8;
let st = second / 10;
let su = second % 10;
self.reg.tr.write(|w| {
w.pm()
.clear_bit()
.ht()
.bits(ht)
.hu()
.bits(hu)
.mnt()
.bits(mnt)
.mnu()
.bits(mnu)
.st()
.bits(st)
.su()
.bits(su)
});
let year = date_time.year();
debug_assert!(year > 2000 && year < 2100);
let yt = ((year - 2000) / 10) as u8;
let yu = ((year - 2000) % 10) as u8;
let wdu = date_time.weekday().number_from_monday() as u8;
let month = date_time.month() as u8;
let mt = month > 9;
let mu = month % 10;
let day = date_time.day() as u8;
let dt = day / 10;
let du = day % 10;
self.reg.dr.write(|w| unsafe {
w.yt()
.bits(yt)
.yu()
.bits(yu)
.wdu()
.bits(wdu)
.mt()
.bit(mt)
.mu()
.bits(mu)
.dt()
.bits(dt)
.du()
.bits(du)
});
self.reg.isr.modify(|_, w| w.init().clear_bit());
}
pub fn clear_date_time(&mut self) {
self.reg.isr.modify(|_, w| w.init().set_bit());
while self.reg.isr.read().initf().bit_is_clear() {}
self.reg.tr.reset();
self.reg.dr.reset();
self.reg.isr.modify(|_, w| w.init().clear_bit());
}
fn calendar_initialized(&self) -> Option<()> {
match self.reg.isr.read().inits().bit() {
true => Some(()),
false => None,
}
}
pub fn date(&self) -> Option<NaiveDate> {
self.calendar_initialized()?;
let data = self.reg.dr.read();
let year = 2000 + i32(data.yt().bits()) * 10 + i32(data.yu().bits());
let month = data.mt().bits() as u8 * 10 + data.mu().bits();
let day = data.dt().bits() * 10 + data.du().bits();
NaiveDate::from_ymd_opt(year, u32(month), u32(day))
}
pub fn time(&self) -> Option<NaiveTime> {
loop {
self.calendar_initialized()?;
let ss = self.reg.ssr.read().ss().bits();
let data = self.reg.tr.read();
let ss_after = self.reg.ssr.read().ss().bits();
if ss == ss_after {
let mut hour = data.ht().bits() * 10 + data.hu().bits();
if data.pm().bit_is_set() {
hour += 12;
}
let minute = data.mnt().bits() * 10 + data.mnu().bits();
let second = data.st().bits() * 10 + data.su().bits();
let micro = self.ss_to_us(ss);
return NaiveTime::from_hms_micro_opt(
u32(hour),
u32(minute),
u32(second),
micro,
);
}
}
}
pub fn date_time(&self) -> Option<NaiveDateTime> {
loop {
self.calendar_initialized()?;
let ss = self.reg.ssr.read().ss().bits();
let dr = self.reg.dr.read();
let tr = self.reg.tr.read();
let ss_after = self.reg.ssr.read().ss().bits();
if ss == ss_after {
let year =
2000 + i32(dr.yt().bits()) * 10 + i32(dr.yu().bits());
let month = dr.mt().bits() as u8 * 10 + dr.mu().bits();
let day = dr.dt().bits() * 10 + dr.du().bits();
let date = NaiveDate::from_ymd_opt(year, u32(month), u32(day))?;
let mut hour = tr.ht().bits() * 10 + tr.hu().bits();
if tr.pm().bit_is_set() {
hour += 12;
}
let minute = tr.mnt().bits() * 10 + tr.mnu().bits();
let second = tr.st().bits() * 10 + tr.su().bits();
let micro = self.ss_to_us(ss);
let time = NaiveTime::from_hms_micro_opt(
u32(hour),
u32(minute),
u32(second),
micro,
)?;
return Some(date.and_time(time));
}
}
}
pub fn subseconds(&self) -> Option<f32> {
self.calendar_initialized()?;
let ss = f32(self.reg.ssr.read().ss().bits());
let prediv_s = f32(self.reg.prer.read().prediv_s().bits());
Some((prediv_s - ss) / (prediv_s + 1.0))
}
fn ss_to_us(&self, ss: u16) -> u32 {
let ss = u32(ss);
let prediv_s = u32(self.reg.prer.read().prediv_s().bits());
assert!(ss <= prediv_s);
(((prediv_s - ss) * 100_000) / (prediv_s + 1)) * 10
}
pub fn subsec_millis(&self) -> Option<u16> {
self.calendar_initialized()?;
let ss = u32(self.reg.ssr.read().ss().bits());
let prediv_s = u32(self.reg.prer.read().prediv_s().bits());
Some(u16(((prediv_s - ss) * 1_000) / (prediv_s + 1)).unwrap())
}
pub fn subsec_micros(&self) -> Option<u32> {
self.calendar_initialized()?;
let ss = self.reg.ssr.read().ss().bits();
Some(self.ss_to_us(ss))
}
pub fn subsec_raw(&self) -> Option<u16> {
self.calendar_initialized()?;
Some(self.reg.ssr.read().ss().bits())
}
pub fn subsec_res(&self) -> Option<u16> {
self.calendar_initialized()?;
Some(self.reg.prer.read().prediv_s().bits())
}
pub fn dst(&self) -> DstState {
if self.reg.cr.read().bkp().bit_is_set() {
DstState::Dst
} else {
DstState::Standard
}
}
pub fn set_dst(&mut self, dst: DstState) {
self.reg.cr.modify(|_, w| w.bkp().bit(dst == DstState::Dst));
}
pub fn begin_dst(&mut self) -> Result<(), DstError> {
self.calendar_initialized()
.ok_or(DstError::ClockNotInitialized)?;
if self.reg.cr.read().bkp().bit_is_set() {
return Err(DstError::AlreadyDst);
}
self.reg
.cr
.modify(|_, w| w.add1h().set_bit().bkp().set_bit());
Ok(())
}
pub fn end_dst(&mut self) -> Result<(), DstError> {
self.calendar_initialized()
.ok_or(DstError::ClockNotInitialized)?;
if self.reg.cr.read().bkp().bit_is_clear() {
return Err(DstError::AlreadyStandardTime);
}
let time = self.reg.tr.read();
if time.ht().bits() == 0 && time.hu().bits() == 0 {
return Err(DstError::CannotSubtract);
}
self.reg
.cr
.modify(|_, w| w.sub1h().set_bit().bkp().clear_bit());
Ok(())
}
pub fn enable_wakeup(&mut self, interval: u32) {
self.reg.cr.modify(|_, w| w.wute().clear_bit());
self.reg.isr.modify(|_, w| w.wutf().clear_bit());
while self.reg.isr.read().wutwf().bit_is_clear() {}
if interval > 1 << 16 {
self.reg
.cr
.modify(|_, w| unsafe { w.wucksel().bits(0b110) });
let interval = u16(interval - (1 << 16) - 1)
.expect("Interval was too large for wakeup timer");
self.reg.wutr.write(|w| w.wut().bits(interval));
} else {
self.reg
.cr
.modify(|_, w| unsafe { w.wucksel().bits(0b100) });
let interval = u16(interval - 1)
.expect("Interval was too large for wakeup timer");
self.reg.wutr.write(|w| w.wut().bits(interval));
}
self.reg.cr.modify(|_, w| w.wute().set_bit());
}
pub fn disable_wakeup(&mut self) {
self.reg.cr.modify(|_, w| w.wute().clear_bit());
self.reg.isr.modify(|_, w| w.wutf().clear_bit());
}
pub fn enable_vbat_timestamp(&mut self) {
self.reg.cr.modify(|_, w| w.tse().clear_bit());
self.reg.isr.modify(|_, w| w.tsf().clear_bit());
self.reg.cr.modify(|_, w| w.itse().set_bit());
self.reg.cr.modify(|_, w| w.tse().set_bit());
}
pub fn disable_timestamp(&mut self) {
self.reg.cr.modify(|_, w| w.tse().clear_bit());
self.reg.isr.modify(|_, w| w.tsf().clear_bit());
}
pub fn read_timestamp(&self) -> Option<NaiveDateTime> {
if !self.reg.isr.read().tsf().bit_is_clear() {
return None;
}
let data = self.reg.dr.read();
let year = 2000 + i32(data.yt().bits()) * 10 + i32(data.yu().bits());
let data = self.reg.tsdr.read();
let month = data.mt().bits() as u8 * 10 + data.mu().bits();
let day = data.dt().bits() * 10 + data.du().bits();
let date = NaiveDate::from_ymd_opt(year, u32(month), u32(day))?;
let data = self.reg.tstr.read();
let mut hour = data.ht().bits() * 10 + data.hu().bits();
if data.pm().bit_is_set() {
hour += 12;
}
let minute = data.mnt().bits() * 10 + data.mnu().bits();
let second = data.st().bits() * 10 + data.su().bits();
let micro = self.ss_to_us(self.reg.tsssr.read().ss().bits());
let time = NaiveTime::from_hms_micro_opt(
u32(hour),
u32(minute),
u32(second),
u32(micro),
)?;
self.reg
.isr
.modify(|_, w| w.tsf().clear_bit().itsf().clear_bit());
Some(date.and_time(time))
}
pub fn listen(&mut self, exti: &mut EXTI, event: Event) {
let rcc = unsafe { &*RCC::ptr() };
match event {
Event::LseCss => {
exti.listen(ExtiEvent::RTC_OTHER);
exti.rtsr1.modify(|_, w| w.tr18().enabled());
rcc.cier.modify(|_, w| w.lsecssie().enabled());
}
Event::AlarmA => {
exti.listen(ExtiEvent::RTC_ALARM);
exti.rtsr1.modify(|_, w| w.tr17().enabled());
self.reg.cr.modify(|_, w| w.alraie().set_bit());
}
Event::AlarmB => {
exti.listen(ExtiEvent::RTC_ALARM);
exti.rtsr1.modify(|_, w| w.tr17().enabled());
self.reg.cr.modify(|_, w| w.alrbie().set_bit());
}
Event::Wakeup => {
exti.listen(ExtiEvent::RTC_WAKEUP);
exti.rtsr1.modify(|_, w| w.tr19().enabled());
self.reg.cr.modify(|_, w| w.wutie().set_bit());
}
Event::Timestamp => {
exti.listen(ExtiEvent::RTC_OTHER);
exti.rtsr1.modify(|_, w| w.tr18().enabled());
self.reg.cr.modify(|_, w| w.tsie().set_bit());
}
}
}
pub fn unlisten(&mut self, exti: &mut EXTI, event: Event) {
let rcc = unsafe { &*RCC::ptr() };
match event {
Event::LseCss => {
rcc.cier.modify(|_, w| w.lsecssie().disabled());
exti.unlisten(ExtiEvent::RTC_OTHER);
exti.rtsr1.modify(|_, w| w.tr18().disabled());
}
Event::AlarmA => {
self.reg.cr.modify(|_, w| w.alraie().clear_bit());
exti.unlisten(ExtiEvent::RTC_ALARM);
exti.rtsr1.modify(|_, w| w.tr17().disabled());
}
Event::AlarmB => {
self.reg.cr.modify(|_, w| w.alrbie().clear_bit());
exti.unlisten(ExtiEvent::RTC_ALARM);
exti.rtsr1.modify(|_, w| w.tr17().disabled());
}
Event::Wakeup => {
self.reg.cr.modify(|_, w| w.wutie().clear_bit());
exti.unlisten(ExtiEvent::RTC_WAKEUP);
exti.rtsr1.modify(|_, w| w.tr19().disabled());
}
Event::Timestamp => {
self.reg.cr.modify(|_, w| w.tsie().clear_bit());
exti.unlisten(ExtiEvent::RTC_OTHER);
exti.rtsr1.modify(|_, w| w.tr18().disabled());
}
}
}
pub fn is_pending(&self, event: Event) -> bool {
let rcc = unsafe { &*RCC::ptr() };
match event {
Event::LseCss => rcc.cifr.read().lsecssf().bit_is_set(),
Event::AlarmA => self.reg.isr.read().alraf().bit_is_set(),
Event::AlarmB => self.reg.isr.read().alrbf().bit_is_set(),
Event::Wakeup => self.reg.isr.read().wutf().bit_is_set(),
Event::Timestamp => self.reg.isr.read().tsf().bit_is_set(),
}
}
pub fn unpend(&mut self, exti: &mut EXTI, event: Event) {
let rcc = unsafe { &*RCC::ptr() };
match event {
Event::LseCss => {
rcc.cicr.write(|w| w.lsecssc().clear());
exti.unpend(ExtiEvent::RTC_OTHER);
}
Event::AlarmA => {
self.reg.isr.modify(|_, w| w.alraf().clear_bit());
exti.unpend(ExtiEvent::RTC_ALARM);
}
Event::AlarmB => {
self.reg.isr.modify(|_, w| w.alrbf().clear_bit());
exti.unpend(ExtiEvent::RTC_ALARM);
}
Event::Wakeup => {
self.reg.isr.modify(|_, w| w.wutf().clear_bit());
exti.unpend(ExtiEvent::RTC_WAKEUP);
}
Event::Timestamp => {
self.reg.isr.modify(|_, w| w.tsf().clear_bit());
exti.unpend(ExtiEvent::RTC_OTHER);
}
}
}
pub fn handle_lse_css(&mut self) {
if !self.is_pending(Event::LseCss) {
return;
}
let rcc = unsafe { &*RCC::ptr() };
rcc.bdcr
.modify(|_, w| w.lsecsson().security_off().lseon().off());
self.prec.kernel_clk_mux(backup::RtcClkSel::LSI);
}
}