-
Notifications
You must be signed in to change notification settings - Fork 247
/
Copy pathmod.rs
209 lines (180 loc) · 7.47 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! 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 crate::pac::{RESETS, RTC};
mod filter;
pub use self::filter::DateTimeFilter;
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
#[allow(unknown_lints)]
#[allow(clippy::needless_pass_by_ref_mut)]
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().to_Hz() - 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)
}
fn set_match_ena(&mut self, ena: bool) {
// Set the enable bit and check if it is set
self.rtc.irq_setup_0.modify(|_, w| w.match_ena().bit(ena));
while self.rtc.irq_setup_0.read().match_active().bit() != ena {
core::hint::spin_loop();
}
}
/// Disable the alarm that was scheduled with [`schedule_alarm`].
///
/// [`schedule_alarm`]: #method.schedule_alarm
pub fn disable_alarm(&mut self) {
self.set_match_ena(false)
}
/// Schedule an alarm. The `filter` determines at which point in time this alarm is set.
///
/// If not all fields are set, the alarm will repeat each time the RTC reaches these values.
/// For example, to fire every minute, set:
/// ```no_run
/// # use rp2040_hal::rtc::{RealTimeClock, DateTimeFilter};
/// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() };
/// real_time_clock.schedule_alarm(DateTimeFilter::default().second(0));
/// ```
///
/// It is worth nothing that the alarm will not fire on schedule if the current time matches.
pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {
self.set_match_ena(false);
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
});
self.set_match_ena(true);
}
/// Enable the propagation of alarm to the NVIC.
pub fn enable_interrupt(&mut self) {
self.rtc.inte.modify(|_, w| w.rtc().set_bit());
}
/// Disable the propagation of the alarm to the NVIC.
pub fn disable_interrupt(&mut self) {
self.rtc.inte.modify(|_, w| w.rtc().clear_bit());
}
/// Clear the interrupt.
///
/// This should be called every time the `RTC_IRQ` interrupt is triggered or the interrupt will
/// continually fire..
pub fn clear_interrupt(&mut self) {
self.set_match_ena(false);
self.set_match_ena(true);
}
}
/// 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,
}