2022-09-16 06:45:27 +02:00
mod filter ;
2023-07-28 13:23:22 +02:00
use embassy_hal_internal ::{ into_ref , Peripheral , PeripheralRef } ;
2022-09-16 06:45:27 +02:00
pub use self ::filter ::DateTimeFilter ;
#[ cfg_attr(feature = " chrono " , path = " datetime_chrono.rs " ) ]
#[ cfg_attr(not(feature = " chrono " ), path = " datetime_no_deps.rs " ) ]
mod datetime ;
pub use self ::datetime ::{ DateTime , DayOfWeek , Error as DateTimeError } ;
use crate ::clocks ::clk_rtc_freq ;
/// A reference to the real time clock of the system
2023-07-11 18:41:45 +02:00
pub struct Rtc < ' d , T : Instance > {
2022-09-16 06:45:27 +02:00
inner : PeripheralRef < ' d , T > ,
}
2023-07-11 18:41:45 +02:00
impl < ' d , T : Instance > Rtc < ' d , T > {
2022-09-16 06:45:27 +02:00
/// Create a new instance of the real time clock, with the given date as an initial value.
///
/// # Errors
///
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
2023-07-11 18:41:45 +02:00
pub fn new ( inner : impl Peripheral < P = T > + ' d ) -> Self {
2022-09-16 06:45:27 +02:00
into_ref! ( inner ) ;
// Set the RTC divider
2023-06-16 01:32:18 +02:00
inner . regs ( ) . clkdiv_m1 ( ) . write ( | w | w . set_clkdiv_m1 ( clk_rtc_freq ( ) - 1 ) ) ;
2022-09-16 06:45:27 +02:00
2023-07-11 18:41:45 +02:00
let result = Self { inner } ;
result
2022-09-16 06:45:27 +02:00
}
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
///
/// Leap year checking is enabled by default.
pub fn set_leap_year_check ( & mut self , leap_year_check_enabled : bool ) {
2023-06-16 01:32:18 +02:00
self . inner . regs ( ) . ctrl ( ) . modify ( | w | {
w . set_force_notleapyear ( ! leap_year_check_enabled ) ;
} ) ;
2022-09-16 06:45:27 +02:00
}
2023-07-12 14:13:19 +02:00
/// Set the time from internal format
pub fn restore ( & mut self , ymd : rp_pac ::rtc ::regs ::Rtc1 , hms : rp_pac ::rtc ::regs ::Rtc0 ) {
// disable RTC while we configure it
self . inner . regs ( ) . ctrl ( ) . modify ( | w | w . set_rtc_enable ( false ) ) ;
while self . inner . regs ( ) . ctrl ( ) . read ( ) . rtc_active ( ) {
core ::hint ::spin_loop ( ) ;
}
self . inner . regs ( ) . setup_0 ( ) . write ( | w | {
* w = rp_pac ::rtc ::regs ::Setup0 ( ymd . 0 ) ;
} ) ;
self . inner . regs ( ) . setup_1 ( ) . write ( | w | {
* w = rp_pac ::rtc ::regs ::Setup1 ( hms . 0 ) ;
} ) ;
// Load the new datetime and re-enable RTC
self . inner . regs ( ) . ctrl ( ) . write ( | w | w . set_load ( true ) ) ;
self . inner . regs ( ) . ctrl ( ) . write ( | w | w . set_rtc_enable ( true ) ) ;
while ! self . inner . regs ( ) . ctrl ( ) . read ( ) . rtc_active ( ) {
core ::hint ::spin_loop ( ) ;
}
}
/// Get the time in internal format
pub fn save ( & mut self ) -> ( rp_pac ::rtc ::regs ::Rtc1 , rp_pac ::rtc ::regs ::Rtc0 ) {
let rtc_0 : rp_pac ::rtc ::regs ::Rtc0 = self . inner . regs ( ) . rtc_0 ( ) . read ( ) ;
let rtc_1 = self . inner . regs ( ) . rtc_1 ( ) . read ( ) ;
( rtc_1 , rtc_0 )
}
2023-07-11 18:41:45 +02:00
/// Checks to see if this Rtc is running
2022-09-16 06:45:27 +02:00
pub fn is_running ( & self ) -> bool {
2023-06-16 01:32:18 +02:00
self . inner . regs ( ) . ctrl ( ) . read ( ) . rtc_active ( )
2022-09-16 06:45:27 +02:00
}
/// Set the datetime to a new value.
///
/// # Errors
///
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
pub fn set_datetime ( & mut self , t : DateTime ) -> Result < ( ) , RtcError > {
self ::datetime ::validate_datetime ( & t ) . map_err ( RtcError ::InvalidDateTime ) ? ;
// disable RTC while we configure it
2023-06-16 01:32:18 +02:00
self . inner . regs ( ) . ctrl ( ) . modify ( | w | w . set_rtc_enable ( false ) ) ;
while self . inner . regs ( ) . ctrl ( ) . read ( ) . rtc_active ( ) {
core ::hint ::spin_loop ( ) ;
}
self . inner . regs ( ) . setup_0 ( ) . write ( | w | {
self ::datetime ::write_setup_0 ( & t , w ) ;
} ) ;
self . inner . regs ( ) . setup_1 ( ) . write ( | w | {
self ::datetime ::write_setup_1 ( & t , w ) ;
} ) ;
// Load the new datetime and re-enable RTC
self . inner . regs ( ) . ctrl ( ) . write ( | w | w . set_load ( true ) ) ;
self . inner . regs ( ) . ctrl ( ) . write ( | w | w . set_rtc_enable ( true ) ) ;
while ! self . inner . regs ( ) . ctrl ( ) . read ( ) . rtc_active ( ) {
core ::hint ::spin_loop ( ) ;
2022-09-16 06:45:27 +02:00
}
Ok ( ( ) )
}
/// Return the current datetime.
///
/// # Errors
///
/// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
pub fn now ( & self ) -> Result < DateTime , RtcError > {
if ! self . is_running ( ) {
return Err ( RtcError ::NotRunning ) ;
}
2023-06-16 01:32:18 +02:00
let rtc_0 = self . inner . regs ( ) . rtc_0 ( ) . read ( ) ;
let rtc_1 = self . inner . regs ( ) . rtc_1 ( ) . read ( ) ;
2022-09-16 06:45:27 +02:00
self ::datetime ::datetime_from_registers ( rtc_0 , rtc_1 ) . map_err ( RtcError ::InvalidDateTime )
}
/// Disable the alarm that was scheduled with [`schedule_alarm`].
///
/// [`schedule_alarm`]: #method.schedule_alarm
pub fn disable_alarm ( & mut self ) {
2023-06-16 01:32:18 +02:00
self . inner . regs ( ) . irq_setup_0 ( ) . modify ( | s | s . set_match_ena ( false ) ) ;
2022-09-16 06:45:27 +02:00
2023-06-16 01:32:18 +02:00
while self . inner . regs ( ) . irq_setup_0 ( ) . read ( ) . match_active ( ) {
core ::hint ::spin_loop ( ) ;
2022-09-16 06:45:27 +02:00
}
}
/// Schedule an alarm. The `filter` determines at which point in time this alarm is set.
///
/// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call:
/// ```no_run
/// # #[cfg(feature = "chrono")]
/// # fn main() { }
/// # #[cfg(not(feature = "chrono"))]
/// # fn main() {
2023-07-11 18:41:45 +02:00
/// # use embassy_rp::rtc::{Rtc, DateTimeFilter};
/// # let mut real_time_clock: Rtc<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
2022-09-16 06:45:27 +02:00
/// let now = real_time_clock.now().unwrap();
/// real_time_clock.schedule_alarm(
/// DateTimeFilter::default()
/// .minute(if now.minute == 59 { 0 } else { now.minute + 1 })
/// );
/// # }
/// ```
pub fn schedule_alarm ( & mut self , filter : DateTimeFilter ) {
self . disable_alarm ( ) ;
2023-06-16 01:32:18 +02:00
self . inner . regs ( ) . irq_setup_0 ( ) . write ( | w | {
filter . write_setup_0 ( w ) ;
} ) ;
self . inner . regs ( ) . irq_setup_1 ( ) . write ( | w | {
filter . write_setup_1 ( w ) ;
} ) ;
self . inner . regs ( ) . inte ( ) . modify ( | w | w . set_rtc ( true ) ) ;
// Set the enable bit and check if it is set
self . inner . regs ( ) . irq_setup_0 ( ) . modify ( | w | w . set_match_ena ( true ) ) ;
while ! self . inner . regs ( ) . irq_setup_0 ( ) . read ( ) . match_active ( ) {
core ::hint ::spin_loop ( ) ;
2022-09-16 06:45:27 +02:00
}
}
/// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered,
/// or the next [`schedule_alarm`] will never fire.
///
/// [`schedule_alarm`]: #method.schedule_alarm
pub fn clear_interrupt ( & mut self ) {
self . disable_alarm ( ) ;
}
}
2023-07-11 18:41:45 +02:00
/// Errors that can occur on methods on [Rtc]
2022-09-16 06:45:27 +02:00
#[ derive(Clone, Debug, PartialEq, Eq) ]
pub enum RtcError {
/// An invalid DateTime was given or stored on the hardware.
InvalidDateTime ( DateTimeError ) ,
/// The RTC clock is not running
NotRunning ,
}
mod sealed {
pub trait Instance {
fn regs ( & self ) -> crate ::pac ::rtc ::Rtc ;
}
}
pub trait Instance : sealed ::Instance { }
impl sealed ::Instance for crate ::peripherals ::RTC {
fn regs ( & self ) -> crate ::pac ::rtc ::Rtc {
crate ::pac ::RTC
}
}
impl Instance for crate ::peripherals ::RTC { }