From 1a12942f530df6b3dbd316ca29daf0b9d83ec36d Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Fri, 16 May 2025 23:22:34 +0200 Subject: [PATCH 1/7] embassy-rp (rp2040): Rtc wait_for_alarm --- embassy-rp/src/rtc/datetime_no_deps.rs | 1 + embassy-rp/src/rtc/filter.rs | 3 +- embassy-rp/src/rtc/mod.rs | 122 ++++++++++++++++++++++++- examples/rp/src/bin/rtc.rs | 8 +- examples/rp/src/bin/rtc_alarm.rs | 69 ++++++++++++++ 5 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 examples/rp/src/bin/rtc_alarm.rs 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 { /// A day of the week #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[allow(missing_docs)] pub enum DayOfWeek { 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}; /// A filter used for [`RealTimeClock::schedule_alarm`]. /// /// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm -#[derive(Default)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] 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, 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 @@ //! RTC driver. mod filter; +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use core::task::Poll; + use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; pub use self::filter::DateTimeFilter; @@ -11,6 +16,13 @@ mod datetime; pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; use crate::clocks::clk_rtc_freq; +use crate::interrupt::typelevel::Binding; +use crate::interrupt::{self, InterruptExt}; + +// Static waker for the interrupt handler +static WAKER: AtomicWaker = AtomicWaker::new(); +// Static flag to indicate if an alarm has occurred +static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false); /// A reference to the real time clock of the system pub struct Rtc<'d, T: Instance> { @@ -23,10 +35,15 @@ impl<'d, T: Instance> Rtc<'d, T> { /// # Errors /// /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. - pub fn new(inner: Peri<'d, T>) -> Self { + pub fn new(inner: Peri<'d, T>, _irq: impl Binding) -> Self { // Set the RTC divider inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); + // Setup the IRQ + // Clear any pending interrupts from the RTC_IRQ interrupt and enable it, so we do not have unexpected interrupts after initialization + interrupt::RTC_IRQ.unpend(); + unsafe { interrupt::RTC_IRQ.enable() }; + Self { inner } } @@ -174,6 +191,109 @@ impl<'d, T: Instance> Rtc<'d, T> { pub fn clear_interrupt(&mut self) { self.disable_alarm(); } + + /// Check if an alarm is scheduled. + /// + /// This function checks if the RTC alarm is currently active. If it is, it returns the alarm configuration + /// as a [`DateTimeFilter`]. Otherwise, it returns `None`. + pub fn alarm_scheduled(&self) -> Option { + // Check if alarm is active + if !self.inner.regs().irq_setup_0().read().match_active() { + return None; + } + + // Get values from both alarm registers + let irq_0 = self.inner.regs().irq_setup_0().read(); + let irq_1 = self.inner.regs().irq_setup_1().read(); + + // Create a DateTimeFilter and populate it based on which fields are enabled + let mut filter = DateTimeFilter::default(); + + if irq_0.year_ena() { + filter.year = Some(irq_0.year()); + } + + if irq_0.month_ena() { + filter.month = Some(irq_0.month()); + } + + if irq_0.day_ena() { + filter.day = Some(irq_0.day()); + } + + if irq_1.dotw_ena() { + // Convert day of week value to DayOfWeek enum + let day_of_week = match irq_1.dotw() { + 0 => DayOfWeek::Sunday, + 1 => DayOfWeek::Monday, + 2 => DayOfWeek::Tuesday, + 3 => DayOfWeek::Wednesday, + 4 => DayOfWeek::Thursday, + 5 => DayOfWeek::Friday, + 6 => DayOfWeek::Saturday, + _ => return None, // Invalid day of week + }; + filter.day_of_week = Some(day_of_week); + } + + if irq_1.hour_ena() { + filter.hour = Some(irq_1.hour()); + } + + if irq_1.min_ena() { + filter.minute = Some(irq_1.min()); + } + + if irq_1.sec_ena() { + filter.second = Some(irq_1.sec()); + } + + Some(filter) + } + + /// Wait for an RTC alarm to trigger. + /// + /// This function will wait until the RTC alarm is triggered. If the alarm is already triggered, it will return immediately. + /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered. + pub async fn wait_for_alarm(&mut self) { + poll_fn(|cx| { + WAKER.register(cx.waker()); + + // If the alarm has occured, we will clear the interrupt and return ready + if ALARM_OCCURRED.load(Ordering::SeqCst) { + // Clear the alarm occurred flag + ALARM_OCCURRED.store(false, Ordering::SeqCst); + + // Clear the interrupt and disable the alarm + self.clear_interrupt(); + + // Return ready + compiler_fence(Ordering::SeqCst); + return Poll::Ready(()); + } else { + // If the alarm has not occurred, we will return pending + return Poll::Pending; + } + }) + .await; + } +} + +/// Interrupt handler. +pub struct InterruptHandler { + _empty: (), +} + +impl crate::interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + // Disable the alarm first thing, to prevent unexpected re-entry + let rtc = crate::pac::RTC; + rtc.irq_setup_0().modify(|w| w.set_match_ena(false)); + + // Set the alarm occurred flag and wake the waker + ALARM_OCCURRED.store(true, Ordering::SeqCst); + WAKER.wake(); + } } /// Errors that can occur on methods on [Rtc] diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs index e9a5e43a8..1692bdf36 100644 --- a/examples/rp/src/bin/rtc.rs +++ b/examples/rp/src/bin/rtc.rs @@ -5,16 +5,22 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; +// Bind the RTC interrupt to the handler +bind_interrupts!(struct Irqs { + RTC_IRQ => embassy_rp::rtc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Wait for 20s"); - let mut rtc = Rtc::new(p.RTC); + let mut rtc = Rtc::new(p.RTC, Irqs); if !rtc.is_running() { info!("Start RTC"); diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs new file mode 100644 index 000000000..83421014f --- /dev/null +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -0,0 +1,69 @@ +//! This example shows how to use RTC (Real Time Clock) for scheduling alarms and reacting to them. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::select::{select, Either}; +use embassy_rp::bind_interrupts; +use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// Bind the RTC interrupt to the handler +bind_interrupts!(struct Irqs { + RTC_IRQ => embassy_rp::rtc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rtc = Rtc::new(p.RTC, Irqs); + + if !rtc.is_running() { + info!("Start RTC"); + let now = DateTime { + year: 2000, + month: 1, + day: 1, + day_of_week: DayOfWeek::Saturday, + hour: 0, + minute: 0, + second: 0, + }; + rtc.set_datetime(now).unwrap(); + } + + loop { + // Wait for 5 seconds or until the alarm is triggered + match select(Timer::after_secs(5), rtc.wait_for_alarm()).await { + // Timer expired + Either::First(_) => { + let dt = rtc.now().unwrap(); + info!( + "Now: {}-{:02}-{:02} {}:{:02}:{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + ); + + // See if the alarm is already scheduled, if not, schedule it + match rtc.alarm_scheduled() { + None => { + info!("Scheduling alarm for 30 seconds from now"); + let next_30s = if dt.second == 59 { 0 } else { dt.second + 30 }; + let dtf = DateTimeFilter::default().second(next_30s); + + rtc.schedule_alarm(dtf); + + info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); + } + Some(_) => {} + } + } + // Alarm triggered + Either::Second(_) => { + info!("ALARM TRIGGERED!"); + } + } + } +} From eb685574601d98c44faed9a3534d056199b46e20 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Tue, 20 May 2025 15:55:31 +0200 Subject: [PATCH 2/7] simplify alarm scheduling logic in RTC example --- examples/rp/src/bin/rtc_alarm.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs index 83421014f..dccf911e3 100644 --- a/examples/rp/src/bin/rtc_alarm.rs +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -50,10 +50,7 @@ async fn main(_spawner: Spawner) { match rtc.alarm_scheduled() { None => { info!("Scheduling alarm for 30 seconds from now"); - let next_30s = if dt.second == 59 { 0 } else { dt.second + 30 }; - let dtf = DateTimeFilter::default().second(next_30s); - - rtc.schedule_alarm(dtf); + rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); } From 37fd802f961486b176c0cdda9087ecf987d51eb8 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 15 Sep 2025 20:15:57 +0200 Subject: [PATCH 3/7] Update CHANGELOG.md --- embassy-rp/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 841c9f068..d1265ffc4 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add PIO SPI - Add PIO I2S input - Add PIO onewire parasite power strong pullup +- add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216)) ## 0.8.0 - 2025-08-26 @@ -55,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.4.0 - 2025-03-09 -- Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857)) +- Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857)) The functions added in this change are `get_addr` `get_tx_threshold`, `set_tx_threshold`, `get_rx_threshold`, `set_rx_threshold`, `set_thresholds`. - Expose the watchdog reset reason. ([#3877](https://github.com/embassy-rs/embassy/pull/3877)) - Update pio-rs, reexport, move instr methods to SM. ([#3865](https://github.com/embassy-rs/embassy/pull/3865)) @@ -96,7 +97,7 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended. - Add Clone and Copy to Error types - fix spinlocks staying locked after reset. - wait until read matches for PSM accesses. -- Remove generics +- Remove generics - fix drop implementation of BufferedUartRx and BufferedUartTx - implement `embedded_storage_async::nor_flash::MultiwriteNorFlash` - rp usb: wake ep-wakers after stalling From ac32f43c3dc915b78e71328855189b8aacfec8c3 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 15 Sep 2025 21:10:22 +0200 Subject: [PATCH 4/7] alarm handling to poll hardware status directly; add ci test --- embassy-rp/src/rtc/mod.rs | 19 ++---- examples/rp/src/bin/rtc_alarm.rs | 12 ++-- tests/rp/Cargo.toml | 6 ++ tests/rp/src/bin/rtc.rs | 113 +++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 tests/rp/src/bin/rtc.rs diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 7289e46af..8e6aa66c9 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -2,7 +2,7 @@ mod filter; use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_internal::{Peri, PeripheralType}; @@ -21,8 +21,6 @@ use crate::interrupt::{self, InterruptExt}; // Static waker for the interrupt handler static WAKER: AtomicWaker = AtomicWaker::new(); -// Static flag to indicate if an alarm has occurred -static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false); /// A reference to the real time clock of the system pub struct Rtc<'d, T: Instance> { @@ -259,19 +257,15 @@ impl<'d, T: Instance> Rtc<'d, T> { poll_fn(|cx| { WAKER.register(cx.waker()); - // If the alarm has occured, we will clear the interrupt and return ready - if ALARM_OCCURRED.load(Ordering::SeqCst) { - // Clear the alarm occurred flag - ALARM_OCCURRED.store(false, Ordering::SeqCst); - - // Clear the interrupt and disable the alarm + // Check hardware interrupt status directly + if self.inner.regs().ints().read().rtc() { + // Clear the interrupt status and disable the alarm + self.inner.regs().ints().write(|w| w.set_rtc(true)); self.clear_interrupt(); - // Return ready compiler_fence(Ordering::SeqCst); return Poll::Ready(()); } else { - // If the alarm has not occurred, we will return pending return Poll::Pending; } }) @@ -290,8 +284,7 @@ impl crate::interrupt::typelevel::Handler let rtc = crate::pac::RTC; rtc.irq_setup_0().modify(|w| w.set_match_ena(false)); - // Set the alarm occurred flag and wake the waker - ALARM_OCCURRED.store(true, Ordering::SeqCst); + // Wake the waker - interrupt status will be checked directly by polling future WAKER.wake(); } } diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs index dccf911e3..e935dbf34 100644 --- a/examples/rp/src/bin/rtc_alarm.rs +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -47,14 +47,10 @@ async fn main(_spawner: Spawner) { ); // See if the alarm is already scheduled, if not, schedule it - match rtc.alarm_scheduled() { - None => { - info!("Scheduling alarm for 30 seconds from now"); - rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); - - info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); - } - Some(_) => {} + if rtc.alarm_scheduled().is_none() { + info!("Scheduling alarm for 30 seconds from now"); + rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); + info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); } } // Alarm triggered diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 809346bed..19461520a 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -64,6 +64,12 @@ name = "float" path = "src/bin/float.rs" required-features = [ "rp2040",] +# RTC is only available on RP2040 +[[bin]] +name = "rtc" +path = "src/bin/rtc.rs" +required-features = [ "rp2040",] + [profile.dev] debug = 2 debug-assertions = true diff --git a/tests/rp/src/bin/rtc.rs b/tests/rp/src/bin/rtc.rs new file mode 100644 index 000000000..8d1d4ee14 --- /dev/null +++ b/tests/rp/src/bin/rtc.rs @@ -0,0 +1,113 @@ +#![no_std] +#![no_main] +#[cfg(feature = "rp2040")] +teleprobe_meta::target!(b"rpi-pico"); + +use defmt::{assert, *}; +use embassy_executor::Spawner; +use embassy_futures::select::{select, Either}; +use embassy_rp::bind_interrupts; +use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +// Bind the RTC interrupt to the handler +bind_interrupts!(struct Irqs { + RTC_IRQ => embassy_rp::rtc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rtc = Rtc::new(p.RTC, Irqs); + + info!("RTC test started"); + + // Initialize RTC if not running + if !rtc.is_running() { + info!("Starting RTC"); + let now = DateTime { + year: 2000, + month: 1, + day: 1, + day_of_week: DayOfWeek::Saturday, + hour: 0, + minute: 0, + second: 0, + }; + rtc.set_datetime(now).unwrap(); + } + + // Test 1: Basic RTC functionality - read current time + let initial_time = rtc.now().unwrap(); + info!( + "Initial time: {}-{:02}-{:02} {}:{:02}:{:02}", + initial_time.year, + initial_time.month, + initial_time.day, + initial_time.hour, + initial_time.minute, + initial_time.second + ); + + // Test 2: Schedule and wait for alarm + info!("Testing alarm scheduling"); + + // Schedule alarm for 3 seconds from now + let alarm_second = (initial_time.second + 3) % 60; + let alarm_filter = DateTimeFilter::default().second(alarm_second); + + info!("Scheduling alarm for second: {}", alarm_second); + rtc.schedule_alarm(alarm_filter); + + // Verify alarm is scheduled + let scheduled = rtc.alarm_scheduled(); + assert!(scheduled.is_some(), "Alarm should be scheduled"); + info!("Alarm scheduled successfully: {}", scheduled.unwrap()); + + // Wait for alarm with timeout + let alarm_start = Instant::now(); + match select(Timer::after_secs(5), rtc.wait_for_alarm()).await { + Either::First(_) => { + core::panic!("Alarm timeout - alarm should have triggered within 5 seconds"); + } + Either::Second(_) => { + let alarm_duration = Instant::now() - alarm_start; + info!("ALARM TRIGGERED after {:?}", alarm_duration); + + // Verify timing is reasonable (should be around 3 seconds, allow some margin) + assert!( + alarm_duration >= Duration::from_secs(2) && alarm_duration <= Duration::from_secs(4), + "Alarm timing incorrect: {:?}", + alarm_duration + ); + } + } + + // Test 3: Verify RTC is still running and time has advanced + let final_time = rtc.now().unwrap(); + info!( + "Final time: {}-{:02}-{:02} {}:{:02}:{:02}", + final_time.year, final_time.month, final_time.day, final_time.hour, final_time.minute, final_time.second + ); + + // Verify time has advanced (allowing for minute/hour rollover) + let time_diff = if final_time.second >= initial_time.second { + final_time.second - initial_time.second + } else { + 60 - initial_time.second + final_time.second + }; + + assert!(time_diff >= 3, "RTC should have advanced by at least 3 seconds"); + info!("Time advanced by {} seconds", time_diff); + + // Test 4: Verify alarm is no longer scheduled after triggering + let post_alarm_scheduled = rtc.alarm_scheduled(); + assert!( + post_alarm_scheduled.is_none(), + "Alarm should not be scheduled after triggering" + ); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From dd10e372d2e656dc71fa460564d405ba6a5b5d4b Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 15 Sep 2025 21:57:41 +0200 Subject: [PATCH 5/7] Fix race condition in RTC alarm by using AtomicBool flag --- embassy-rp/src/rtc/mod.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 8e6aa66c9..8b0deed21 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -2,7 +2,7 @@ mod filter; use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use core::task::Poll; use embassy_hal_internal::{Peri, PeripheralType}; @@ -21,6 +21,8 @@ use crate::interrupt::{self, InterruptExt}; // Static waker for the interrupt handler static WAKER: AtomicWaker = AtomicWaker::new(); +// Static flag to indicate if an alarm has occurred +static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false); /// A reference to the real time clock of the system pub struct Rtc<'d, T: Instance> { @@ -257,10 +259,15 @@ impl<'d, T: Instance> Rtc<'d, T> { poll_fn(|cx| { WAKER.register(cx.waker()); - // Check hardware interrupt status directly - if self.inner.regs().ints().read().rtc() { - // Clear the interrupt status and disable the alarm - self.inner.regs().ints().write(|w| w.set_rtc(true)); + // Atomically check and clear the alarm occurred flag to prevent race conditions + if critical_section::with(|_| { + let occurred = ALARM_OCCURRED.load(Ordering::SeqCst); + if occurred { + ALARM_OCCURRED.store(false, Ordering::SeqCst); + } + occurred + }) { + // Clear the interrupt and disable the alarm self.clear_interrupt(); compiler_fence(Ordering::SeqCst); @@ -284,7 +291,8 @@ impl crate::interrupt::typelevel::Handler let rtc = crate::pac::RTC; rtc.irq_setup_0().modify(|w| w.set_match_ena(false)); - // Wake the waker - interrupt status will be checked directly by polling future + // Set the alarm occurred flag and wake the waker + ALARM_OCCURRED.store(true, Ordering::SeqCst); WAKER.wake(); } } From 362624ca5fa0a979b45e5828abc338c85835537c Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 15 Sep 2025 21:57:49 +0200 Subject: [PATCH 6/7] Log current date and time when alarm is triggered --- examples/rp/src/bin/rtc_alarm.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs index e935dbf34..94b5fbd27 100644 --- a/examples/rp/src/bin/rtc_alarm.rs +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -55,7 +55,11 @@ async fn main(_spawner: Spawner) { } // Alarm triggered Either::Second(_) => { - info!("ALARM TRIGGERED!"); + let dt = rtc.now().unwrap(); + info!( + "ALARM TRIGGERED! Now: {}-{:02}-{:02} {}:{:02}:{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + ); } } } From a54996d8d76d7168ec458597cee276de975bd699 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Tue, 16 Sep 2025 05:49:14 +0200 Subject: [PATCH 7/7] RTC alarm test timing was off --- tests/rp/src/bin/rtc.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/rp/src/bin/rtc.rs b/tests/rp/src/bin/rtc.rs index 8d1d4ee14..c66981d95 100644 --- a/tests/rp/src/bin/rtc.rs +++ b/tests/rp/src/bin/rtc.rs @@ -36,6 +36,7 @@ async fn main(_spawner: Spawner) { second: 0, }; rtc.set_datetime(now).unwrap(); + Timer::after_millis(100).await; } // Test 1: Basic RTC functionality - read current time @@ -53,8 +54,18 @@ async fn main(_spawner: Spawner) { // Test 2: Schedule and wait for alarm info!("Testing alarm scheduling"); - // Schedule alarm for 3 seconds from now - let alarm_second = (initial_time.second + 3) % 60; + // Wait until we're at a predictable second, then schedule for a future second + loop { + let current = rtc.now().unwrap(); + if current.second <= 55 { + break; + } + Timer::after_millis(100).await; + } + + // Now schedule alarm for 3 seconds from current time + let current_time = rtc.now().unwrap(); + let alarm_second = (current_time.second + 3) % 60; let alarm_filter = DateTimeFilter::default().second(alarm_second); info!("Scheduling alarm for second: {}", alarm_second); @@ -69,13 +80,13 @@ async fn main(_spawner: Spawner) { let alarm_start = Instant::now(); match select(Timer::after_secs(5), rtc.wait_for_alarm()).await { Either::First(_) => { - core::panic!("Alarm timeout - alarm should have triggered within 5 seconds"); + core::panic!("Alarm timeout - alarm should have triggered by now"); } Either::Second(_) => { let alarm_duration = Instant::now() - alarm_start; info!("ALARM TRIGGERED after {:?}", alarm_duration); - // Verify timing is reasonable (should be around 3 seconds, allow some margin) + // Verify timing is reasonable (should be around 3 seconds) assert!( alarm_duration >= Duration::from_secs(2) && alarm_duration <= Duration::from_secs(4), "Alarm timing incorrect: {:?}", @@ -107,6 +118,7 @@ async fn main(_spawner: Spawner) { post_alarm_scheduled.is_none(), "Alarm should not be scheduled after triggering" ); + info!("Alarm correctly cleared after triggering"); info!("Test OK"); cortex_m::asm::bkpt();