aboutsummaryrefslogtreecommitdiff
path: root/embassy-nxp/src/time_driver
diff options
context:
space:
mode:
authorIrina Chiorean <[email protected]>2025-07-25 18:00:49 +0300
committerIrina Chiorean <[email protected]>2025-08-04 12:58:41 +0300
commit517714c98e4b5dc4c7ee844527f5d33fdc342125 (patch)
tree7dde24bb8bb16a14995483bb71450801bbf0675d /embassy-nxp/src/time_driver
parenta8cb8a7fe1f594b765dee4cfc6ff3065842c7c6e (diff)
feat: add RTC time driver
Diffstat (limited to 'embassy-nxp/src/time_driver')
-rw-r--r--embassy-nxp/src/time_driver/rtc.rs172
1 files changed, 172 insertions, 0 deletions
diff --git a/embassy-nxp/src/time_driver/rtc.rs b/embassy-nxp/src/time_driver/rtc.rs
new file mode 100644
index 000000000..94272e9c2
--- /dev/null
+++ b/embassy-nxp/src/time_driver/rtc.rs
@@ -0,0 +1,172 @@
1use core::cell::{Cell, RefCell};
2use core::task::Waker;
3
4use critical_section::CriticalSection;
5use embassy_hal_internal::interrupt::{InterruptExt, Priority};
6use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
7use embassy_time_driver::{time_driver_impl, Driver};
8use embassy_time_queue_utils::Queue;
9use lpc55_pac::{interrupt, PMC, RTC, SYSCON};
10struct AlarmState {
11 timestamp: Cell<u64>,
12}
13
14unsafe impl Send for AlarmState {}
15
16impl AlarmState {
17 const fn new() -> Self {
18 Self {
19 timestamp: Cell::new(u64::MAX),
20 }
21 }
22}
23
24pub struct RtcDriver {
25 alarms: Mutex<AlarmState>,
26 queue: Mutex<RefCell<Queue>>,
27}
28
29time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
30 alarms: Mutex::new(AlarmState::new()),
31 queue: Mutex::new(RefCell::new(Queue::new())),
32});
33impl RtcDriver {
34 fn init(&'static self) {
35 let syscon = unsafe { &*SYSCON::ptr() };
36 let pmc = unsafe { &*PMC::ptr() };
37 let rtc = unsafe { &*RTC::ptr() };
38
39 syscon.ahbclkctrl0.modify(|_, w| w.rtc().enable());
40
41 // By default the RTC enters software reset. If for some reason it is
42 // not in reset, we enter and them promptly leave.q
43 rtc.ctrl.modify(|_, w| w.swreset().set_bit());
44 rtc.ctrl.modify(|_, w| w.swreset().clear_bit());
45
46 // Select clock source - either XTAL or FRO
47 // pmc.rtcosc32k.write(|w| w.sel().xtal32k());
48 pmc.rtcosc32k.write(|w| w.sel().fro32k());
49
50 // Start the RTC peripheral
51 rtc.ctrl.modify(|_, w| w.rtc_osc_pd().power_up());
52
53 // rtc.ctrl.modify(|_, w| w.rtc_en().clear_bit()); // EXTRA
54
55 //reset/clear(?) counter
56 rtc.count.reset();
57 //en rtc main counter
58 rtc.ctrl.modify(|_, w| w.rtc_en().set_bit());
59 rtc.ctrl.modify(|_, w| w.rtc1khz_en().set_bit());
60 // subsec counter enable
61 rtc.ctrl.modify(|_, w| w.rtc_subsec_ena().set_bit());
62
63 // enable irq
64 unsafe {
65 interrupt::RTC.set_priority(Priority::from(3));
66 interrupt::RTC.enable();
67 }
68 }
69
70 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
71 let rtc = unsafe { &*RTC::ptr() };
72 let alarm = &self.alarms.borrow(cs);
73 alarm.timestamp.set(timestamp);
74 let now = self.now();
75
76 if timestamp <= now {
77 alarm.timestamp.set(u64::MAX);
78 return false;
79 }
80
81 //time diff in sub-sec not ticks (32kHz)
82 let diff = timestamp - now;
83 let sec = (diff / 32768) as u32;
84 let subsec = (diff % 32768) as u32;
85
86 let current_sec = rtc.count.read().val().bits();
87 let target_sec = current_sec.wrapping_add(sec as u32);
88
89 rtc.match_.write(|w| unsafe { w.matval().bits(target_sec) });
90 rtc.wake.write(|w| unsafe {
91 let ms = (subsec * 1000) / 32768;
92 w.val().bits(ms as u16)
93 });
94 if subsec > 0 {
95 let ms = (subsec * 1000) / 32768;
96 rtc.wake.write(|w| unsafe { w.val().bits(ms as u16) });
97 }
98 rtc.ctrl.modify(|_, w| w.alarm1hz().clear_bit().wake1khz().clear_bit());
99 true
100 }
101
102 fn on_interrupt(&self) {
103 critical_section::with(|cs| {
104 let rtc = unsafe { &*RTC::ptr() };
105 let flags = rtc.ctrl.read();
106 if flags.alarm1hz().bit_is_clear() {
107 rtc.ctrl.modify(|_, w| w.alarm1hz().set_bit());
108 self.trigger_alarm(cs);
109 }
110
111 if flags.wake1khz().bit_is_clear() {
112 rtc.ctrl.modify(|_, w| w.wake1khz().set_bit());
113 self.trigger_alarm(cs);
114 }
115 });
116 }
117
118 fn trigger_alarm(&self, cs: CriticalSection) {
119 let alarm = &self.alarms.borrow(cs);
120 alarm.timestamp.set(u64::MAX);
121 let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
122 if next == u64::MAX {
123 // no scheduled events, skipping
124 return;
125 }
126 while !self.set_alarm(cs, next) {
127 next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
128 if next == u64::MAX {
129 //no next event found after retry
130 return;
131 }
132 }
133 }
134}
135
136impl Driver for RtcDriver {
137 fn now(&self) -> u64 {
138 let rtc = unsafe { &*RTC::ptr() };
139
140 loop {
141 let sec1 = rtc.count.read().val().bits() as u64;
142 let sub1 = rtc.subsec.read().subsec().bits() as u64;
143 let sec2 = rtc.count.read().val().bits() as u64;
144 let sub2 = rtc.subsec.read().subsec().bits() as u64;
145
146 if sec1 == sec2 && sub1 == sub2 {
147 return sec1 * 32768 + sub1;
148 }
149 }
150 }
151
152 fn schedule_wake(&self, at: u64, waker: &Waker) {
153 critical_section::with(|cs| {
154 let mut queue = self.queue.borrow(cs).borrow_mut();
155
156 if queue.schedule_wake(at, waker) {
157 let mut next = queue.next_expiration(self.now());
158 while !self.set_alarm(cs, next) {
159 next = queue.next_expiration(self.now());
160 }
161 }
162 })
163 }
164}
165#[cortex_m_rt::interrupt]
166fn RTC() {
167 DRIVER.on_interrupt();
168}
169
170pub fn init() {
171 DRIVER.init();
172}