diff --git a/examples/rtc.rs b/examples/rtc.rs new file mode 100644 index 00000000..31e7103d --- /dev/null +++ b/examples/rtc.rs @@ -0,0 +1,47 @@ +#![no_main] +#![no_std] + +use hal::{ + delay::SYSTDelayExt, + rcc::RccExt, + rtc::RtcExt, + stm32::Peripherals, + time::{Date, ExtU32, Month, MonthDay, Time, Year}, +}; +use stm32g4xx_hal as hal; + +use cortex_m_rt::entry; + +use utils::logger::info; + +#[macro_use] +mod utils; + +#[entry] +fn main() -> ! { + utils::logger::init(); + + info!("start"); + + let dp = Peripherals::take().unwrap(); + let cp = cortex_m::Peripherals::take().expect("cannot take core peripherals"); + + info!("rcc"); + + let mut rcc = dp.RCC.constrain(); + + info!("Setup rtc"); + let mut delay = cp.SYST.delay(&rcc.clocks); + let mut rtc = dp.RTC.constrain(&mut rcc); + + info!("Setup date"); + rtc.set_date(&Date::new(Year(2024), Month(8), MonthDay(5))); + rtc.set_time(&Time::new(0.hours(), 59.minutes(), 2.secs(), true)); + + info!("Enter Loop"); + + loop { + info!("Timestamp: {:?} {:?}", rtc.get_date(), rtc.get_time()); + delay.delay_ms(500); + } +} diff --git a/src/lib.rs b/src/lib.rs index 5441692a..7814d7d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,7 @@ pub mod pwm; pub mod pwr; // pub mod qei; pub mod rcc; +pub mod rtc; // pub mod rng; pub mod serial; pub mod signature; diff --git a/src/rcc/config.rs b/src/rcc/config.rs index b53a280a..5c60ea1a 100644 --- a/src/rcc/config.rs +++ b/src/rcc/config.rs @@ -46,6 +46,16 @@ pub enum PLLSrc { HSE_BYPASS(Hertz), } +/// RTC clock input source +#[derive(Clone, Copy)] +pub enum RtcSrc { + LSE, + LSE_BYPASS, + LSI, + HSE, + HSE_BYPASS, +} + /// Divider for the PLL clock input (M) /// This must be set based on the input clock to keep the PLL input frequency within the limits /// specified in the datasheet. diff --git a/src/rcc/mod.rs b/src/rcc/mod.rs index e209a1d4..24cf024c 100644 --- a/src/rcc/mod.rs +++ b/src/rcc/mod.rs @@ -231,12 +231,6 @@ impl Rcc { } } - pub fn unlock_rtc(&mut self) { - self.rb.apb1enr1.modify(|_, w| w.pwren().set_bit()); - let pwr = unsafe { &(*PWR::ptr()) }; - pwr.cr1.modify(|_, w| w.dbp().set_bit()); - } - fn config_pll(&self, pll_cfg: PllConfig) -> PLLClocks { // Disable PLL self.rb.cr.modify(|_, w| w.pllon().clear_bit()); @@ -457,6 +451,46 @@ impl Rcc { pub fn clear_reset_reason(&mut self) { self.rb.csr.modify(|_, w| w.rmvf().set_bit()); } + + pub(crate) fn unlock_rtc(&self) { + self.rb.apb1enr1.modify(|_, w| w.pwren().set_bit()); + let pwr = unsafe { &(*PWR::ptr()) }; + pwr.cr1.modify(|_, w| w.dbp().set_bit()); + while pwr.cr1.read().dbp().bit_is_clear() {} + } + + pub(crate) fn enable_rtc(&self, src: RtcSrc) { + self.unlock_rtc(); + self.rb + .apb1enr1 + .modify(|_, w| w.rtcapben().set_bit().pwren().set_bit()); + self.rb.apb1smenr1.modify(|_, w| w.rtcapbsmen().set_bit()); + self.rb.bdcr.modify(|_, w| w.bdrst().set_bit()); + + let rtc_sel = match src { + RtcSrc::LSE | RtcSrc::LSE_BYPASS => 0b01, + RtcSrc::LSI => 0b10, + RtcSrc::HSE | RtcSrc::HSE_BYPASS => 0b11, + }; + + self.rb.bdcr.modify(|_, w| { + w.rtcsel() + .bits(rtc_sel) + .rtcen() + .set_bit() + .bdrst() + .clear_bit() + }); + + self.unlock_rtc(); + match src { + RtcSrc::LSE => self.enable_lse(false), + RtcSrc::LSE_BYPASS => self.enable_lse(true), + RtcSrc::LSI => self.enable_lsi(), + RtcSrc::HSE => self.enable_hse(false), + RtcSrc::HSE_BYPASS => self.enable_hse(true), + }; + } } pub struct ResetReason { diff --git a/src/rtc.rs b/src/rtc.rs new file mode 100644 index 00000000..a5884c44 --- /dev/null +++ b/src/rtc.rs @@ -0,0 +1,394 @@ +//! Real Time Clock +use core::convert::TryInto; + +use crate::rcc::{Rcc, RtcSrc}; +use crate::stm32::RTC; +use crate::time::{self, *}; + +#[derive(Debug, PartialEq, Eq)] +pub enum RtcHourFormat { + H24, + H12, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum RtcCalibrationFrequency { + F1Hz, + F512Hz, +} + +pub enum Event { + WakeupTimer, + AlarmA, + AlarmB, + Timestamp, +} + +#[derive(Debug, Default, PartialEq, Eq)] +pub struct Alarm { + day: Option, + hours: Option, + minutes: Option, + seconds: Option, + subseconds: u16, + subseconds_mask_bits: u8, + use_weekday: bool, +} + +impl Alarm { + pub fn new() -> Self { + Self::default() + } + + pub fn set_month_day(mut self, day: time::MonthDay) -> Self { + self.use_weekday = false; + self.day = Some(day.0.try_into().unwrap()); + self + } + + pub fn set_week_day(mut self, day: time::WeekDay) -> Self { + self.use_weekday = true; + self.day = Some(day.0.try_into().unwrap()); + self + } + + pub fn set_hours(mut self, val: time::Hour) -> Self { + self.hours = Some(val.ticks().try_into().unwrap()); + self + } + + pub fn set_minutes(mut self, val: time::Minute) -> Self { + self.minutes = Some(val.ticks().try_into().unwrap()); + self + } + + pub fn set_seconds(mut self, val: time::Second) -> Self { + self.seconds = Some(val.ticks().try_into().unwrap()); + self + } + + pub fn set_subseconds(mut self, subseconds: u16, mask_bits: u8) -> Self { + self.subseconds_mask_bits = mask_bits; + self.subseconds = subseconds; + self + } + + pub fn mask_day(mut self) -> Self { + self.day = None; + self + } + + pub fn mask_hours(mut self) -> Self { + self.hours = None; + self + } + + pub fn mask_minutes(mut self) -> Self { + self.minutes = None; + self + } + + pub fn mask_seconds(mut self) -> Self { + self.seconds = None; + self + } +} + +impl From