diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-12-08 23:27:32 +0100 |
|---|---|---|
| committer | Dániel Buga <[email protected]> | 2024-12-13 21:20:59 +0100 |
| commit | b268b1795fed58544c166c41842ce0d66328aa3e (patch) | |
| tree | 55b6fb09f6694b5e3355d344770b36bfe1550415 /embassy-nrf | |
| parent | ec96395d084d5edc8be25ddaea8547e2ebd447a6 (diff) | |
Merge time-driver and time-queue-driver traits, make HALs own and handle the queue.
Diffstat (limited to 'embassy-nrf')
| -rw-r--r-- | embassy-nrf/src/time_driver.rs | 115 |
1 files changed, 63 insertions, 52 deletions
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index f8b3c4bbc..a27fae9a8 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use core::cell::Cell; | 1 | use core::cell::{Cell, RefCell}; |
| 2 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | 2 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; |
| 3 | 3 | ||
| 4 | use critical_section::CriticalSection; | 4 | use critical_section::CriticalSection; |
| 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 6 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; | 6 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; |
| 7 | use embassy_time_driver::Driver; | 7 | use embassy_time_driver::Driver; |
| 8 | use embassy_time_queue_driver::GlobalTimerQueue; | 8 | use embassy_time_queue_driver::Queue; |
| 9 | 9 | ||
| 10 | use crate::interrupt::InterruptExt; | 10 | use crate::interrupt::InterruptExt; |
| 11 | use crate::{interrupt, pac}; | 11 | use crate::{interrupt, pac}; |
| @@ -111,11 +111,13 @@ struct RtcDriver { | |||
| 111 | period: AtomicU32, | 111 | period: AtomicU32, |
| 112 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. | 112 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. |
| 113 | alarms: Mutex<AlarmState>, | 113 | alarms: Mutex<AlarmState>, |
| 114 | queue: Mutex<RefCell<Queue>>, | ||
| 114 | } | 115 | } |
| 115 | 116 | ||
| 116 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | 117 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 117 | period: AtomicU32::new(0), | 118 | period: AtomicU32::new(0), |
| 118 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), | 119 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 120 | queue: Mutex::new(RefCell::new(Queue::new())), | ||
| 119 | }); | 121 | }); |
| 120 | 122 | ||
| 121 | impl RtcDriver { | 123 | impl RtcDriver { |
| @@ -194,59 +196,60 @@ impl RtcDriver { | |||
| 194 | alarm.timestamp.set(u64::MAX); | 196 | alarm.timestamp.set(u64::MAX); |
| 195 | 197 | ||
| 196 | // Call after clearing alarm, so the callback can set another alarm. | 198 | // Call after clearing alarm, so the callback can set another alarm. |
| 197 | TIMER_QUEUE_DRIVER.dispatch(); | 199 | let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); |
| 200 | while !self.set_alarm(cs, next) { | ||
| 201 | next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); | ||
| 202 | } | ||
| 198 | } | 203 | } |
| 199 | 204 | ||
| 200 | fn set_alarm(&self, timestamp: u64) -> bool { | 205 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
| 201 | critical_section::with(|cs| { | 206 | let n = 0; |
| 202 | let n = 0; | 207 | let alarm = &self.alarms.borrow(cs); |
| 203 | let alarm = &self.alarms.borrow(cs); | 208 | alarm.timestamp.set(timestamp); |
| 204 | alarm.timestamp.set(timestamp); | ||
| 205 | 209 | ||
| 206 | let r = rtc(); | 210 | let r = rtc(); |
| 207 | 211 | ||
| 208 | let t = self.now(); | 212 | let t = self.now(); |
| 209 | if timestamp <= t { | 213 | if timestamp <= t { |
| 210 | // If alarm timestamp has passed the alarm will not fire. | 214 | // If alarm timestamp has passed the alarm will not fire. |
| 211 | // Disarm the alarm and return `false` to indicate that. | 215 | // Disarm the alarm and return `false` to indicate that. |
| 212 | r.intenclr().write(|w| w.0 = compare_n(n)); | 216 | r.intenclr().write(|w| w.0 = compare_n(n)); |
| 213 | 217 | ||
| 214 | alarm.timestamp.set(u64::MAX); | 218 | alarm.timestamp.set(u64::MAX); |
| 215 | 219 | ||
| 216 | return false; | 220 | return false; |
| 217 | } | 221 | } |
| 218 | 222 | ||
| 219 | // If it hasn't triggered yet, setup it in the compare channel. | 223 | // If it hasn't triggered yet, setup it in the compare channel. |
| 220 | 224 | ||
| 221 | // Write the CC value regardless of whether we're going to enable it now or not. | 225 | // Write the CC value regardless of whether we're going to enable it now or not. |
| 222 | // This way, when we enable it later, the right value is already set. | 226 | // This way, when we enable it later, the right value is already set. |
| 223 | 227 | ||
| 224 | // nrf52 docs say: | 228 | // nrf52 docs say: |
| 225 | // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. | 229 | // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. |
| 226 | // To workaround this, we never write a timestamp smaller than N+3. | 230 | // To workaround this, we never write a timestamp smaller than N+3. |
| 227 | // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. | 231 | // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. |
| 228 | // | 232 | // |
| 229 | // It is impossible for rtc to tick more than once because | 233 | // It is impossible for rtc to tick more than once because |
| 230 | // - this code takes less time than 1 tick | 234 | // - this code takes less time than 1 tick |
| 231 | // - it runs with interrupts disabled so nothing else can preempt it. | 235 | // - it runs with interrupts disabled so nothing else can preempt it. |
| 232 | // | 236 | // |
| 233 | // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed | 237 | // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed |
| 234 | // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, | 238 | // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, |
| 235 | // and we don't do that here. | 239 | // and we don't do that here. |
| 236 | let safe_timestamp = timestamp.max(t + 3); | 240 | let safe_timestamp = timestamp.max(t + 3); |
| 237 | r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); | 241 | r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); |
| 238 | 242 | ||
| 239 | let diff = timestamp - t; | 243 | let diff = timestamp - t; |
| 240 | if diff < 0xc00000 { | 244 | if diff < 0xc00000 { |
| 241 | r.intenset().write(|w| w.0 = compare_n(n)); | 245 | r.intenset().write(|w| w.0 = compare_n(n)); |
| 242 | } else { | 246 | } else { |
| 243 | // If it's too far in the future, don't setup the compare channel yet. | 247 | // If it's too far in the future, don't setup the compare channel yet. |
| 244 | // It will be setup later by `next_period`. | 248 | // It will be setup later by `next_period`. |
| 245 | r.intenclr().write(|w| w.0 = compare_n(n)); | 249 | r.intenclr().write(|w| w.0 = compare_n(n)); |
| 246 | } | 250 | } |
| 247 | 251 | ||
| 248 | true | 252 | true |
| 249 | }) | ||
| 250 | } | 253 | } |
| 251 | } | 254 | } |
| 252 | 255 | ||
| @@ -258,6 +261,19 @@ impl Driver for RtcDriver { | |||
| 258 | let counter = rtc().counter().read().0; | 261 | let counter = rtc().counter().read().0; |
| 259 | calc_now(period, counter) | 262 | calc_now(period, counter) |
| 260 | } | 263 | } |
| 264 | |||
| 265 | fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { | ||
| 266 | critical_section::with(|cs| { | ||
| 267 | let mut queue = self.queue.borrow(cs).borrow_mut(); | ||
| 268 | |||
| 269 | if queue.schedule_wake(at, waker) { | ||
| 270 | let mut next = queue.next_expiration(self.now()); | ||
| 271 | while !self.set_alarm(cs, next) { | ||
| 272 | next = queue.next_expiration(self.now()); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | }) | ||
| 276 | } | ||
| 261 | } | 277 | } |
| 262 | 278 | ||
| 263 | #[cfg(feature = "_nrf54l")] | 279 | #[cfg(feature = "_nrf54l")] |
| @@ -277,8 +293,3 @@ fn RTC1() { | |||
| 277 | pub(crate) fn init(irq_prio: crate::interrupt::Priority) { | 293 | pub(crate) fn init(irq_prio: crate::interrupt::Priority) { |
| 278 | DRIVER.init(irq_prio) | 294 | DRIVER.init(irq_prio) |
| 279 | } | 295 | } |
| 280 | |||
| 281 | embassy_time_queue_driver::timer_queue_impl!( | ||
| 282 | static TIMER_QUEUE_DRIVER: GlobalTimerQueue | ||
| 283 | = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) | ||
| 284 | ); | ||
