diff --git a/Cargo.toml b/Cargo.toml index 50d22df8..769f5b15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,9 +69,11 @@ stm32l431 = [ "stm32l4/stm32l4x1" ] stm32l451 = [ "stm32l4/stm32l4x1" ] stm32l471 = [ "stm32l4/stm32l4x1" ] +# L412 +stm32l412 = [ "stm32l4/stm32l412" ] +stm32l422 = [ "stm32l4/stm32l412" ] + # L4x2 -stm32l412 = [ "stm32l4/stm32l4x2" ] -stm32l422 = [ "stm32l4/stm32l4x2" ] stm32l432 = [ "stm32l4/stm32l4x2" ] stm32l442 = [ "stm32l4/stm32l4x2" ] stm32l452 = [ "stm32l4/stm32l4x2" ] diff --git a/src/lib.rs b/src/lib.rs index f2421d7e..87ea2daf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,9 +86,9 @@ pub use stm32l4; #[cfg(any(feature = "stm32l431", feature = "stm32l451", feature = "stm32l471"))] pub use stm32l4::stm32l4x1 as pac; +#[cfg(any(feature = "stm32l412", feature = "stm32l422"))] +pub use stm32l4::stm32l412 as pac; #[cfg(any( - feature = "stm32l412", - feature = "stm32l422", feature = "stm32l432", feature = "stm32l442", feature = "stm32l452", diff --git a/src/rtc.rs b/src/rtc.rs index cecfc6df..eb54b466 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -1,5 +1,37 @@ //! RTC peripheral abstraction +/// refer to AN4759 to compare features of RTC2 and RTC3 +#[cfg(not(any( + feature = "stm32l412", + feature = "stm32l422", + feature = "stm32l4p5", + feature = "stm32l4q5" +)))] +pub mod rtc2; +#[cfg(not(any( + feature = "stm32l412", + feature = "stm32l422", + feature = "stm32l4p5", + feature = "stm32l4q5" +)))] +pub use rtc2 as rtc_registers; + +/// refer to AN4759 to compare features of RTC2 and RTC3 +#[cfg(any( + feature = "stm32l412", + feature = "stm32l422", + feature = "stm32l4p5", + feature = "stm32l4q5" +))] +pub mod rtc3; +#[cfg(any( + feature = "stm32l412", + feature = "stm32l422", + feature = "stm32l4p5", + feature = "stm32l4q5" +))] +pub use rtc3 as rtc_registers; + use void::Void; use crate::{ @@ -214,10 +246,9 @@ impl Rtc { self.write(false, |rtc| match alarm { Alarm::AlarmA => { - rtc.cr.modify(|_, w| w.alrae().clear_bit()); - - // Wait until we're allowed to update the alarm b configuration - while rtc.isr.read().alrawf().bit_is_clear() {} + rtc.cr.modify(|_, w| w.alrae().clear_bit()); // Disable Alarm A + rtc_registers::clear_alarm_a_flag(rtc); + while !rtc_registers::is_alarm_a_accessible(rtc) {} rtc.alrmar.modify(|_, w| unsafe { w.dt() @@ -241,13 +272,19 @@ impl Rtc { .wdsel() .clear_bit() }); + // binary mode alarm not implemented (RTC3 only) + // subsecond alarm not implemented + // would need a conversion method between `time.micros` and RTC ticks + // write the SS value and mask to `rtc.alrmassr` + + // enable alarm and reenable interrupt if it was enabled rtc.cr.modify(|_, w| w.alrae().set_bit()); } Alarm::AlarmB => { rtc.cr.modify(|_, w| w.alrbe().clear_bit()); - // Wait until we're allowed to update the alarm b configuration - while rtc.isr.read().alrbwf().bit_is_clear() {} + rtc_registers::clear_alarm_b_flag(rtc); + while !rtc_registers::is_alarm_b_accessible(rtc) {} rtc.alrmbr.modify(|_, w| unsafe { w.dt() @@ -271,10 +308,15 @@ impl Rtc { .wdsel() .clear_bit() }); + // binary mode alarm not implemented (RTC3 only) + // subsecond alarm not implemented + // would need a conversion method between `time.micros` and RTC ticks + // write the SS value and mask to `rtc.alrmbssr` + + // enable alarm and reenable interrupt if it was enabled rtc.cr.modify(|_, w| w.alrbe().set_bit()); } }); - self.check_interrupt(alarm.into(), true); } /// Starts listening for an interrupt event @@ -334,27 +376,27 @@ impl Rtc { /// Checks for an interrupt event pub fn check_interrupt(&mut self, event: Event, clear: bool) -> bool { let result = match event { - Event::WakeupTimer => self.rtc.isr.read().wutf().bit_is_set(), - Event::AlarmA => self.rtc.isr.read().alraf().bit_is_set(), - Event::AlarmB => self.rtc.isr.read().alrbf().bit_is_set(), - Event::Timestamp => self.rtc.isr.read().tsf().bit_is_set(), + Event::WakeupTimer => rtc_registers::is_wakeup_timer_flag_set(&self.rtc), + Event::AlarmA => rtc_registers::is_alarm_a_flag_set(&self.rtc), + Event::AlarmB => rtc_registers::is_alarm_b_flag_set(&self.rtc), + Event::Timestamp => rtc_registers::is_timestamp_flag_set(&self.rtc), }; if clear { self.write(false, |rtc| match event { Event::WakeupTimer => { - rtc.isr.modify(|_, w| w.wutf().clear_bit()); + rtc_registers::clear_wakeup_timer_flag(rtc); unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 20)) }; } Event::AlarmA => { - rtc.isr.modify(|_, w| w.alraf().clear_bit()); + rtc_registers::clear_alarm_a_flag(rtc); unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 18)) }; } Event::AlarmB => { - rtc.isr.modify(|_, w| w.alrbf().clear_bit()); + rtc_registers::clear_alarm_b_flag(rtc); unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 18)) }; } Event::Timestamp => { - rtc.isr.modify(|_, w| w.tsf().clear_bit()); + rtc_registers::clear_timestamp_flag(rtc); unsafe { (*EXTI::ptr()).pr1.write(|w| w.bits(1 << 19)) }; } }) @@ -427,8 +469,7 @@ impl Rtc { }); // TODO configuration for output pins - rtc.or - .modify(|_, w| w.rtc_alarm_type().clear_bit().rtc_out_rmp().clear_bit()); + rtc_registers::reset_gpio(rtc); }); self.rtc_config = rtc_config; @@ -448,16 +489,16 @@ impl Rtc { self.rtc.wpr.write(|w| unsafe { w.key().bits(0xca) }); self.rtc.wpr.write(|w| unsafe { w.key().bits(0x53) }); - if init_mode && self.rtc.isr.read().initf().bit_is_clear() { - // are we already in init mode? - self.rtc.isr.modify(|_, w| w.init().set_bit()); - while self.rtc.isr.read().initf().bit_is_clear() {} // wait to return to init state + if init_mode && !rtc_registers::is_init_mode(&self.rtc) { + rtc_registers::enter_init_mode(&self.rtc); + // wait till init state entered + // ~2 RTCCLK cycles + while !rtc_registers::is_init_mode(&self.rtc) {} } let result = f(&self.rtc); - if init_mode { - self.rtc.isr.modify(|_, w| w.init().clear_bit()); // Exits init mode + rtc_registers::exit_init_mode(&self.rtc); } // Re-enable write protection. @@ -467,16 +508,14 @@ impl Rtc { result } + pub const BACKUP_REGISTER_COUNT: usize = rtc_registers::BACKUP_REGISTER_COUNT; + /// Read content of the backup register. /// /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn read_backup_register(&self, register: usize) -> Option { - if register < 32 { - Some(self.rtc.bkpr[register].read().bits()) - } else { - None - } + rtc_registers::read_backup_register(&self.rtc, register) } /// Set content of the backup register. @@ -484,9 +523,7 @@ impl Rtc { /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn write_backup_register(&self, register: usize, value: u32) { - if register < 32 { - unsafe { self.rtc.bkpr[register].write(|w| w.bits(value)) } - } + rtc_registers::write_backup_register(&self.rtc, register, value) } } @@ -572,7 +609,7 @@ impl timer::CountDown for WakeupTimer<'_> { // Let's wait for WUTWF to clear. Otherwise we might run into a race // condition, if the user calls this method again really quickly. - while self.rtc.rtc.isr.read().wutwf().bit_is_set() {} + while rtc_registers::is_wakeup_timer_write_flag_set(&self.rtc.rtc) {} } fn wait(&mut self) -> nb::Result<(), Void> { @@ -591,12 +628,8 @@ impl timer::Cancel for WakeupTimer<'_> { self.rtc.write(false, |rtc| { // Disable the wakeup timer rtc.cr.modify(|_, w| w.wute().clear_bit()); - - // Wait until we're allowed to update the wakeup timer configuration - while rtc.isr.read().wutwf().bit_is_clear() {} - - // Clear wakeup timer flag - rtc.isr.modify(|_, w| w.wutf().clear_bit()); + while rtc_registers::is_wakeup_timer_write_flag_set(&rtc) {} + rtc_registers::clear_wakeup_timer_flag(rtc); // According to the reference manual, section 26.7.4, the WUTF flag // must be cleared at least 1.5 RTCCLK periods "before WUTF is set diff --git a/src/rtc/rtc2.rs b/src/rtc/rtc2.rs new file mode 100644 index 00000000..6ce33ca4 --- /dev/null +++ b/src/rtc/rtc2.rs @@ -0,0 +1,117 @@ +use crate::pac::RTC; + +pub fn reset_gpio(rtc: &RTC) { + rtc.or + .modify(|_, w| w.rtc_alarm_type().clear_bit().rtc_out_rmp().clear_bit()); +} + +/// true if initf bit indicates RTC peripheral is in init mode +pub fn is_init_mode(rtc: &RTC) -> bool { + rtc.isr.read().initf().bit_is_set() +} + +/// to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode +pub fn enter_init_mode(rtc: &RTC) { + rtc.isr.modify(|_, w| w.init().set_bit()); +} + +/// counting will restart in 4 RTCCLK cycles +pub fn exit_init_mode(rtc: &RTC) { + rtc.isr.modify(|_, w| w.init().clear_bit()); // Exits init mode +} + +/// has wakeup timer expired? +pub fn is_wakeup_timer_flag_set(rtc: &RTC) -> bool { + rtc.isr.read().wutf().bit_is_set() +} + +pub fn is_wakeup_timer_write_flag_set(rtc: &RTC) -> bool { + rtc.isr.read().wutwf().bit_is_set() +} + +/// clear the wakeup timer flag +pub fn clear_wakeup_timer_flag(rtc: &RTC) { + rtc.isr.modify(|_, w| w.wutf().clear_bit()); +} + +/// has alarm A been triggered +pub fn is_alarm_a_flag_set(rtc: &RTC) -> bool { + rtc.isr.read().alraf().bit_is_set() +} + +/// clear the alarm A flag +pub fn clear_alarm_a_flag(rtc: &RTC) { + rtc.isr.modify(|_, w| w.alraf().clear_bit()); +} + +/// has alarm B been triggered? +pub fn is_alarm_b_flag_set(rtc: &RTC) -> bool { + rtc.isr.read().alrbf().bit_is_set() +} + +/// clear the alarm B flag +pub fn clear_alarm_b_flag(rtc: &RTC) { + rtc.isr.modify(|_, w| w.alrbf().clear_bit()); +} + +/// has timestamp event triggered +pub fn is_timestamp_flag_set(rtc: &RTC) -> bool { + rtc.isr.read().tsf().bit_is_set() +} + +/// clear the timestamp event flag +pub fn clear_timestamp_flag(rtc: &RTC) { + rtc.isr.modify(|_, w| w.tsf().clear_bit()); +} + +pub fn is_alarm_a_accessible(rtc: &RTC) -> bool { + rtc.isr.read().alrawf().bit_is_clear() +} + +pub fn is_alarm_b_accessible(rtc: &RTC) -> bool { + rtc.isr.read().alrbwf().bit_is_clear() +} + +// AN7459 +// L4 series except L41/2 has 20 backup registers +// L41/2, L4P/Q and L4R/S have 32 backup registers +#[cfg(not(any( + feature = "stm32l4r5", + feature = "stm32l4s5", + feature = "stm32l4r7", + feature = "stm32l4s7", + feature = "stm32l4r9", + feature = "stm32l4s9" +)))] +pub const BACKUP_REGISTER_COUNT: usize = 20; +#[cfg(any( + feature = "stm32l4r5", + feature = "stm32l4s5", + feature = "stm32l4r7", + feature = "stm32l4s7", + feature = "stm32l4r9", + feature = "stm32l4s9" +))] +pub const BACKUP_REGISTER_COUNT: usize = 32; + +/// Read content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn read_backup_register(rtc: &RTC, register: usize) -> Option { + if register < BACKUP_REGISTER_COUNT { + Some(rtc.bkpr[register].read().bits()) + } else { + None + } +} + +/// Set content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn write_backup_register(rtc: &RTC, register: usize, value: u32) { + if register < BACKUP_REGISTER_COUNT { + unsafe { rtc.bkpr[register].write(|w| w.bits(value)) } + } +} diff --git a/src/rtc/rtc3.rs b/src/rtc/rtc3.rs new file mode 100644 index 00000000..7259fef2 --- /dev/null +++ b/src/rtc/rtc3.rs @@ -0,0 +1,111 @@ +use crate::pac::RTC; + +pub fn reset_gpio(rtc: &RTC) { + rtc.cr.modify(|_, w| { + w.out2en() + .clear_bit() + .tampalrm_type() + .clear_bit() + .tampalrm_pu() + .clear_bit() + }); +} + +/// true if initf bit indicates RTC peripheral is in init mode +pub fn is_init_mode(rtc: &RTC) -> bool { + rtc.icsr.read().initf().bit_is_set() +} + +/// to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode +pub fn enter_init_mode(rtc: &RTC) { + rtc.icsr.modify(|_, w| w.init().set_bit()); +} + +/// counting will restart in 4 RTCCLK cycles +pub fn exit_init_mode(rtc: &RTC) { + rtc.icsr.modify(|_, w| w.init().clear_bit()); // Exits init mode +} + +/// has wakeup timer expired? +pub fn is_wakeup_timer_flag_set(rtc: &RTC) -> bool { + rtc.sr.read().wutf().bit_is_set() +} + +/// are WUT settings modifiable +pub fn is_wakeup_timer_write_flag_set(rtc: &RTC) -> bool { + rtc.icsr.read().wutwf().bit_is_set() +} + +/// clear the wakeup timer flag +pub fn clear_wakeup_timer_flag(rtc: &RTC) { + rtc.scr.write(|w| w.cwutf().set_bit()); +} + +/// has alarm A been triggered +pub fn is_alarm_a_flag_set(rtc: &RTC) -> bool { + rtc.sr.read().alraf().bit_is_set() +} + +/// clear the alarm A flag +pub fn clear_alarm_a_flag(rtc: &RTC) { + rtc.scr.write(|w| w.calraf().set_bit()); +} + +/// has alarm B been triggered? +pub fn is_alarm_b_flag_set(rtc: &RTC) -> bool { + rtc.sr.read().alrbf().bit_is_set() +} + +/// clear the alarm B flag +pub fn clear_alarm_b_flag(rtc: &RTC) { + rtc.scr.write(|w| w.calrbf().set_bit()); +} + +/// has timestamp event triggered +pub fn is_timestamp_flag_set(rtc: &RTC) -> bool { + rtc.sr.read().tsf().bit_is_set() +} + +/// clear the timestamp event flag +pub fn clear_timestamp_flag(rtc: &RTC) { + rtc.scr.write(|w| w.ctsf().set_bit()); +} + +pub fn is_alarm_a_accessible(_rtc: &RTC) -> bool { + // RTC type 3 has no wait after disabling the alarm (AN4759 - Rev 7, Table 8) + true +} + +pub fn is_alarm_b_accessible(_rtc: &RTC) -> bool { + // RTC type 3 has no wait after disabling the alarm (AN4759 - Rev 7, Table 8) + true +} + +// AN7459 +// L4 series except L41/2 has 20 backup registers +// L41/2, L4P/Q and L4R/S have 32 backup registers +pub const BACKUP_REGISTER_COUNT: usize = 32; + +/// Read content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn read_backup_register(_rtc: &RTC, register: usize) -> Option { + if register < BACKUP_REGISTER_COUNT { + //Some(rtc.bkpr[register].read().bits()) + None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not even in the L412 PAC + } else { + None + } +} + +/// Set content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn write_backup_register(_rtc: &RTC, register: usize, _value: u32) { + if register < BACKUP_REGISTER_COUNT { + // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not even in the L412 PAC + //unsafe { self.rtc.bkpr[register].write(|w| w.bits(value)) } + } +}