diff options
| author | 1-rafael-1 <[email protected]> | 2025-05-16 23:22:34 +0200 |
|---|---|---|
| committer | 1-rafael-1 <[email protected]> | 2025-05-16 23:22:34 +0200 |
| commit | 1a12942f530df6b3dbd316ca29daf0b9d83ec36d (patch) | |
| tree | 62cf7a05a4ff6497aab9e176415ca469a6c8046f /embassy-rp/src | |
| parent | 575eab3c60c0d2098f9e9d2a22429aa174b6b968 (diff) | |
embassy-rp (rp2040): Rtc wait_for_alarm
Diffstat (limited to 'embassy-rp/src')
| -rw-r--r-- | embassy-rp/src/rtc/datetime_no_deps.rs | 1 | ||||
| -rw-r--r-- | embassy-rp/src/rtc/filter.rs | 3 | ||||
| -rw-r--r-- | embassy-rp/src/rtc/mod.rs | 122 |
3 files changed, 124 insertions, 2 deletions
diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs index 5de00e6b4..77d4a3055 100644 --- a/embassy-rp/src/rtc/datetime_no_deps.rs +++ b/embassy-rp/src/rtc/datetime_no_deps.rs | |||
| @@ -46,6 +46,7 @@ pub struct DateTime { | |||
| 46 | /// A day of the week | 46 | /// A day of the week |
| 47 | #[repr(u8)] | 47 | #[repr(u8)] |
| 48 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] | 48 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] |
| 49 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 49 | #[allow(missing_docs)] | 50 | #[allow(missing_docs)] |
| 50 | pub enum DayOfWeek { | 51 | pub enum DayOfWeek { |
| 51 | Sunday = 0, | 52 | Sunday = 0, |
diff --git a/embassy-rp/src/rtc/filter.rs b/embassy-rp/src/rtc/filter.rs index d4a3bab2f..433053613 100644 --- a/embassy-rp/src/rtc/filter.rs +++ b/embassy-rp/src/rtc/filter.rs | |||
| @@ -4,7 +4,8 @@ use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1}; | |||
| 4 | /// A filter used for [`RealTimeClock::schedule_alarm`]. | 4 | /// A filter used for [`RealTimeClock::schedule_alarm`]. |
| 5 | /// | 5 | /// |
| 6 | /// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm | 6 | /// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm |
| 7 | #[derive(Default)] | 7 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] |
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct DateTimeFilter { | 9 | pub struct DateTimeFilter { |
| 9 | /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. | 10 | /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. |
| 10 | pub year: Option<u16>, | 11 | pub year: Option<u16>, |
diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 63cf91d28..7289e46af 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs | |||
| @@ -1,7 +1,12 @@ | |||
| 1 | //! RTC driver. | 1 | //! RTC driver. |
| 2 | mod filter; | 2 | mod filter; |
| 3 | 3 | ||
| 4 | use core::future::poll_fn; | ||
| 5 | use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | 8 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 5 | 10 | ||
| 6 | pub use self::filter::DateTimeFilter; | 11 | pub use self::filter::DateTimeFilter; |
| 7 | 12 | ||
| @@ -11,6 +16,13 @@ mod datetime; | |||
| 11 | 16 | ||
| 12 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; | 17 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; |
| 13 | use crate::clocks::clk_rtc_freq; | 18 | use crate::clocks::clk_rtc_freq; |
| 19 | use crate::interrupt::typelevel::Binding; | ||
| 20 | use crate::interrupt::{self, InterruptExt}; | ||
| 21 | |||
| 22 | // Static waker for the interrupt handler | ||
| 23 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 24 | // Static flag to indicate if an alarm has occurred | ||
| 25 | static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false); | ||
| 14 | 26 | ||
| 15 | /// A reference to the real time clock of the system | 27 | /// A reference to the real time clock of the system |
| 16 | pub struct Rtc<'d, T: Instance> { | 28 | pub struct Rtc<'d, T: Instance> { |
| @@ -23,10 +35,15 @@ impl<'d, T: Instance> Rtc<'d, T> { | |||
| 23 | /// # Errors | 35 | /// # Errors |
| 24 | /// | 36 | /// |
| 25 | /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. | 37 | /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. |
| 26 | pub fn new(inner: Peri<'d, T>) -> Self { | 38 | pub fn new(inner: Peri<'d, T>, _irq: impl Binding<interrupt::typelevel::RTC_IRQ, InterruptHandler>) -> Self { |
| 27 | // Set the RTC divider | 39 | // Set the RTC divider |
| 28 | inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); | 40 | inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); |
| 29 | 41 | ||
| 42 | // Setup the IRQ | ||
| 43 | // Clear any pending interrupts from the RTC_IRQ interrupt and enable it, so we do not have unexpected interrupts after initialization | ||
| 44 | interrupt::RTC_IRQ.unpend(); | ||
| 45 | unsafe { interrupt::RTC_IRQ.enable() }; | ||
| 46 | |||
| 30 | Self { inner } | 47 | Self { inner } |
| 31 | } | 48 | } |
| 32 | 49 | ||
| @@ -174,6 +191,109 @@ impl<'d, T: Instance> Rtc<'d, T> { | |||
| 174 | pub fn clear_interrupt(&mut self) { | 191 | pub fn clear_interrupt(&mut self) { |
| 175 | self.disable_alarm(); | 192 | self.disable_alarm(); |
| 176 | } | 193 | } |
| 194 | |||
| 195 | /// Check if an alarm is scheduled. | ||
| 196 | /// | ||
| 197 | /// This function checks if the RTC alarm is currently active. If it is, it returns the alarm configuration | ||
| 198 | /// as a [`DateTimeFilter`]. Otherwise, it returns `None`. | ||
| 199 | pub fn alarm_scheduled(&self) -> Option<DateTimeFilter> { | ||
| 200 | // Check if alarm is active | ||
| 201 | if !self.inner.regs().irq_setup_0().read().match_active() { | ||
| 202 | return None; | ||
| 203 | } | ||
| 204 | |||
| 205 | // Get values from both alarm registers | ||
| 206 | let irq_0 = self.inner.regs().irq_setup_0().read(); | ||
| 207 | let irq_1 = self.inner.regs().irq_setup_1().read(); | ||
| 208 | |||
| 209 | // Create a DateTimeFilter and populate it based on which fields are enabled | ||
| 210 | let mut filter = DateTimeFilter::default(); | ||
| 211 | |||
| 212 | if irq_0.year_ena() { | ||
| 213 | filter.year = Some(irq_0.year()); | ||
| 214 | } | ||
| 215 | |||
| 216 | if irq_0.month_ena() { | ||
| 217 | filter.month = Some(irq_0.month()); | ||
| 218 | } | ||
| 219 | |||
| 220 | if irq_0.day_ena() { | ||
| 221 | filter.day = Some(irq_0.day()); | ||
| 222 | } | ||
| 223 | |||
| 224 | if irq_1.dotw_ena() { | ||
| 225 | // Convert day of week value to DayOfWeek enum | ||
| 226 | let day_of_week = match irq_1.dotw() { | ||
| 227 | 0 => DayOfWeek::Sunday, | ||
| 228 | 1 => DayOfWeek::Monday, | ||
| 229 | 2 => DayOfWeek::Tuesday, | ||
| 230 | 3 => DayOfWeek::Wednesday, | ||
| 231 | 4 => DayOfWeek::Thursday, | ||
| 232 | 5 => DayOfWeek::Friday, | ||
| 233 | 6 => DayOfWeek::Saturday, | ||
| 234 | _ => return None, // Invalid day of week | ||
| 235 | }; | ||
| 236 | filter.day_of_week = Some(day_of_week); | ||
| 237 | } | ||
| 238 | |||
| 239 | if irq_1.hour_ena() { | ||
| 240 | filter.hour = Some(irq_1.hour()); | ||
| 241 | } | ||
| 242 | |||
| 243 | if irq_1.min_ena() { | ||
| 244 | filter.minute = Some(irq_1.min()); | ||
| 245 | } | ||
| 246 | |||
| 247 | if irq_1.sec_ena() { | ||
| 248 | filter.second = Some(irq_1.sec()); | ||
| 249 | } | ||
| 250 | |||
| 251 | Some(filter) | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Wait for an RTC alarm to trigger. | ||
| 255 | /// | ||
| 256 | /// This function will wait until the RTC alarm is triggered. If the alarm is already triggered, it will return immediately. | ||
| 257 | /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered. | ||
| 258 | pub async fn wait_for_alarm(&mut self) { | ||
| 259 | poll_fn(|cx| { | ||
| 260 | WAKER.register(cx.waker()); | ||
| 261 | |||
| 262 | // If the alarm has occured, we will clear the interrupt and return ready | ||
| 263 | if ALARM_OCCURRED.load(Ordering::SeqCst) { | ||
| 264 | // Clear the alarm occurred flag | ||
| 265 | ALARM_OCCURRED.store(false, Ordering::SeqCst); | ||
| 266 | |||
| 267 | // Clear the interrupt and disable the alarm | ||
| 268 | self.clear_interrupt(); | ||
| 269 | |||
| 270 | // Return ready | ||
| 271 | compiler_fence(Ordering::SeqCst); | ||
| 272 | return Poll::Ready(()); | ||
| 273 | } else { | ||
| 274 | // If the alarm has not occurred, we will return pending | ||
| 275 | return Poll::Pending; | ||
| 276 | } | ||
| 277 | }) | ||
| 278 | .await; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Interrupt handler. | ||
| 283 | pub struct InterruptHandler { | ||
| 284 | _empty: (), | ||
| 285 | } | ||
| 286 | |||
| 287 | impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC_IRQ> for InterruptHandler { | ||
| 288 | unsafe fn on_interrupt() { | ||
| 289 | // Disable the alarm first thing, to prevent unexpected re-entry | ||
| 290 | let rtc = crate::pac::RTC; | ||
| 291 | rtc.irq_setup_0().modify(|w| w.set_match_ena(false)); | ||
| 292 | |||
| 293 | // Set the alarm occurred flag and wake the waker | ||
| 294 | ALARM_OCCURRED.store(true, Ordering::SeqCst); | ||
| 295 | WAKER.wake(); | ||
| 296 | } | ||
| 177 | } | 297 | } |
| 178 | 298 | ||
| 179 | /// Errors that can occur on methods on [Rtc] | 299 | /// Errors that can occur on methods on [Rtc] |
