use crate::pac::{EXTI, PWR, RCC, RTC};
use core::convert::TryInto;
use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike};
use cfg_if::cfg_if;
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
pub enum RtcClockSource {
Lse = 0b01,
Lsi = 0b10,
Hse = 0b11,
}
#[derive(Debug)]
pub enum Error {
InvalidInputData,
}
#[derive(Clone, Copy, Debug)]
enum WakeupDivision {
Sixteen,
Eight,
Four,
Two,
}
#[derive(Clone, Copy, Debug)]
enum ClockConfig {
One(WakeupDivision),
Two,
Three,
}
pub enum Event {
WakeupTimer,
AlarmA,
AlarmB,
Timestamp,
}
pub enum Alarm {
AlarmA,
AlarmB,
}
impl From<Alarm> for Event {
fn from(a: Alarm) -> Self {
match a {
Alarm::AlarmA => Event::AlarmA,
Alarm::AlarmB => Event::AlarmB,
}
}
}
pub struct Rtc {
regs: RTC,
config: RtcConfig,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct RtcConfig {
clock_source: RtcClockSource,
async_prescaler: u8,
sync_prescaler: u16,
bypass_lse_output: bool,
}
impl Default for RtcConfig {
fn default() -> Self {
RtcConfig {
clock_source: RtcClockSource::Lsi,
async_prescaler: 127,
sync_prescaler: 255,
bypass_lse_output: false,
}
}
}
impl RtcConfig {
pub fn clock_source(mut self, source: RtcClockSource) -> Self {
self.clock_source = source;
self
}
pub fn async_prescaler(mut self, prescaler: u8) -> Self {
self.async_prescaler = prescaler;
self
}
pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
self.sync_prescaler = prescaler;
self
}
pub fn bypass_lse_output(mut self, bypass: bool) -> Self {
self.bypass_lse_output = bypass;
self
}
}
impl Rtc {
pub fn new(regs: RTC, rcc: &mut RCC, pwr: &mut PWR, config: RtcConfig) -> Self {
let mut result = Self { regs, config };
cfg_if! {
if #[cfg(any(feature = "f3", feature = "f4"))] {
rcc.apb1enr.modify(|_, w| w.pwren().set_bit());
pwr.cr.read();
pwr.cr.modify(|_, w| w.dbp().set_bit());
while pwr.cr.read().dbp().bit_is_clear() {}
} else if #[cfg(any(feature = "l4", feature = "l5", feature = "g4"))] {
rcc.apb1enr1.modify(|_, w| {
w.pwren().set_bit();
w.rtcapben().set_bit()
});
rcc.apb1smenr1.modify(|_, w| w.rtcapbsmen().set_bit());
pwr.cr1.read();
pwr.cr1.modify( | _, w| w.dbp().set_bit());
while pwr.cr1.read().dbp().bit_is_clear() {}
} else if #[cfg(any(feature = "g0"))] {
rcc.apbenr1.modify(|_, w| {
w.pwren().set_bit();
w.rtcapben().set_bit()
});
rcc.apbsmenr1.modify(|_, w| w.rtcapbsmen().set_bit());
pwr.cr1.read();
pwr.cr1.modify( | _, w| w.dbp().set_bit());
while pwr.cr1.read().dbp().bit_is_clear() {}
} else {
rcc.apb4enr.modify(|_, w| w.rtcapben().set_bit());
rcc.apb4lpenr.modify(|_, w| w.rtcapblpen().set_bit());
pwr.cr1.read();
pwr.cr1.modify( | _, w| w.dbp().set_bit());
while pwr.cr1.read().dbp().bit_is_clear() {}
}
}
rcc.bdcr.modify(|_, w| w.bdrst().set_bit());
rcc.bdcr.modify(|_, w| w.bdrst().clear_bit());
match config.clock_source {
RtcClockSource::Lsi => {
rcc.csr.modify(|_, w| w.lsion().set_bit());
while rcc.csr.read().lsirdy().bit_is_clear() {}
}
RtcClockSource::Lse => {
rcc.bdcr.modify(|_, w| {
w.lseon().set_bit();
w.lsebyp().bit(config.bypass_lse_output)
});
while rcc.bdcr.read().lserdy().bit_is_clear() {}
}
_ => (),
}
rcc.bdcr.modify(|_, w| {
unsafe { w.rtcsel().bits(result.config.clock_source as u8) };
w.rtcen().set_bit()
});
result.edit_regs(false, |regs| {
regs.cr.modify(
|_, w| {
unsafe {
w.fmt()
.clear_bit()
.osel()
.bits(0b00)
.pol()
.clear_bit()
}
},
);
regs.prer.modify(|_, w| unsafe {
w.prediv_s().bits(config.sync_prescaler);
w.prediv_a().bits(config.async_prescaler)
});
});
result
}
pub fn set_24h_fmt(&mut self) {
self.edit_regs(true, |regs| regs.cr.modify(|_, w| w.fmt().set_bit()));
}
pub fn set_12h_fmt(&mut self) {
self.edit_regs(true, |regs| regs.cr.modify(|_, w| w.fmt().clear_bit()));
}
pub fn is_24h_fmt(&self) -> bool {
self.regs.cr.read().fmt().bit()
}
fn set_wakeup_interval_inner(&mut self, sleep_time: f32) {
let lfe_freq = match self.config.clock_source {
RtcClockSource::Lse => 32_768.,
RtcClockSource::Lsi => 40_000.,
RtcClockSource::Hse => 250_000.,
};
let clock_cfg;
let wutr;
if sleep_time >= 0.00012207 && sleep_time < 32. {
let division;
let div;
if sleep_time < 4. {
division = WakeupDivision::Two;
div = 2.;
} else if sleep_time < 8. {
division = WakeupDivision::Four;
div = 4.;
} else if sleep_time < 16. {
division = WakeupDivision::Eight;
div = 8.;
} else {
division = WakeupDivision::Sixteen;
div = 16.;
}
clock_cfg = ClockConfig::One(division);
wutr = sleep_time * lfe_freq / div - 1.
} else if sleep_time < 65_536. {
clock_cfg = ClockConfig::Two;
wutr = sleep_time;
} else if sleep_time < 131_072. {
clock_cfg = ClockConfig::Three;
wutr = sleep_time - 65_537.;
} else {
panic!("Wakeup period must be between 0122.07µs and 36 hours.")
}
self.regs
.wutr
.modify(|_, w| unsafe { w.wut().bits(wutr as u16) });
let word = match clock_cfg {
ClockConfig::One(division) => match division {
WakeupDivision::Sixteen => 0b000,
WakeupDivision::Eight => 0b001,
WakeupDivision::Four => 0b010,
WakeupDivision::Two => 0b011,
},
ClockConfig::Two => 0b100,
ClockConfig::Three => 0b110,
};
self.regs
.cr
.modify(|_, w| unsafe { w.wucksel().bits(word) });
}
#[cfg(not(feature = "f373"))]
pub fn set_wakeup(&mut self, exti: &mut EXTI, sleep_time: f32) {
cfg_if! {
if #[cfg(any(feature = "f3", feature = "l4"))] {
exti.imr1.modify(|_, w| w.mr20().unmasked());
exti.rtsr1.modify(|_, w| w.tr20().bit(true));
exti.ftsr1.modify(|_, w| w.tr20().bit(false));
} else if #[cfg(feature = "f4")] {
exti.imr.modify(|_, w| w.mr20().unmasked());
exti.rtsr.modify(|_, w| w.tr20().bit(true));
exti.ftsr.modify(|_, w| w.tr20().bit(false));
} else if #[cfg(feature = "g4")]{
exti.imr1.modify(|_, w| w.im20().unmasked());
exti.rtsr1.modify(|_, w| w.rt20().bit(true));
exti.ftsr1.modify(|_, w| w.ft20().bit(false));
} else if #[cfg(any(feature = "l5", feature = "g0"))] {
} else if #[cfg(any(feature = "h747cm4", feature = "h747cm7"))] {
exti.c1imr1.modify(|_, w| w.mr20().unmasked());
exti.rtsr1.modify(|_, w| w.tr20().bit(true));
exti.ftsr1.modify(|_, w| w.tr20().bit(false));
} else {
exti.cpuimr1.modify(|_, w| w.mr20().unmasked());
exti.rtsr1.modify(|_, w| w.tr20().bit(true));
exti.ftsr1.modify(|_, w| w.tr20().bit(false));
}
}
self.regs.wpr.write(|w| unsafe { w.bits(0xCA) });
self.regs.wpr.write(|w| unsafe { w.bits(0x53) });
self.regs.cr.modify(|_, w| w.wute().clear_bit());
cfg_if! {
if #[cfg(any(feature = "l5", feature = "g0", feature = "g4"))] {
while self.regs.icsr.read().wutwf().bit_is_clear() {}
} else {
while self.regs.isr.read().wutwf().bit_is_clear() {}
}
}
self.set_wakeup_interval_inner(sleep_time);
self.regs.cr.modify(|_, w| w.wute().set_bit());
self.regs.cr.modify(|_, w| w.wutie().set_bit());
cfg_if! {
if #[cfg(any(feature = "l5", feature = "g0", feature = "g4"))] {
self.regs.scr.write(|w| w.cwutf().set_bit());
} else {
self.regs.isr.modify(|_, w| w.wutf().clear_bit());
}
}
self.regs.wpr.write(|w| unsafe { w.bits(0xFF) });
}
pub fn enable_wakeup(&mut self) {
unsafe {
self.regs.wpr.write(|w| w.bits(0xCA));
self.regs.wpr.write(|w| w.bits(0x53));
self.regs.cr.modify(|_, w| w.wute().set_bit());
self.regs.wpr.write(|w| w.bits(0xFF));
}
}
pub fn disable_wakeup(&mut self) {
unsafe {
self.regs.wpr.write(|w| w.bits(0xCA));
self.regs.wpr.write(|w| w.bits(0x53));
self.regs.cr.modify(|_, w| w.wute().clear_bit());
self.regs.wpr.write(|w| w.bits(0xFF));
}
}
pub fn set_wakeup_interval(&mut self, sleep_time: f32) {
self.regs.wpr.write(|w| unsafe { w.bits(0xCA) });
self.regs.wpr.write(|w| unsafe { w.bits(0x53) });
let started_enabled = self.regs.cr.read().wute().bit_is_set();
if started_enabled {
self.regs.cr.modify(|_, w| w.wute().clear_bit());
}
cfg_if! {
if #[cfg(any(feature = "l5", feature = "g0", feature = "g4"))] {
while self.regs.icsr.read().wutwf().bit_is_clear() {}
} else {
while self.regs.isr.read().wutwf().bit_is_clear() {}
}
}
self.set_wakeup_interval_inner(sleep_time);
if started_enabled {
self.regs.cr.modify(|_, w| w.wute().set_bit());
}
self.regs.wpr.write(|w| unsafe { w.bits(0xFF) });
}
pub fn clear_wakeup_flag(&mut self) {
self.edit_regs(false, |regs| {
regs.cr.modify(|_, w| w.wute().clear_bit());
cfg_if! {
if #[cfg(any(feature = "l5", feature = "g0", feature = "g4"))] {
regs.scr.write(|w| w.cwutf().set_bit());
} else {
regs.isr.modify(|_, w| w.wutf().clear_bit());
}
}
regs.cr.modify(|_, w| w.wute().set_bit());
});
}
fn edit_regs<F>(&mut self, init_mode: bool, mut closure: F)
where
F: FnMut(&mut RTC),
{
self.regs.wpr.write(|w| unsafe { w.bits(0xCA) });
self.regs.wpr.write(|w| unsafe { w.bits(0x53) });
cfg_if! {
if #[cfg(any(feature = "l5", feature = "g0", feature = "g4"))] {
if init_mode && self.regs.icsr.read().initf().bit_is_clear() {
self.regs.icsr.modify(|_, w| w.init().set_bit());
while self.regs.icsr.read().initf().bit_is_clear() {}
}
closure(&mut self.regs);
if init_mode {
self.regs.icsr.modify(|_, w| w.init().clear_bit());
while self.regs.icsr.read().initf().bit_is_set() {}
}
} else {
if init_mode && self.regs.isr.read().initf().bit_is_clear() {
self.regs.isr.modify(|_, w| w.init().set_bit());
while self.regs.isr.read().initf().bit_is_clear() {}
}
closure(&mut self.regs);
if init_mode {
self.regs.isr.modify(|_, w| w.init().clear_bit());
while self.regs.isr.read().initf().bit_is_set() {}
}
}
}
self.regs.wpr.write(|w| unsafe { w.bits(0xFF) });
}
}
impl Rtcc for Rtc {
type Error = Error;
fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> {
self.set_24h_fmt();
let (ht, hu) = bcd2_encode(time.hour())?;
let (mnt, mnu) = bcd2_encode(time.minute())?;
let (st, su) = bcd2_encode(time.second())?;
self.edit_regs(true, |regs| {
regs.tr.write(|w| unsafe {
w.ht().bits(ht);
w.hu().bits(hu);
w.mnt().bits(mnt);
w.mnu().bits(mnu);
w.st().bits(st);
w.su().bits(su);
w.pm().clear_bit()
})
});
Ok(())
}
fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
if seconds > 59 {
return Err(Error::InvalidInputData);
}
let (st, su) = bcd2_encode(seconds as u32)?;
self.edit_regs(true, |regs| {
regs.tr
.modify(|_, w| unsafe { w.st().bits(st).su().bits(su) })
});
Ok(())
}
fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> {
if minutes > 59 {
return Err(Error::InvalidInputData);
}
let (mnt, mnu) = bcd2_encode(minutes as u32)?;
self.edit_regs(true, |regs| {
regs.tr
.modify(|_, w| unsafe { w.mnt().bits(mnt).mnu().bits(mnu) })
});
Ok(())
}
fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> {
let (ht, hu) = hours_to_register(hours)?;
match hours {
Hours::H24(_h) => self.set_24h_fmt(),
Hours::AM(_h) | Hours::PM(_h) => self.set_12h_fmt(),
}
self.edit_regs(true, |regs| {
regs.tr
.modify(|_, w| unsafe { w.ht().bits(ht).hu().bits(hu) })
});
Ok(())
}
fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> {
if !(1..=7).contains(&weekday) {
return Err(Error::InvalidInputData);
}
self.edit_regs(true, |regs| {
regs.dr.modify(|_, w| unsafe { w.wdu().bits(weekday) })
});
Ok(())
}
fn set_day(&mut self, day: u8) -> Result<(), Self::Error> {
if !(1..=31).contains(&day) {
return Err(Error::InvalidInputData);
}
let (dt, du) = bcd2_encode(day as u32)?;
self.edit_regs(true, |regs| {
regs.dr
.modify(unsafe { |_, w| w.dt().bits(dt).du().bits(du) })
});
Ok(())
}
fn set_month(&mut self, month: u8) -> Result<(), Self::Error> {
if !(1..=12).contains(&month) {
return Err(Error::InvalidInputData);
}
let (mt, mu) = bcd2_encode(month as u32)?;
self.edit_regs(true, |regs| {
regs.dr
.modify(|_, w| unsafe { w.mt().bit(mt > 0).mu().bits(mu) })
});
Ok(())
}
fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
if !(1970..=2038).contains(&year) {
return Err(Error::InvalidInputData);
}
let (yt, yu) = bcd2_encode(year as u32)?;
self.edit_regs(true, |regs| {
regs.dr
.modify(|_, w| unsafe { w.yt().bits(yt).yu().bits(yu) })
});
Ok(())
}
fn set_date(&mut self, date: &NaiveDate) -> Result<(), Self::Error> {
if date.year() < 1970 {
return Err(Error::InvalidInputData);
}
let (yt, yu) = bcd2_encode((date.year() - 1970) as u32)?;
let (mt, mu) = bcd2_encode(date.month())?;
let (dt, du) = bcd2_encode(date.day())?;
self.edit_regs(true, |regs| {
regs.dr.write(|w| unsafe {
w.dt().bits(dt);
w.du().bits(du);
w.mt().bit(mt > 0);
w.mu().bits(mu);
w.yt().bits(yt);
w.yu().bits(yu)
})
});
Ok(())
}
fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> {
if date.year() < 1970 {
return Err(Error::InvalidInputData);
}
self.set_24h_fmt();
let (yt, yu) = bcd2_encode((date.year() - 1970) as u32)?;
let (mt, mu) = bcd2_encode(date.month())?;
let (dt, du) = bcd2_encode(date.day())?;
let (ht, hu) = bcd2_encode(date.hour())?;
let (mnt, mnu) = bcd2_encode(date.minute())?;
let (st, su) = bcd2_encode(date.second())?;
self.edit_regs(true, |regs| {
regs.dr.write(|w| unsafe {
w.dt().bits(dt);
w.du().bits(du);
w.mt().bit(mt > 0);
w.mu().bits(mu);
w.yt().bits(yt);
w.yu().bits(yu)
})
});
self.edit_regs(true, |regs| {
regs.tr.write(|w| unsafe {
w.ht().bits(ht);
w.hu().bits(hu);
w.mnt().bits(mnt);
w.mnu().bits(mnu);
w.st().bits(st);
w.su().bits(su);
w.pm().clear_bit()
})
});
Ok(())
}
fn get_seconds(&mut self) -> Result<u8, Self::Error> {
let tr = self.regs.tr.read();
let seconds = bcd2_decode(tr.st().bits(), tr.su().bits());
Ok(seconds as u8)
}
fn get_minutes(&mut self) -> Result<u8, Self::Error> {
let tr = self.regs.tr.read();
let minutes = bcd2_decode(tr.mnt().bits(), tr.mnu().bits());
Ok(minutes as u8)
}
fn get_hours(&mut self) -> Result<Hours, Self::Error> {
let tr = self.regs.tr.read();
let hours = bcd2_decode(tr.ht().bits(), tr.hu().bits());
if self.is_24h_fmt() {
return Ok(Hours::H24(hours as u8));
}
if !tr.pm().bit() {
return Ok(Hours::AM(hours as u8));
}
Ok(Hours::PM(hours as u8))
}
fn get_time(&mut self) -> Result<NaiveTime, Self::Error> {
self.set_24h_fmt();
let seconds = self.get_seconds()?;
let minutes = self.get_minutes()?;
let hours = hours_to_u8(self.get_hours()?)?;
Ok(NaiveTime::from_hms(
hours.into(),
minutes.into(),
seconds.into(),
))
}
fn get_weekday(&mut self) -> Result<u8, Self::Error> {
let dr = self.regs.dr.read();
let weekday = bcd2_decode(dr.wdu().bits(), 0x00);
Ok(weekday as u8)
}
fn get_day(&mut self) -> Result<u8, Self::Error> {
let dr = self.regs.dr.read();
let day = bcd2_decode(dr.dt().bits(), dr.du().bits());
Ok(day as u8)
}
fn get_month(&mut self) -> Result<u8, Self::Error> {
let dr = self.regs.dr.read();
let mt: u8 = if dr.mt().bit() { 1 } else { 0 };
let month = bcd2_decode(mt, dr.mu().bits());
Ok(month as u8)
}
fn get_year(&mut self) -> Result<u16, Self::Error> {
let dr = self.regs.dr.read();
let year = bcd2_decode(dr.yt().bits(), dr.yu().bits());
Ok(year as u16)
}
fn get_date(&mut self) -> Result<NaiveDate, Self::Error> {
let day = self.get_day()?;
let month = self.get_month()?;
let year = self.get_year()?;
Ok(NaiveDate::from_ymd(year.into(), month.into(), day.into()))
}
fn get_datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
self.set_24h_fmt();
let day = self.get_day()?;
let month = self.get_month()?;
let year = self.get_year()?;
let seconds = self.get_seconds()?;
let minutes = self.get_minutes()?;
let hours = hours_to_u8(self.get_hours()?)?;
Ok(
NaiveDate::from_ymd(year.into(), month.into(), day.into()).and_hms(
hours.into(),
minutes.into(),
seconds.into(),
),
)
}
}
fn bcd2_encode(word: u32) -> Result<(u8, u8), Error> {
let l = match (word / 10).try_into() {
Ok(v) => v,
Err(_) => {
return Err(Error::InvalidInputData);
}
};
let r = match (word % 10).try_into() {
Ok(v) => v,
Err(_) => {
return Err(Error::InvalidInputData);
}
};
Ok((l, r))
}
fn bcd2_decode(fst: u8, snd: u8) -> u32 {
(fst * 10 + snd).into()
}
fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> {
match hours {
Hours::H24(h) => Ok(bcd2_encode(h as u32))?,
Hours::AM(h) => Ok(bcd2_encode((h - 1) as u32))?,
Hours::PM(h) => Ok(bcd2_encode((h + 11) as u32))?,
}
}
fn hours_to_u8(hours: Hours) -> Result<u8, Error> {
if let Hours::H24(h) = hours {
Ok(h)
} else {
Err(Error::InvalidInputData)
}
}