aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathisDerooNXP <[email protected]>2025-12-04 09:42:46 -0800
committerGitHub <[email protected]>2025-12-04 09:42:46 -0800
commit22bae2d80153003af4637c4b0cc26d52858f5228 (patch)
tree61860b41c823f499984a369f6762489a1766cce8
parentcc918e997a8709c213c173edb8657ff269dae7a4 (diff)
Rtc support v2 (#91)
Signed-off-by: Mathis Deroo <[email protected]> Signed-off-by: Felipe Balbi <[email protected]>
-rw-r--r--examples/src/bin/rtc_alarm.rs39
-rw-r--r--src/lib.rs1
-rw-r--r--src/rtc.rs198
3 files changed, 182 insertions, 56 deletions
diff --git a/examples/src/bin/rtc_alarm.rs b/examples/src/bin/rtc_alarm.rs
index a7800a2d1..fe355888b 100644
--- a/examples/src/bin/rtc_alarm.rs
+++ b/examples/src/bin/rtc_alarm.rs
@@ -2,23 +2,14 @@
2#![no_main] 2#![no_main]
3 3
4use embassy_executor::Spawner; 4use embassy_executor::Spawner;
5use embassy_mcxa as hal;
6use hal::rtc::{RtcDateTime, RtcInterruptEnable};
7use hal::InterruptExt;
8
9type MyRtc = hal::rtc::Rtc<'static, hal::rtc::Rtc0>;
10
11use embassy_mcxa::bind_interrupts; 5use embassy_mcxa::bind_interrupts;
12use {defmt_rtt as _, panic_probe as _}; 6use hal::rtc::{InterruptHandler, Rtc, RtcDateTime};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
13 8
14bind_interrupts!(struct Irqs { 9bind_interrupts!(struct Irqs {
15 RTC => hal::rtc::RtcHandler; 10 RTC => InterruptHandler<hal::rtc::Rtc0>;
16}); 11});
17 12
18#[used]
19#[no_mangle]
20static KEEP_RTC: unsafe extern "C" fn() = RTC;
21
22#[embassy_executor::main] 13#[embassy_executor::main]
23async fn main(_spawner: Spawner) { 14async fn main(_spawner: Spawner) {
24 let p = hal::init(hal::config::Config::default()); 15 let p = hal::init(hal::config::Config::default());
@@ -27,7 +18,7 @@ async fn main(_spawner: Spawner) {
27 18
28 let rtc_config = hal::rtc::get_default_config(); 19 let rtc_config = hal::rtc::get_default_config();
29 20
30 let rtc = MyRtc::new(p.RTC0, rtc_config); 21 let mut rtc = Rtc::new(p.RTC0, Irqs, rtc_config);
31 22
32 let now = RtcDateTime { 23 let now = RtcDateTime {
33 year: 2025, 24 year: 2025,
@@ -46,29 +37,11 @@ async fn main(_spawner: Spawner) {
46 let mut alarm = now; 37 let mut alarm = now;
47 alarm.second += 10; 38 alarm.second += 10;
48 39
49 rtc.set_alarm(alarm);
50 defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)"); 40 defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)");
51
52 rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
53
54 unsafe {
55 hal::interrupt::RTC.enable();
56 }
57
58 unsafe {
59 cortex_m::interrupt::enable();
60 }
61
62 rtc.start();
63
64 defmt::info!("RTC started, waiting for alarm..."); 41 defmt::info!("RTC started, waiting for alarm...");
65 42
66 loop { 43 rtc.wait_for_alarm(alarm).await;
67 if rtc.is_alarm_triggered() { 44 defmt::info!("*** ALARM TRIGGERED! ***");
68 defmt::info!("*** ALARM TRIGGERED! ***");
69 break;
70 }
71 }
72 45
73 defmt::info!("Example complete - Test PASSED!"); 46 defmt::info!("Example complete - Test PASSED!");
74} 47}
diff --git a/src/lib.rs b/src/lib.rs
index fb204d27b..c6d8adc8f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -333,7 +333,6 @@ pub use interrupt::InterruptExt;
333pub use mcxa_pac as pac; 333pub use mcxa_pac as pac;
334#[cfg(not(feature = "unstable-pac"))] 334#[cfg(not(feature = "unstable-pac"))]
335pub(crate) use mcxa_pac as pac; 335pub(crate) use mcxa_pac as pac;
336pub use rtc::Rtc0 as Rtc0Token;
337 336
338/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. 337/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
339/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling). 338/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
diff --git a/src/rtc.rs b/src/rtc.rs
index b750a97ea..f975d9c9f 100644
--- a/src/rtc.rs
+++ b/src/rtc.rs
@@ -1,36 +1,52 @@
1//! RTC DateTime driver. 1//! RTC DateTime driver.
2use core::sync::atomic::{AtomicBool, Ordering}; 2use core::marker::PhantomData;
3 3
4use embassy_hal_internal::{Peri, PeripheralType}; 4use embassy_hal_internal::{Peri, PeripheralType};
5use maitake_sync::WaitCell;
5 6
6use crate::clocks::with_clocks; 7use crate::clocks::with_clocks;
8use crate::interrupt::typelevel::{Handler, Interrupt};
7use crate::pac; 9use crate::pac;
8use crate::pac::rtc0::cr::Um; 10use crate::pac::rtc0::cr::Um;
9 11
10type Regs = pac::rtc0::RegisterBlock; 12type Regs = pac::rtc0::RegisterBlock;
11 13
12static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); 14/// Global wait cell for alarm notifications
15static WAKER: WaitCell = WaitCell::new();
13 16
14// Token-based instance pattern like embassy-imxrt 17/// RTC interrupt handler.
18pub struct InterruptHandler<I: Instance> {
19 _phantom: PhantomData<I>,
20}
21
22/// Trait for RTC peripheral instances
15pub trait Instance: PeripheralType { 23pub trait Instance: PeripheralType {
24 type Interrupt: Interrupt;
16 fn ptr() -> *const Regs; 25 fn ptr() -> *const Regs;
17} 26}
18 27
19/// Token for RTC0 28/// Token for RTC0
20pub type Rtc0 = crate::peripherals::RTC0; 29pub type Rtc0 = crate::peripherals::RTC0;
21impl Instance for crate::peripherals::RTC0 { 30impl Instance for crate::peripherals::RTC0 {
31 type Interrupt = crate::interrupt::typelevel::RTC;
22 #[inline(always)] 32 #[inline(always)]
23 fn ptr() -> *const Regs { 33 fn ptr() -> *const Regs {
24 pac::Rtc0::ptr() 34 pac::Rtc0::ptr()
25 } 35 }
26} 36}
27 37
38/// Number of days in a standard year
28const DAYS_IN_A_YEAR: u32 = 365; 39const DAYS_IN_A_YEAR: u32 = 365;
40/// Number of seconds in a day
29const SECONDS_IN_A_DAY: u32 = 86400; 41const SECONDS_IN_A_DAY: u32 = 86400;
42/// Number of seconds in an hour
30const SECONDS_IN_A_HOUR: u32 = 3600; 43const SECONDS_IN_A_HOUR: u32 = 3600;
44/// Number of seconds in a minute
31const SECONDS_IN_A_MINUTE: u32 = 60; 45const SECONDS_IN_A_MINUTE: u32 = 60;
46/// Unix epoch start year
32const YEAR_RANGE_START: u16 = 1970; 47const YEAR_RANGE_START: u16 = 1970;
33 48
49/// Date and time structure for RTC operations
34#[derive(Debug, Clone, Copy)] 50#[derive(Debug, Clone, Copy)]
35pub struct RtcDateTime { 51pub struct RtcDateTime {
36 pub year: u16, 52 pub year: u16,
@@ -51,6 +67,7 @@ pub struct RtcConfig {
51 compensation_time: u8, 67 compensation_time: u8,
52} 68}
53 69
70/// RTC interrupt enable flags
54#[derive(Copy, Clone)] 71#[derive(Copy, Clone)]
55pub struct RtcInterruptEnable; 72pub struct RtcInterruptEnable;
56impl RtcInterruptEnable { 73impl RtcInterruptEnable {
@@ -60,6 +77,19 @@ impl RtcInterruptEnable {
60 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4; 77 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
61} 78}
62 79
80/// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01)
81///
82/// # Arguments
83///
84/// * `datetime` - The date and time to convert
85///
86/// # Returns
87///
88/// Unix timestamp as u32
89///
90/// # Note
91///
92/// This function handles leap years correctly.
63pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 { 93pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
64 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; 94 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
65 95
@@ -80,6 +110,19 @@ pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
80 seconds 110 seconds
81} 111}
82 112
113/// Converts Unix timestamp to DateTime structure
114///
115/// # Arguments
116///
117/// * `seconds` - Unix timestamp (seconds since 1970-01-01)
118///
119/// # Returns
120///
121/// RtcDateTime structure with the converted date and time
122///
123/// # Note
124///
125/// This function handles leap years correctly.
83pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime { 126pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
84 let mut seconds_remaining = seconds; 127 let mut seconds_remaining = seconds;
85 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1; 128 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
@@ -142,6 +185,15 @@ pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
142 } 185 }
143} 186}
144 187
188/// Returns default RTC configuration
189///
190/// # Returns
191///
192/// RtcConfig with sensible default values:
193/// - No wakeup selection
194/// - Update mode 0 (immediate updates)
195/// - No supervisor access restriction
196/// - No compensation
145pub fn get_default_config() -> RtcConfig { 197pub fn get_default_config() -> RtcConfig {
146 RtcConfig { 198 RtcConfig {
147 wakeup_select: false, 199 wakeup_select: false,
@@ -157,8 +209,12 @@ pub struct Rtc<'a, I: Instance> {
157} 209}
158 210
159impl<'a, I: Instance> Rtc<'a, I> { 211impl<'a, I: Instance> Rtc<'a, I> {
160 /// initialize RTC 212 /// Create a new instance of the real time clock.
161 pub fn new(_inst: Peri<'a, I>, config: RtcConfig) -> Self { 213 pub fn new(
214 _inst: Peri<'a, I>,
215 _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
216 config: RtcConfig,
217 ) -> Self {
162 let rtc = unsafe { &*I::ptr() }; 218 let rtc = unsafe { &*I::ptr() };
163 219
164 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock 220 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
@@ -170,7 +226,7 @@ impl<'a, I: Instance> Rtc<'a, I> {
170 Some(Some(_)) => {} 226 Some(Some(_)) => {}
171 } 227 }
172 228
173 /* RTC reset */ 229 // RTC reset
174 rtc.cr().modify(|_, w| w.swr().set_bit()); 230 rtc.cr().modify(|_, w| w.swr().set_bit());
175 rtc.cr().modify(|_, w| w.swr().clear_bit()); 231 rtc.cr().modify(|_, w| w.swr().clear_bit());
176 rtc.tsr().write(|w| unsafe { w.bits(1) }); 232 rtc.tsr().write(|w| unsafe { w.bits(1) });
@@ -184,23 +240,60 @@ impl<'a, I: Instance> Rtc<'a, I> {
184 .bits(config.compensation_time) 240 .bits(config.compensation_time)
185 }); 241 });
186 242
243 // Enable RTC interrupt
244 I::Interrupt::unpend();
245 unsafe { I::Interrupt::enable() };
246
187 Self { 247 Self {
188 _inst: core::marker::PhantomData, 248 _inst: core::marker::PhantomData,
189 } 249 }
190 } 250 }
191 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.
192 pub fn set_datetime(&self, datetime: RtcDateTime) { 261 pub fn set_datetime(&self, datetime: RtcDateTime) {
193 let rtc = unsafe { &*I::ptr() }; 262 let rtc = unsafe { &*I::ptr() };
194 let seconds = convert_datetime_to_seconds(&datetime); 263 let seconds = convert_datetime_to_seconds(&datetime);
195 rtc.tsr().write(|w| unsafe { w.bits(seconds) }); 264 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
196 } 265 }
197 266
267 /// Get the current date and time
268 ///
269 /// # Returns
270 ///
271 /// Current date and time as RtcDateTime
272 ///
273 /// # Note
274 ///
275 /// Reads the current Unix timestamp from the time seconds register and converts it.
198 pub fn get_datetime(&self) -> RtcDateTime { 276 pub fn get_datetime(&self) -> RtcDateTime {
199 let rtc = unsafe { &*I::ptr() }; 277 let rtc = unsafe { &*I::ptr() };
200 let seconds = rtc.tsr().read().bits(); 278 let seconds = rtc.tsr().read().bits();
201 convert_seconds_to_datetime(seconds) 279 convert_seconds_to_datetime(seconds)
202 } 280 }
203 281
282 /// Set the alarm date and time
283 ///
284 /// # Arguments
285 ///
286 /// * `alarm` - The date and time when the alarm should trigger
287 ///
288 /// # Note
289 ///
290 /// This function:
291 /// - Clears any existing alarm by writing 0 to the alarm register
292 /// - Waits for the clear operation to complete
293 /// - Sets the new alarm time
294 /// - Waits for the write operation to complete
295 /// - Uses timeouts to prevent infinite loops
296 /// - Enables the alarm interrupt after setting
204 pub fn set_alarm(&self, alarm: RtcDateTime) { 297 pub fn set_alarm(&self, alarm: RtcDateTime) {
205 let rtc = unsafe { &*I::ptr() }; 298 let rtc = unsafe { &*I::ptr() };
206 let seconds = convert_datetime_to_seconds(&alarm); 299 let seconds = convert_datetime_to_seconds(&alarm);
@@ -217,24 +310,59 @@ impl<'a, I: Instance> Rtc<'a, I> {
217 while rtc.tar().read().bits() != seconds && timeout > 0 { 310 while rtc.tar().read().bits() != seconds && timeout > 0 {
218 timeout -= 1; 311 timeout -= 1;
219 } 312 }
313
314 self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
220 } 315 }
221 316
317 /// Get the current alarm date and time
318 ///
319 /// # Returns
320 ///
321 /// Alarm date and time as RtcDateTime
322 ///
323 /// # Note
324 ///
325 /// Reads the alarm timestamp from the time alarm register and converts it.
222 pub fn get_alarm(&self) -> RtcDateTime { 326 pub fn get_alarm(&self) -> RtcDateTime {
223 let rtc = unsafe { &*I::ptr() }; 327 let rtc = unsafe { &*I::ptr() };
224 let alarm_seconds = rtc.tar().read().bits(); 328 let alarm_seconds = rtc.tar().read().bits();
225 convert_seconds_to_datetime(alarm_seconds) 329 convert_seconds_to_datetime(alarm_seconds)
226 } 330 }
227 331
332 /// Start the RTC time counter
333 ///
334 /// # Note
335 ///
336 /// Sets the Time Counter Enable (TCE) bit in the status register.
228 pub fn start(&self) { 337 pub fn start(&self) {
229 let rtc = unsafe { &*I::ptr() }; 338 let rtc = unsafe { &*I::ptr() };
230 rtc.sr().modify(|_, w| w.tce().set_bit()); 339 rtc.sr().modify(|_, w| w.tce().set_bit());
231 } 340 }
232 341
342 /// Stop the RTC time counter
343 ///
344 /// # Note
345 ///
346 /// Clears the Time Counter Enable (TCE) bit in the status register.
233 pub fn stop(&self) { 347 pub fn stop(&self) {
234 let rtc = unsafe { &*I::ptr() }; 348 let rtc = unsafe { &*I::ptr() };
235 rtc.sr().modify(|_, w| w.tce().clear_bit()); 349 rtc.sr().modify(|_, w| w.tce().clear_bit());
236 } 350 }
237 351
352 /// Enable specific RTC interrupts
353 ///
354 /// # Arguments
355 ///
356 /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants)
357 ///
358 /// # Note
359 ///
360 /// This function enables the specified interrupt types and resets the alarm occurred flag.
361 /// Available interrupts:
362 /// - Time Invalid Interrupt
363 /// - Time Overflow Interrupt
364 /// - Alarm Interrupt
365 /// - Seconds Interrupt
238 pub fn set_interrupt(&self, mask: u32) { 366 pub fn set_interrupt(&self, mask: u32) {
239 let rtc = unsafe { &*I::ptr() }; 367 let rtc = unsafe { &*I::ptr() };
240 368
@@ -250,10 +378,17 @@ impl<'a, I: Instance> Rtc<'a, I> {
250 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { 378 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
251 rtc.ier().modify(|_, w| w.tsie().tsie_1()); 379 rtc.ier().modify(|_, w| w.tsie().tsie_1());
252 } 380 }
253
254 ALARM_TRIGGERED.store(false, Ordering::SeqCst);
255 } 381 }
256 382
383 /// Disable specific RTC interrupts
384 ///
385 /// # Arguments
386 ///
387 /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants)
388 ///
389 /// # Note
390 ///
391 /// This function disables the specified interrupt types.
257 pub fn disable_interrupt(&self, mask: u32) { 392 pub fn disable_interrupt(&self, mask: u32) {
258 let rtc = unsafe { &*I::ptr() }; 393 let rtc = unsafe { &*I::ptr() };
259 394
@@ -271,29 +406,48 @@ impl<'a, I: Instance> Rtc<'a, I> {
271 } 406 }
272 } 407 }
273 408
409 /// Clear the alarm interrupt flag
410 ///
411 /// # Note
412 ///
413 /// This function clears the Time Alarm Interrupt Enable bit.
274 pub fn clear_alarm_flag(&self) { 414 pub fn clear_alarm_flag(&self) {
275 let rtc = unsafe { &*I::ptr() }; 415 let rtc = unsafe { &*I::ptr() };
276 rtc.ier().modify(|_, w| w.taie().clear_bit()); 416 rtc.ier().modify(|_, w| w.taie().clear_bit());
277 } 417 }
278 418
279 pub fn is_alarm_triggered(&self) -> bool { 419 /// Wait for an RTC alarm to trigger.
280 ALARM_TRIGGERED.load(Ordering::Relaxed) 420 ///
281 } 421 /// # Arguments
282} 422 ///
423 /// * `alarm` - The date and time when the alarm should trigger
424 /// This function will wait until the RTC alarm is triggered.
425 /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.
426 pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) {
427 let wait = WAKER.subscribe().await;
283 428
284pub fn on_interrupt() { 429 self.set_alarm(alarm);
285 let rtc = unsafe { &*pac::Rtc0::ptr() }; 430 self.start();
286 // Check if this is actually a time alarm interrupt 431
287 let sr = rtc.sr().read(); 432 // REVISIT: propagate error?
288 if sr.taf().bit_is_set() { 433 let _ = wait.await;
289 rtc.ier().modify(|_, w| w.taie().clear_bit()); 434
290 ALARM_TRIGGERED.store(true, Ordering::SeqCst); 435 // Clear the interrupt and disable the alarm after waking up
436 self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
291 } 437 }
292} 438}
293 439
294pub struct RtcHandler; 440/// RTC interrupt handler
295impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC> for RtcHandler { 441///
442/// This struct implements the interrupt handler for RTC events.
443impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
296 unsafe fn on_interrupt() { 444 unsafe fn on_interrupt() {
297 on_interrupt(); 445 let rtc = &*pac::Rtc0::ptr();
446 // Check if this is actually a time alarm interrupt
447 let sr = rtc.sr().read();
448 if sr.taf().bit_is_set() {
449 rtc.ier().modify(|_, w| w.taie().clear_bit());
450 WAKER.wake();
451 }
298 } 452 }
299} 453}