1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
|
//! RTC DateTime driver.
use core::marker::PhantomData;
use embassy_hal_internal::{Peri, PeripheralType};
use maitake_sync::WaitCell;
use crate::clocks::with_clocks;
use crate::interrupt::typelevel::{Handler, Interrupt};
use crate::pac;
use crate::pac::rtc0::cr::Um;
type Regs = pac::rtc0::RegisterBlock;
/// Global wait cell for alarm notifications
static WAKER: WaitCell = WaitCell::new();
/// RTC interrupt handler.
pub struct InterruptHandler<I: Instance> {
_phantom: PhantomData<I>,
}
/// Trait for RTC peripheral instances
pub trait Instance: PeripheralType {
type Interrupt: Interrupt;
fn ptr() -> *const Regs;
}
/// Token for RTC0
pub type Rtc0 = crate::peripherals::RTC0;
impl Instance for crate::peripherals::RTC0 {
type Interrupt = crate::interrupt::typelevel::RTC;
#[inline(always)]
fn ptr() -> *const Regs {
pac::Rtc0::ptr()
}
}
/// Number of days in a standard year
const DAYS_IN_A_YEAR: u32 = 365;
/// Number of seconds in a day
const SECONDS_IN_A_DAY: u32 = 86400;
/// Number of seconds in an hour
const SECONDS_IN_A_HOUR: u32 = 3600;
/// Number of seconds in a minute
const SECONDS_IN_A_MINUTE: u32 = 60;
/// Unix epoch start year
const YEAR_RANGE_START: u16 = 1970;
/// Date and time structure for RTC operations
#[derive(Debug, Clone, Copy)]
pub struct RtcDateTime {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
#[derive(Copy, Clone)]
pub struct RtcConfig {
#[allow(dead_code)]
wakeup_select: bool,
update_mode: Um,
#[allow(dead_code)]
supervisor_access: bool,
compensation_interval: u8,
compensation_time: u8,
}
/// RTC interrupt enable flags
#[derive(Copy, Clone)]
pub struct RtcInterruptEnable;
impl RtcInterruptEnable {
pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
}
/// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01)
///
/// # Arguments
///
/// * `datetime` - The date and time to convert
///
/// # Returns
///
/// Unix timestamp as u32
///
/// # Note
///
/// This function handles leap years correctly.
pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
seconds += (datetime.year as u32 / 4) - (1970 / 4);
seconds += month_days[datetime.month as usize] as u32;
seconds += datetime.day as u32 - 1;
if (datetime.year & 3 == 0) && (datetime.month <= 2) {
seconds -= 1;
}
seconds = seconds * SECONDS_IN_A_DAY
+ (datetime.hour as u32 * SECONDS_IN_A_HOUR)
+ (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
+ datetime.second as u32;
seconds
}
/// Converts Unix timestamp to DateTime structure
///
/// # Arguments
///
/// * `seconds` - Unix timestamp (seconds since 1970-01-01)
///
/// # Returns
///
/// RtcDateTime structure with the converted date and time
///
/// # Note
///
/// This function handles leap years correctly.
pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
let mut seconds_remaining = seconds;
let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
seconds_remaining %= SECONDS_IN_A_DAY;
let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
seconds_remaining %= SECONDS_IN_A_HOUR;
let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
let mut year = YEAR_RANGE_START;
let mut days_in_year = DAYS_IN_A_YEAR;
while days > days_in_year {
days -= days_in_year;
year += 1;
days_in_year = if year.is_multiple_of(4) {
DAYS_IN_A_YEAR + 1
} else {
DAYS_IN_A_YEAR
};
}
let days_per_month = [
31,
if year.is_multiple_of(4) { 29 } else { 28 },
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,
];
let mut month = 1;
for (m, month_days) in days_per_month.iter().enumerate() {
let m = m + 1;
if days <= *month_days as u32 {
month = m;
break;
} else {
days -= *month_days as u32;
}
}
let day = days as u8;
RtcDateTime {
year,
month: month as u8,
day,
hour,
minute,
second,
}
}
/// Returns default RTC configuration
///
/// # Returns
///
/// RtcConfig with sensible default values:
/// - No wakeup selection
/// - Update mode 0 (immediate updates)
/// - No supervisor access restriction
/// - No compensation
pub fn get_default_config() -> RtcConfig {
RtcConfig {
wakeup_select: false,
update_mode: Um::Um0,
supervisor_access: false,
compensation_interval: 0,
compensation_time: 0,
}
}
/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
pub struct Rtc<'a, I: Instance> {
_inst: core::marker::PhantomData<&'a mut I>,
}
impl<'a, I: Instance> Rtc<'a, I> {
/// Create a new instance of the real time clock.
pub fn new(
_inst: Peri<'a, I>,
_irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
config: RtcConfig,
) -> Self {
let rtc = unsafe { &*I::ptr() };
// The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
// on the vsys domain is active
let clocks = with_clocks(|c| c.clk_16k_vsys.clone());
match clocks {
None => panic!("Clocks have not been initialized"),
Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"),
Some(Some(_)) => {}
}
// RTC reset
rtc.cr().modify(|_, w| w.swr().set_bit());
rtc.cr().modify(|_, w| w.swr().clear_bit());
rtc.tsr().write(|w| unsafe { w.bits(1) });
rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
rtc.tcr().modify(|_, w| unsafe {
w.cir()
.bits(config.compensation_interval)
.tcr()
.bits(config.compensation_time)
});
// Enable RTC interrupt
I::Interrupt::unpend();
unsafe { I::Interrupt::enable() };
Self {
_inst: core::marker::PhantomData,
}
}
/// Set the current date and time
///
/// # Arguments
///
/// * `datetime` - The date and time to set
///
/// # Note
///
/// The datetime is converted to Unix timestamp and written to the time seconds register.
pub fn set_datetime(&self, datetime: RtcDateTime) {
let rtc = unsafe { &*I::ptr() };
let seconds = convert_datetime_to_seconds(&datetime);
rtc.tsr().write(|w| unsafe { w.bits(seconds) });
}
/// Get the current date and time
///
/// # Returns
///
/// Current date and time as RtcDateTime
///
/// # Note
///
/// Reads the current Unix timestamp from the time seconds register and converts it.
pub fn get_datetime(&self) -> RtcDateTime {
let rtc = unsafe { &*I::ptr() };
let seconds = rtc.tsr().read().bits();
convert_seconds_to_datetime(seconds)
}
/// Set the alarm date and time
///
/// # Arguments
///
/// * `alarm` - The date and time when the alarm should trigger
///
/// # Note
///
/// This function:
/// - Clears any existing alarm by writing 0 to the alarm register
/// - Waits for the clear operation to complete
/// - Sets the new alarm time
/// - Waits for the write operation to complete
/// - Uses timeouts to prevent infinite loops
/// - Enables the alarm interrupt after setting
pub fn set_alarm(&self, alarm: RtcDateTime) {
let rtc = unsafe { &*I::ptr() };
let seconds = convert_datetime_to_seconds(&alarm);
rtc.tar().write(|w| unsafe { w.bits(0) });
let mut timeout = 10000;
while rtc.tar().read().bits() != 0 && timeout > 0 {
timeout -= 1;
}
rtc.tar().write(|w| unsafe { w.bits(seconds) });
let mut timeout = 10000;
while rtc.tar().read().bits() != seconds && timeout > 0 {
timeout -= 1;
}
self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
}
/// Get the current alarm date and time
///
/// # Returns
///
/// Alarm date and time as RtcDateTime
///
/// # Note
///
/// Reads the alarm timestamp from the time alarm register and converts it.
pub fn get_alarm(&self) -> RtcDateTime {
let rtc = unsafe { &*I::ptr() };
let alarm_seconds = rtc.tar().read().bits();
convert_seconds_to_datetime(alarm_seconds)
}
/// Start the RTC time counter
///
/// # Note
///
/// Sets the Time Counter Enable (TCE) bit in the status register.
pub fn start(&self) {
let rtc = unsafe { &*I::ptr() };
rtc.sr().modify(|_, w| w.tce().set_bit());
}
/// Stop the RTC time counter
///
/// # Note
///
/// Clears the Time Counter Enable (TCE) bit in the status register.
pub fn stop(&self) {
let rtc = unsafe { &*I::ptr() };
rtc.sr().modify(|_, w| w.tce().clear_bit());
}
/// Enable specific RTC interrupts
///
/// # Arguments
///
/// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants)
///
/// # Note
///
/// This function enables the specified interrupt types and resets the alarm occurred flag.
/// Available interrupts:
/// - Time Invalid Interrupt
/// - Time Overflow Interrupt
/// - Alarm Interrupt
/// - Seconds Interrupt
pub fn set_interrupt(&self, mask: u32) {
let rtc = unsafe { &*I::ptr() };
if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.tiie().tiie_1());
}
if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.toie().toie_1());
}
if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.taie().taie_1());
}
if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.tsie().tsie_1());
}
}
/// Disable specific RTC interrupts
///
/// # Arguments
///
/// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants)
///
/// # Note
///
/// This function disables the specified interrupt types.
pub fn disable_interrupt(&self, mask: u32) {
let rtc = unsafe { &*I::ptr() };
if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.tiie().tiie_0());
}
if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.toie().toie_0());
}
if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.taie().taie_0());
}
if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
rtc.ier().modify(|_, w| w.tsie().tsie_0());
}
}
/// Clear the alarm interrupt flag
///
/// # Note
///
/// This function clears the Time Alarm Interrupt Enable bit.
pub fn clear_alarm_flag(&self) {
let rtc = unsafe { &*I::ptr() };
rtc.ier().modify(|_, w| w.taie().clear_bit());
}
/// Wait for an RTC alarm to trigger.
///
/// # Arguments
///
/// * `alarm` - The date and time when the alarm should trigger
/// This function will wait until the RTC alarm is triggered.
/// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.
pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) {
let wait = WAKER.subscribe().await;
self.set_alarm(alarm);
self.start();
// REVISIT: propagate error?
let _ = wait.await;
// Clear the interrupt and disable the alarm after waking up
self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
}
}
/// RTC interrupt handler
///
/// This struct implements the interrupt handler for RTC events.
impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let rtc = &*pac::Rtc0::ptr();
// Check if this is actually a time alarm interrupt
let sr = rtc.sr().read();
if sr.taf().bit_is_set() {
rtc.ier().modify(|_, w| w.taie().clear_bit());
WAKER.wake();
}
}
}
|