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