aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/rtc/mod.rs
diff options
context:
space:
mode:
authorMathias <[email protected]>2022-09-16 06:45:27 +0200
committerMathias <[email protected]>2022-09-16 13:18:23 +0200
commitfeead3ae89d57e9f0ff7d7a264136d3e89aaebcf (patch)
tree5553b485edc241ee2a72c76a08ee955e9cf0bd57 /embassy-rp/src/rtc/mod.rs
parent9794bc59cc74598f5131f502153d4288c3261274 (diff)
Implement RealTimeClock for embassy-rp
Diffstat (limited to 'embassy-rp/src/rtc/mod.rs')
-rw-r--r--embassy-rp/src/rtc/mod.rs188
1 files changed, 188 insertions, 0 deletions
diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs
new file mode 100644
index 000000000..7f3bbbe73
--- /dev/null
+++ b/embassy-rp/src/rtc/mod.rs
@@ -0,0 +1,188 @@
1mod filter;
2
3use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
4
5pub use self::filter::DateTimeFilter;
6
7#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
8#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
9mod datetime;
10
11pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
12use crate::clocks::clk_rtc_freq;
13
14/// A reference to the real time clock of the system
15pub struct RealTimeClock<'d, T: Instance> {
16 inner: PeripheralRef<'d, T>,
17}
18
19impl<'d, T: Instance> RealTimeClock<'d, T> {
20 /// Create a new instance of the real time clock, with the given date as an initial value.
21 ///
22 /// # Errors
23 ///
24 /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
25 pub fn new(inner: impl Peripheral<P = T> + 'd, initial_date: DateTime) -> Result<Self, RtcError> {
26 into_ref!(inner);
27
28 // Set the RTC divider
29 unsafe {
30 inner
31 .regs()
32 .clkdiv_m1()
33 .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1))
34 };
35
36 let mut result = Self { inner };
37 result.set_leap_year_check(true); // should be on by default, make sure this is the case.
38 result.set_datetime(initial_date)?;
39 Ok(result)
40 }
41
42 /// 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.
43 ///
44 /// Leap year checking is enabled by default.
45 pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {
46 unsafe {
47 self.inner
48 .regs()
49 .ctrl()
50 .modify(|w| w.set_force_notleapyear(!leap_year_check_enabled))
51 };
52 }
53
54 /// Checks to see if this RealTimeClock is running
55 pub fn is_running(&self) -> bool {
56 unsafe { self.inner.regs().ctrl().read().rtc_active() }
57 }
58
59 /// Set the datetime to a new value.
60 ///
61 /// # Errors
62 ///
63 /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
64 pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
65 self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
66
67 // disable RTC while we configure it
68 unsafe {
69 self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));
70 while self.inner.regs().ctrl().read().rtc_active() {
71 core::hint::spin_loop();
72 }
73
74 self.inner.regs().setup_0().write(|w| {
75 self::datetime::write_setup_0(&t, w);
76 });
77 self.inner.regs().setup_1().write(|w| {
78 self::datetime::write_setup_1(&t, w);
79 });
80
81 // Load the new datetime and re-enable RTC
82 self.inner.regs().ctrl().write(|w| w.set_load(true));
83 self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));
84 while !self.inner.regs().ctrl().read().rtc_active() {
85 core::hint::spin_loop();
86 }
87 }
88 Ok(())
89 }
90
91 /// Return the current datetime.
92 ///
93 /// # Errors
94 ///
95 /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
96 pub fn now(&self) -> Result<DateTime, RtcError> {
97 if !self.is_running() {
98 return Err(RtcError::NotRunning);
99 }
100
101 let rtc_0 = unsafe { self.inner.regs().rtc_0().read() };
102 let rtc_1 = unsafe { self.inner.regs().rtc_1().read() };
103
104 self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime)
105 }
106
107 /// Disable the alarm that was scheduled with [`schedule_alarm`].
108 ///
109 /// [`schedule_alarm`]: #method.schedule_alarm
110 pub fn disable_alarm(&mut self) {
111 unsafe {
112 self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false));
113
114 while self.inner.regs().irq_setup_0().read().match_active() {
115 core::hint::spin_loop();
116 }
117 }
118 }
119
120 /// Schedule an alarm. The `filter` determines at which point in time this alarm is set.
121 ///
122 /// 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:
123 /// ```no_run
124 /// # #[cfg(feature = "chrono")]
125 /// # fn main() { }
126 /// # #[cfg(not(feature = "chrono"))]
127 /// # fn main() {
128 /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
129 /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() };
130 /// let now = real_time_clock.now().unwrap();
131 /// real_time_clock.schedule_alarm(
132 /// DateTimeFilter::default()
133 /// .minute(if now.minute == 59 { 0 } else { now.minute + 1 })
134 /// );
135 /// # }
136 /// ```
137 pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {
138 self.disable_alarm();
139
140 unsafe {
141 self.inner.regs().irq_setup_0().write(|w| {
142 filter.write_setup_0(w);
143 });
144 self.inner.regs().irq_setup_1().write(|w| {
145 filter.write_setup_1(w);
146 });
147
148 // Set the enable bit and check if it is set
149 self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));
150 while !self.inner.regs().irq_setup_0().read().match_active() {
151 core::hint::spin_loop();
152 }
153 }
154 }
155
156 /// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered,
157 /// or the next [`schedule_alarm`] will never fire.
158 ///
159 /// [`schedule_alarm`]: #method.schedule_alarm
160 pub fn clear_interrupt(&mut self) {
161 self.disable_alarm();
162 }
163}
164
165/// Errors that can occur on methods on [RtcClock]
166#[derive(Clone, Debug, PartialEq, Eq)]
167pub enum RtcError {
168 /// An invalid DateTime was given or stored on the hardware.
169 InvalidDateTime(DateTimeError),
170
171 /// The RTC clock is not running
172 NotRunning,
173}
174
175mod sealed {
176 pub trait Instance {
177 fn regs(&self) -> crate::pac::rtc::Rtc;
178 }
179}
180
181pub trait Instance: sealed::Instance {}
182
183impl sealed::Instance for crate::peripherals::RTC {
184 fn regs(&self) -> crate::pac::rtc::Rtc {
185 crate::pac::RTC
186 }
187}
188impl Instance for crate::peripherals::RTC {}