diff options
| author | 1-rafael-1 <[email protected]> | 2025-09-15 21:10:22 +0200 |
|---|---|---|
| committer | 1-rafael-1 <[email protected]> | 2025-09-15 21:10:22 +0200 |
| commit | ac32f43c3dc915b78e71328855189b8aacfec8c3 (patch) | |
| tree | 42d52279b5c13c9557f34bf4e022ca965351b93d | |
| parent | 37fd802f961486b176c0cdda9087ecf987d51eb8 (diff) | |
alarm handling to poll hardware status directly; add ci test
| -rw-r--r-- | embassy-rp/src/rtc/mod.rs | 19 | ||||
| -rw-r--r-- | examples/rp/src/bin/rtc_alarm.rs | 12 | ||||
| -rw-r--r-- | tests/rp/Cargo.toml | 6 | ||||
| -rw-r--r-- | tests/rp/src/bin/rtc.rs | 113 |
4 files changed, 129 insertions, 21 deletions
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 @@ | |||
| 2 | mod filter; | 2 | mod filter; |
| 3 | 3 | ||
| 4 | use core::future::poll_fn; | 4 | use core::future::poll_fn; |
| 5 | use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; | 5 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{Peri, PeripheralType}; | 8 | use embassy_hal_internal::{Peri, PeripheralType}; |
| @@ -21,8 +21,6 @@ use crate::interrupt::{self, InterruptExt}; | |||
| 21 | 21 | ||
| 22 | // Static waker for the interrupt handler | 22 | // Static waker for the interrupt handler |
| 23 | static WAKER: AtomicWaker = AtomicWaker::new(); | 23 | static WAKER: AtomicWaker = AtomicWaker::new(); |
| 24 | // Static flag to indicate if an alarm has occurred | ||
| 25 | static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false); | ||
| 26 | 24 | ||
| 27 | /// A reference to the real time clock of the system | 25 | /// A reference to the real time clock of the system |
| 28 | pub struct Rtc<'d, T: Instance> { | 26 | pub struct Rtc<'d, T: Instance> { |
| @@ -259,19 +257,15 @@ impl<'d, T: Instance> Rtc<'d, T> { | |||
| 259 | poll_fn(|cx| { | 257 | poll_fn(|cx| { |
| 260 | WAKER.register(cx.waker()); | 258 | WAKER.register(cx.waker()); |
| 261 | 259 | ||
| 262 | // If the alarm has occured, we will clear the interrupt and return ready | 260 | // Check hardware interrupt status directly |
| 263 | if ALARM_OCCURRED.load(Ordering::SeqCst) { | 261 | if self.inner.regs().ints().read().rtc() { |
| 264 | // Clear the alarm occurred flag | 262 | // Clear the interrupt status and disable the alarm |
| 265 | ALARM_OCCURRED.store(false, Ordering::SeqCst); | 263 | self.inner.regs().ints().write(|w| w.set_rtc(true)); |
| 266 | |||
| 267 | // Clear the interrupt and disable the alarm | ||
| 268 | self.clear_interrupt(); | 264 | self.clear_interrupt(); |
| 269 | 265 | ||
| 270 | // Return ready | ||
| 271 | compiler_fence(Ordering::SeqCst); | 266 | compiler_fence(Ordering::SeqCst); |
| 272 | return Poll::Ready(()); | 267 | return Poll::Ready(()); |
| 273 | } else { | 268 | } else { |
| 274 | // If the alarm has not occurred, we will return pending | ||
| 275 | return Poll::Pending; | 269 | return Poll::Pending; |
| 276 | } | 270 | } |
| 277 | }) | 271 | }) |
| @@ -290,8 +284,7 @@ impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC_IRQ> | |||
| 290 | let rtc = crate::pac::RTC; | 284 | let rtc = crate::pac::RTC; |
| 291 | rtc.irq_setup_0().modify(|w| w.set_match_ena(false)); | 285 | rtc.irq_setup_0().modify(|w| w.set_match_ena(false)); |
| 292 | 286 | ||
| 293 | // Set the alarm occurred flag and wake the waker | 287 | // Wake the waker - interrupt status will be checked directly by polling future |
| 294 | ALARM_OCCURRED.store(true, Ordering::SeqCst); | ||
| 295 | WAKER.wake(); | 288 | WAKER.wake(); |
| 296 | } | 289 | } |
| 297 | } | 290 | } |
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) { | |||
| 47 | ); | 47 | ); |
| 48 | 48 | ||
| 49 | // See if the alarm is already scheduled, if not, schedule it | 49 | // See if the alarm is already scheduled, if not, schedule it |
| 50 | match rtc.alarm_scheduled() { | 50 | if rtc.alarm_scheduled().is_none() { |
| 51 | None => { | 51 | info!("Scheduling alarm for 30 seconds from now"); |
| 52 | info!("Scheduling alarm for 30 seconds from now"); | 52 | rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); |
| 53 | rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); | 53 | info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); |
| 54 | |||
| 55 | info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); | ||
| 56 | } | ||
| 57 | Some(_) => {} | ||
| 58 | } | 54 | } |
| 59 | } | 55 | } |
| 60 | // Alarm triggered | 56 | // 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" | |||
| 64 | path = "src/bin/float.rs" | 64 | path = "src/bin/float.rs" |
| 65 | required-features = [ "rp2040",] | 65 | required-features = [ "rp2040",] |
| 66 | 66 | ||
| 67 | # RTC is only available on RP2040 | ||
| 68 | [[bin]] | ||
| 69 | name = "rtc" | ||
| 70 | path = "src/bin/rtc.rs" | ||
| 71 | required-features = [ "rp2040",] | ||
| 72 | |||
| 67 | [profile.dev] | 73 | [profile.dev] |
| 68 | debug = 2 | 74 | debug = 2 |
| 69 | debug-assertions = true | 75 | 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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #[cfg(feature = "rp2040")] | ||
| 4 | teleprobe_meta::target!(b"rpi-pico"); | ||
| 5 | |||
| 6 | use defmt::{assert, *}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_futures::select::{select, Either}; | ||
| 9 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; | ||
| 11 | use embassy_time::{Duration, Instant, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | // Bind the RTC interrupt to the handler | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | RTC_IRQ => embassy_rp::rtc::InterruptHandler; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_rp::init(Default::default()); | ||
| 22 | let mut rtc = Rtc::new(p.RTC, Irqs); | ||
| 23 | |||
| 24 | info!("RTC test started"); | ||
| 25 | |||
| 26 | // Initialize RTC if not running | ||
| 27 | if !rtc.is_running() { | ||
| 28 | info!("Starting RTC"); | ||
| 29 | let now = DateTime { | ||
| 30 | year: 2000, | ||
| 31 | month: 1, | ||
| 32 | day: 1, | ||
| 33 | day_of_week: DayOfWeek::Saturday, | ||
| 34 | hour: 0, | ||
| 35 | minute: 0, | ||
| 36 | second: 0, | ||
| 37 | }; | ||
| 38 | rtc.set_datetime(now).unwrap(); | ||
| 39 | } | ||
| 40 | |||
| 41 | // Test 1: Basic RTC functionality - read current time | ||
| 42 | let initial_time = rtc.now().unwrap(); | ||
| 43 | info!( | ||
| 44 | "Initial time: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 45 | initial_time.year, | ||
| 46 | initial_time.month, | ||
| 47 | initial_time.day, | ||
| 48 | initial_time.hour, | ||
| 49 | initial_time.minute, | ||
| 50 | initial_time.second | ||
| 51 | ); | ||
| 52 | |||
| 53 | // Test 2: Schedule and wait for alarm | ||
| 54 | info!("Testing alarm scheduling"); | ||
| 55 | |||
| 56 | // Schedule alarm for 3 seconds from now | ||
| 57 | let alarm_second = (initial_time.second + 3) % 60; | ||
| 58 | let alarm_filter = DateTimeFilter::default().second(alarm_second); | ||
| 59 | |||
| 60 | info!("Scheduling alarm for second: {}", alarm_second); | ||
| 61 | rtc.schedule_alarm(alarm_filter); | ||
| 62 | |||
| 63 | // Verify alarm is scheduled | ||
| 64 | let scheduled = rtc.alarm_scheduled(); | ||
| 65 | assert!(scheduled.is_some(), "Alarm should be scheduled"); | ||
| 66 | info!("Alarm scheduled successfully: {}", scheduled.unwrap()); | ||
| 67 | |||
| 68 | // Wait for alarm with timeout | ||
| 69 | let alarm_start = Instant::now(); | ||
| 70 | match select(Timer::after_secs(5), rtc.wait_for_alarm()).await { | ||
| 71 | Either::First(_) => { | ||
| 72 | core::panic!("Alarm timeout - alarm should have triggered within 5 seconds"); | ||
| 73 | } | ||
| 74 | Either::Second(_) => { | ||
| 75 | let alarm_duration = Instant::now() - alarm_start; | ||
| 76 | info!("ALARM TRIGGERED after {:?}", alarm_duration); | ||
| 77 | |||
| 78 | // Verify timing is reasonable (should be around 3 seconds, allow some margin) | ||
| 79 | assert!( | ||
| 80 | alarm_duration >= Duration::from_secs(2) && alarm_duration <= Duration::from_secs(4), | ||
| 81 | "Alarm timing incorrect: {:?}", | ||
| 82 | alarm_duration | ||
| 83 | ); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | // Test 3: Verify RTC is still running and time has advanced | ||
| 88 | let final_time = rtc.now().unwrap(); | ||
| 89 | info!( | ||
| 90 | "Final time: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 91 | final_time.year, final_time.month, final_time.day, final_time.hour, final_time.minute, final_time.second | ||
| 92 | ); | ||
| 93 | |||
| 94 | // Verify time has advanced (allowing for minute/hour rollover) | ||
| 95 | let time_diff = if final_time.second >= initial_time.second { | ||
| 96 | final_time.second - initial_time.second | ||
| 97 | } else { | ||
| 98 | 60 - initial_time.second + final_time.second | ||
| 99 | }; | ||
| 100 | |||
| 101 | assert!(time_diff >= 3, "RTC should have advanced by at least 3 seconds"); | ||
| 102 | info!("Time advanced by {} seconds", time_diff); | ||
| 103 | |||
| 104 | // Test 4: Verify alarm is no longer scheduled after triggering | ||
| 105 | let post_alarm_scheduled = rtc.alarm_scheduled(); | ||
| 106 | assert!( | ||
| 107 | post_alarm_scheduled.is_none(), | ||
| 108 | "Alarm should not be scheduled after triggering" | ||
| 109 | ); | ||
| 110 | |||
| 111 | info!("Test OK"); | ||
| 112 | cortex_m::asm::bkpt(); | ||
| 113 | } | ||
