mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-23 20:51:31 +11:00
Added RtcClock DateTime and alarms (#213)
* Added RealTimeClock, DateTime and RTC alarms * Improved documentation on weird behaviors in the RealTimeClock * Fixed incorrect leap_year_check in RealTimeClock * Fixed rtc-datetime PR feedback
This commit is contained in:
parent
de53600199
commit
f68f148d12
4
.github/workflows/check.yml
vendored
4
.github/workflows/check.yml
vendored
|
@ -22,3 +22,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --doc --target x86_64-unknown-linux-gnu
|
args: --doc --target x86_64-unknown-linux-gnu
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --doc --target x86_64-unknown-linux-gnu --features chrono
|
||||||
|
|
|
@ -25,6 +25,7 @@ void = { version = "1.0.2", default-features = false }
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
|
|
||||||
futures = { version = "0.3", default-features = false, optional = true }
|
futures = { version = "0.3", default-features = false, optional = true }
|
||||||
|
chrono = { version = "0.4", default-features = false, optional = true }
|
||||||
|
|
||||||
# namespaced features will let use use "dep:embassy-traits" in the features rather than using this
|
# namespaced features will let use use "dep:embassy-traits" in the features rather than using this
|
||||||
# trick of renaming the crate.
|
# trick of renaming the crate.
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
//! Real Time Clock (RTC)
|
|
||||||
// See [Chapter 4 Section 8](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
|
||||||
// TODO
|
|
67
rp2040-hal/src/rtc/datetime_chrono.rs
Normal file
67
rp2040-hal/src/rtc/datetime_chrono.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use chrono::{Datelike, Timelike};
|
||||||
|
use rp2040_pac::rtc::{rtc_0, rtc_1, setup_0, setup_1};
|
||||||
|
|
||||||
|
/// Alias for [`chrono::NaiveDateTime`]
|
||||||
|
pub type DateTime = chrono::NaiveDateTime;
|
||||||
|
/// Alias for [`chrono::Weekday`]
|
||||||
|
pub type DayOfWeek = chrono::Weekday;
|
||||||
|
|
||||||
|
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||||
|
///
|
||||||
|
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Error {
|
||||||
|
/// The [DateTime] has an invalid year. The year must be between 0 and 4095.
|
||||||
|
InvalidYear,
|
||||||
|
/// The [DateTime] contains an invalid date.
|
||||||
|
InvalidDate,
|
||||||
|
/// The [DateTime] contains an invalid time.
|
||||||
|
InvalidTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||||
|
dotw.num_days_from_sunday() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||||
|
if dt.year() < 0 || dt.year() > 4095 {
|
||||||
|
// rp2040 can't hold these years
|
||||||
|
Err(Error::InvalidYear)
|
||||||
|
} else {
|
||||||
|
// The rest of the chrono date is assumed to be valid
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_setup_0(dt: &DateTime, w: &mut setup_0::W) {
|
||||||
|
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||||
|
unsafe {
|
||||||
|
w.year().bits(dt.year() as u16);
|
||||||
|
w.month().bits(dt.month() as u8);
|
||||||
|
w.day().bits(dt.day() as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_setup_1(dt: &DateTime, w: &mut setup_1::W) {
|
||||||
|
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||||
|
unsafe {
|
||||||
|
w.dotw().bits(dt.weekday().num_days_from_sunday() as u8);
|
||||||
|
w.hour().bits(dt.hour() as u8);
|
||||||
|
w.min().bits(dt.minute() as u8);
|
||||||
|
w.sec().bits(dt.second() as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn datetime_from_registers(rtc_0: rtc_0::R, rtc_1: rtc_1::R) -> Result<DateTime, Error> {
|
||||||
|
let year = rtc_1.year().bits() as i32;
|
||||||
|
let month = rtc_1.month().bits() as u32;
|
||||||
|
let day = rtc_1.day().bits() as u32;
|
||||||
|
|
||||||
|
let hour = rtc_0.hour().bits() as u32;
|
||||||
|
let minute = rtc_0.min().bits() as u32;
|
||||||
|
let second = rtc_0.sec().bits() as u32;
|
||||||
|
|
||||||
|
let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?;
|
||||||
|
let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?;
|
||||||
|
Ok(DateTime::new(date, time))
|
||||||
|
}
|
133
rp2040-hal/src/rtc/datetime_no_deps.rs
Normal file
133
rp2040-hal/src/rtc/datetime_no_deps.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
use rp2040_pac::rtc::{rtc_0, rtc_1, setup_0, setup_1};
|
||||||
|
|
||||||
|
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||||
|
///
|
||||||
|
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Error {
|
||||||
|
/// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
|
||||||
|
InvalidYear,
|
||||||
|
/// The [DateTime] contains an invalid month value. Must be between `1..=12`.
|
||||||
|
InvalidMonth,
|
||||||
|
/// The [DateTime] contains an invalid day value. Must be between `1..=31`.
|
||||||
|
InvalidDay,
|
||||||
|
/// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
|
||||||
|
InvalidDayOfWeek(
|
||||||
|
/// The value of the DayOfWeek that was given.
|
||||||
|
u8,
|
||||||
|
),
|
||||||
|
/// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
|
||||||
|
InvalidHour,
|
||||||
|
/// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
|
||||||
|
InvalidMinute,
|
||||||
|
/// The [DateTime] contains an invalid second value. Must be between `0..=59`.
|
||||||
|
InvalidSecond,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure containing date and time information
|
||||||
|
pub struct DateTime {
|
||||||
|
/// 0..4095
|
||||||
|
pub year: u16,
|
||||||
|
/// 1..12, 1 is January
|
||||||
|
pub month: u8,
|
||||||
|
/// 1..28,29,30,31 depending on month
|
||||||
|
pub day: u8,
|
||||||
|
///
|
||||||
|
pub day_of_week: DayOfWeek,
|
||||||
|
/// 0..23
|
||||||
|
pub hour: u8,
|
||||||
|
/// 0..59
|
||||||
|
pub minute: u8,
|
||||||
|
/// 0..59
|
||||||
|
pub second: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A day of the week
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum DayOfWeek {
|
||||||
|
Sunday = 0,
|
||||||
|
Monday = 1,
|
||||||
|
Tuesday = 2,
|
||||||
|
Wednesday = 3,
|
||||||
|
Thursday = 4,
|
||||||
|
Friday = 5,
|
||||||
|
Saturday = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
|
||||||
|
Ok(match v {
|
||||||
|
0 => DayOfWeek::Sunday,
|
||||||
|
1 => DayOfWeek::Monday,
|
||||||
|
2 => DayOfWeek::Tuesday,
|
||||||
|
3 => DayOfWeek::Wednesday,
|
||||||
|
4 => DayOfWeek::Thursday,
|
||||||
|
5 => DayOfWeek::Friday,
|
||||||
|
6 => DayOfWeek::Saturday,
|
||||||
|
x => return Err(Error::InvalidDayOfWeek(x)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||||
|
dotw as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||||
|
if dt.year > 4095 {
|
||||||
|
Err(Error::InvalidYear)
|
||||||
|
} else if dt.month < 1 || dt.month > 12 {
|
||||||
|
Err(Error::InvalidMonth)
|
||||||
|
} else if dt.day < 1 || dt.day > 31 {
|
||||||
|
Err(Error::InvalidDay)
|
||||||
|
} else if dt.hour > 23 {
|
||||||
|
Err(Error::InvalidHour)
|
||||||
|
} else if dt.minute > 59 {
|
||||||
|
Err(Error::InvalidMinute)
|
||||||
|
} else if dt.second > 59 {
|
||||||
|
Err(Error::InvalidSecond)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_setup_0(dt: &DateTime, w: &mut setup_0::W) {
|
||||||
|
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||||
|
unsafe {
|
||||||
|
w.year().bits(dt.year);
|
||||||
|
w.month().bits(dt.month);
|
||||||
|
w.day().bits(dt.day);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn write_setup_1(dt: &DateTime, w: &mut setup_1::W) {
|
||||||
|
// Safety: the `.bits()` fields are marked `unsafe` but all bit values are valid
|
||||||
|
unsafe {
|
||||||
|
w.dotw().bits(dt.day_of_week as u8);
|
||||||
|
w.hour().bits(dt.hour);
|
||||||
|
w.min().bits(dt.minute);
|
||||||
|
w.sec().bits(dt.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn datetime_from_registers(rtc_0: rtc_0::R, rtc_1: rtc_1::R) -> Result<DateTime, Error> {
|
||||||
|
let year = rtc_1.year().bits();
|
||||||
|
let month = rtc_1.month().bits();
|
||||||
|
let day = rtc_1.day().bits();
|
||||||
|
|
||||||
|
let day_of_week = rtc_0.dotw().bits();
|
||||||
|
let hour = rtc_0.hour().bits();
|
||||||
|
let minute = rtc_0.min().bits();
|
||||||
|
let second = rtc_0.sec().bits();
|
||||||
|
|
||||||
|
let day_of_week = day_of_week_from_u8(day_of_week)?;
|
||||||
|
Ok(DateTime {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
day_of_week,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
})
|
||||||
|
}
|
120
rp2040-hal/src/rtc/filter.rs
Normal file
120
rp2040-hal/src/rtc/filter.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use super::DayOfWeek;
|
||||||
|
use rp2040_pac::rtc::{irq_setup_0, irq_setup_1};
|
||||||
|
|
||||||
|
/// A filter used for [`RealTimeClock::schedule_alarm`].
|
||||||
|
///
|
||||||
|
/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct DateTimeFilter {
|
||||||
|
/// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value.
|
||||||
|
pub year: Option<u16>,
|
||||||
|
/// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value.
|
||||||
|
pub month: Option<u8>,
|
||||||
|
/// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value.
|
||||||
|
pub day: Option<u8>,
|
||||||
|
/// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value.
|
||||||
|
pub day_of_week: Option<DayOfWeek>,
|
||||||
|
/// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value.
|
||||||
|
pub hour: Option<u8>,
|
||||||
|
/// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value.
|
||||||
|
pub minute: Option<u8>,
|
||||||
|
/// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value.
|
||||||
|
pub second: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DateTimeFilter {
|
||||||
|
/// Set a filter on the given year
|
||||||
|
pub fn year(mut self, year: u16) -> Self {
|
||||||
|
self.year = Some(year);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set a filter on the given month
|
||||||
|
pub fn month(mut self, month: u8) -> Self {
|
||||||
|
self.month = Some(month);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set a filter on the given day
|
||||||
|
pub fn day(mut self, day: u8) -> Self {
|
||||||
|
self.day = Some(day);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set a filter on the given day of the week
|
||||||
|
pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self {
|
||||||
|
self.day_of_week = Some(day_of_week);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set a filter on the given hour
|
||||||
|
pub fn hour(mut self, hour: u8) -> Self {
|
||||||
|
self.hour = Some(hour);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set a filter on the given minute
|
||||||
|
pub fn minute(mut self, minute: u8) -> Self {
|
||||||
|
self.minute = Some(minute);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set a filter on the given second
|
||||||
|
pub fn second(mut self, second: u8) -> Self {
|
||||||
|
self.second = Some(second);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register helper functions
|
||||||
|
impl DateTimeFilter {
|
||||||
|
pub(super) fn write_setup_0(&self, w: &mut irq_setup_0::W) {
|
||||||
|
// Safety: setting .bits() is considered unsafe because
|
||||||
|
// svd2rust doesn't know what the valid values are.
|
||||||
|
// But all values in these bitmasks are safe
|
||||||
|
if let Some(year) = self.year {
|
||||||
|
w.year_ena().set_bit();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
w.year().bits(year);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(month) = self.month {
|
||||||
|
w.month_ena().set_bit();
|
||||||
|
unsafe {
|
||||||
|
w.month().bits(month);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(day) = self.day {
|
||||||
|
w.day_ena().set_bit();
|
||||||
|
unsafe {
|
||||||
|
w.day().bits(day);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(super) fn write_setup_1(&self, w: &mut irq_setup_1::W) {
|
||||||
|
// Safety: setting .bits() is considered unsafe because
|
||||||
|
// svd2rust doesn't know what the valid values are.
|
||||||
|
// But all values in these bitmasks are safe
|
||||||
|
if let Some(day_of_week) = self.day_of_week {
|
||||||
|
w.dotw_ena().set_bit();
|
||||||
|
let bits = super::datetime::day_of_week_to_u8(day_of_week);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
w.dotw().bits(bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(hour) = self.hour {
|
||||||
|
w.hour_ena().set_bit();
|
||||||
|
unsafe {
|
||||||
|
w.hour().bits(hour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(minute) = self.minute {
|
||||||
|
w.min_ena().set_bit();
|
||||||
|
unsafe {
|
||||||
|
w.min().bits(minute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(second) = self.second {
|
||||||
|
w.sec_ena().set_bit();
|
||||||
|
unsafe {
|
||||||
|
w.sec().bits(second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
207
rp2040-hal/src/rtc/mod.rs
Normal file
207
rp2040-hal/src/rtc/mod.rs
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
//! Real time clock functionality
|
||||||
|
//!
|
||||||
|
//! A [`RealTimeClock`] can be configured with an initial [`DateTime`]. Afterwards the clock will track time automatically. The current `DateTime` can be retrieved by [`RealTimeClock::now()`].
|
||||||
|
//!
|
||||||
|
//! With the **chrono** feature enabled, the following types will be alias for chrono types:
|
||||||
|
//! - `DateTime`: `chrono::NaiveDateTime`
|
||||||
|
//! - `DayOfWeek`: `chrono::Weekday`
|
||||||
|
//!
|
||||||
|
//! # Notes
|
||||||
|
//!
|
||||||
|
//! There are some things to take into account. As per the datasheet:
|
||||||
|
//!
|
||||||
|
//! - **Day of week**: The RTC will not compute the correct day of the week; it will only increment the existing value.
|
||||||
|
//! - With the `chrono` feature, the day of week is calculated by chrono and should be correct. The value from the rp2040 itself is not used.
|
||||||
|
//! - **Leap year**: If the current year is evenly divisible by 4, a leap year is detected, then Feb 28th is followed by Feb 29th instead of March 1st.
|
||||||
|
//! - There are cases where this is incorrect, e.g. century years have no leap day, but the chip will still add a Feb 29th.
|
||||||
|
//! - To disable leap year checking and never have a Feb 29th, call `RealTimeClock::set_leap_year_check(false)`.
|
||||||
|
//!
|
||||||
|
//! Other limitations:
|
||||||
|
//!
|
||||||
|
//! - **Leap seconds**: The rp2040 will not take leap seconds into account
|
||||||
|
//! - With the `chrono` feature, leap seconds will be silently handled by `chrono`. This means there might be a slight difference between the value of [`RealTimeClock::now()`] and adding 2 times together in code.
|
||||||
|
|
||||||
|
use crate::clocks::Clock;
|
||||||
|
use crate::clocks::RtcClock;
|
||||||
|
use embedded_time::fixed_point::FixedPoint;
|
||||||
|
use rp2040_pac::{RESETS, RTC};
|
||||||
|
|
||||||
|
mod filter;
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
|
/// A reference to the real time clock of the system
|
||||||
|
pub struct RealTimeClock {
|
||||||
|
rtc: RTC,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RealTimeClock {
|
||||||
|
/// Create a new instance of the real time clock, with the given date as an initial value.
|
||||||
|
///
|
||||||
|
/// Note that the [`ClocksManager`] should be enabled first. See the [`clocks`] module for more information.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||||
|
///
|
||||||
|
/// [`ClocksManager`]: ../clocks/struct.ClocksManager.html
|
||||||
|
/// [`clocks`]: ../clocks/index.html
|
||||||
|
pub fn new(
|
||||||
|
rtc: RTC,
|
||||||
|
clock: RtcClock,
|
||||||
|
resets: &mut RESETS,
|
||||||
|
initial_date: DateTime,
|
||||||
|
) -> Result<Self, RtcError> {
|
||||||
|
// Toggle the RTC reset
|
||||||
|
resets.reset.modify(|_, w| w.rtc().set_bit());
|
||||||
|
resets.reset.modify(|_, w| w.rtc().clear_bit());
|
||||||
|
while resets.reset_done.read().rtc().bit_is_clear() {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the RTC divider
|
||||||
|
let freq = clock.freq().integer() - 1;
|
||||||
|
rtc.clkdiv_m1.write(|w| unsafe { w.bits(freq) });
|
||||||
|
|
||||||
|
let mut result = Self { rtc };
|
||||||
|
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
|
||||||
|
result.set_datetime(initial_date)?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) {
|
||||||
|
self.rtc
|
||||||
|
.ctrl
|
||||||
|
.modify(|_, w| w.force_notleapyear().bit(!leap_year_check_enabled));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks to see if this RealTimeClock is running
|
||||||
|
pub fn is_running(&self) -> bool {
|
||||||
|
self.rtc.ctrl.read().rtc_active().bit_is_set()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
self.rtc.ctrl.modify(|_, w| w.rtc_enable().clear_bit());
|
||||||
|
while self.rtc.ctrl.read().rtc_active().bit_is_set() {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.rtc.setup_0.write(|w| {
|
||||||
|
self::datetime::write_setup_0(&t, w);
|
||||||
|
w
|
||||||
|
});
|
||||||
|
self.rtc.setup_1.write(|w| {
|
||||||
|
self::datetime::write_setup_1(&t, w);
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load the new datetime and re-enable RTC
|
||||||
|
self.rtc.ctrl.write(|w| w.load().set_bit());
|
||||||
|
self.rtc.ctrl.write(|w| w.rtc_enable().set_bit());
|
||||||
|
while self.rtc.ctrl.read().rtc_active().bit_is_clear() {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rtc_0 = self.rtc.rtc_0.read();
|
||||||
|
let rtc_1 = self.rtc.rtc_1.read();
|
||||||
|
|
||||||
|
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) {
|
||||||
|
self.rtc
|
||||||
|
.irq_setup_0
|
||||||
|
.modify(|_, s| s.match_ena().clear_bit());
|
||||||
|
|
||||||
|
while self.rtc.irq_setup_0.read().match_active().bit() {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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() {
|
||||||
|
/// # use rp2040_hal::rtc::{RealTimeClock, DateTimeFilter};
|
||||||
|
/// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() };
|
||||||
|
/// 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();
|
||||||
|
|
||||||
|
self.rtc.irq_setup_0.write(|w| {
|
||||||
|
filter.write_setup_0(w);
|
||||||
|
w
|
||||||
|
});
|
||||||
|
self.rtc.irq_setup_1.write(|w| {
|
||||||
|
filter.write_setup_1(w);
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the enable bit and check if it is set
|
||||||
|
self.rtc.irq_setup_0.modify(|_, w| w.match_ena().set_bit());
|
||||||
|
while self.rtc.irq_setup_0.read().match_active().bit_is_clear() {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors that can occur on methods on [RtcClock]
|
||||||
|
#[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,
|
||||||
|
}
|
Loading…
Reference in a new issue