1
1
//! RTC driver.
2
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
8
use embassy_hal_internal:: { Peri , PeripheralType } ;
9
+ use embassy_sync:: waitqueue:: AtomicWaker ;
5
10
6
11
pub use self :: filter:: DateTimeFilter ;
7
12
@@ -11,6 +16,13 @@ mod datetime;
11
16
12
17
pub use self :: datetime:: { DateTime , DayOfWeek , Error as DateTimeError } ;
13
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
27
/// A reference to the real time clock of the system
16
28
pub struct Rtc < ' d , T : Instance > {
@@ -23,10 +35,15 @@ impl<'d, T: Instance> Rtc<'d, T> {
23
35
/// # Errors
24
36
///
25
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
39
// Set the RTC divider
28
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
47
Self { inner }
31
48
}
32
49
@@ -174,6 +191,110 @@ impl<'d, T: Instance> Rtc<'d, T> {
174
191
pub fn clear_interrupt ( & mut self ) {
175
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
+ // Atomically check and clear the alarm occurred flag to prevent race conditions
263
+ if critical_section:: with ( |_| {
264
+ let occurred = ALARM_OCCURRED . load ( Ordering :: SeqCst ) ;
265
+ if occurred {
266
+ ALARM_OCCURRED . store ( false , Ordering :: SeqCst ) ;
267
+ }
268
+ occurred
269
+ } ) {
270
+ // Clear the interrupt and disable the alarm
271
+ self . clear_interrupt ( ) ;
272
+
273
+ compiler_fence ( Ordering :: SeqCst ) ;
274
+ return Poll :: Ready ( ( ) ) ;
275
+ } else {
276
+ return Poll :: Pending ;
277
+ }
278
+ } )
279
+ . await ;
280
+ }
281
+ }
282
+
283
+ /// Interrupt handler.
284
+ pub struct InterruptHandler {
285
+ _empty : ( ) ,
286
+ }
287
+
288
+ impl crate :: interrupt:: typelevel:: Handler < crate :: interrupt:: typelevel:: RTC_IRQ > for InterruptHandler {
289
+ unsafe fn on_interrupt ( ) {
290
+ // Disable the alarm first thing, to prevent unexpected re-entry
291
+ let rtc = crate :: pac:: RTC ;
292
+ rtc. irq_setup_0 ( ) . modify ( |w| w. set_match_ena ( false ) ) ;
293
+
294
+ // Set the alarm occurred flag and wake the waker
295
+ ALARM_OCCURRED . store ( true , Ordering :: SeqCst ) ;
296
+ WAKER . wake ( ) ;
297
+ }
177
298
}
178
299
179
300
/// Errors that can occur on methods on [Rtc]
0 commit comments