diff options
| author | Ulf Lilleengen <[email protected]> | 2025-01-01 19:47:39 +0100 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2025-01-01 19:47:39 +0100 |
| commit | e012e1385814647e1c17cb64175a19745c18bb8f (patch) | |
| tree | aaa7abf2a5db062ef899ed85f9e85674e8cb138a | |
| parent | 0733bee926ef117ad99e54194b009d7345051414 (diff) | |
Ensure alarm is re-scheduled if timetamp is in the past
Fixes #3672
| -rw-r--r-- | embassy-nrf/src/time_driver.rs | 80 |
1 files changed, 43 insertions, 37 deletions
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index ade6fd2a1..01079e26a 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs | |||
| @@ -209,47 +209,53 @@ impl RtcDriver { | |||
| 209 | 209 | ||
| 210 | let r = rtc(); | 210 | let r = rtc(); |
| 211 | 211 | ||
| 212 | let t = self.now(); | 212 | loop { |
| 213 | if timestamp <= t { | 213 | let t = self.now(); |
| 214 | // If alarm timestamp has passed the alarm will not fire. | 214 | if timestamp <= t { |
| 215 | // Disarm the alarm and return `false` to indicate that. | 215 | // If alarm timestamp has passed the alarm will not fire. |
| 216 | r.intenclr().write(|w| w.0 = compare_n(n)); | 216 | // Disarm the alarm and return `false` to indicate that. |
| 217 | r.intenclr().write(|w| w.0 = compare_n(n)); | ||
| 217 | 218 | ||
| 218 | alarm.timestamp.set(u64::MAX); | 219 | alarm.timestamp.set(u64::MAX); |
| 219 | 220 | ||
| 220 | return false; | 221 | return false; |
| 221 | } | 222 | } |
| 222 | 223 | ||
| 223 | // If it hasn't triggered yet, setup it in the compare channel. | 224 | // If it hasn't triggered yet, setup it in the compare channel. |
| 224 | 225 | ||
| 225 | // Write the CC value regardless of whether we're going to enable it now or not. | 226 | // Write the CC value regardless of whether we're going to enable it now or not. |
| 226 | // This way, when we enable it later, the right value is already set. | 227 | // This way, when we enable it later, the right value is already set. |
| 227 | 228 | ||
| 228 | // nrf52 docs say: | 229 | // nrf52 docs say: |
| 229 | // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. | 230 | // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. |
| 230 | // To workaround this, we never write a timestamp smaller than N+3. | 231 | // To workaround this, we never write a timestamp smaller than N+3. |
| 231 | // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. | 232 | // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. |
| 232 | // | 233 | // |
| 233 | // It is impossible for rtc to tick more than once because | 234 | // It is impossible for rtc to tick more than once because |
| 234 | // - this code takes less time than 1 tick | 235 | // - this code takes less time than 1 tick |
| 235 | // - it runs with interrupts disabled so nothing else can preempt it. | 236 | // - it runs with interrupts disabled so nothing else can preempt it. |
| 236 | // | 237 | // |
| 237 | // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed | 238 | // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed |
| 238 | // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, | 239 | // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, |
| 239 | // and we don't do that here. | 240 | // and we don't do that here. |
| 240 | let safe_timestamp = timestamp.max(t + 3); | 241 | let safe_timestamp = timestamp.max(t + 3); |
| 241 | r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); | 242 | r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); |
| 242 | 243 | ||
| 243 | let diff = timestamp - t; | 244 | let diff = timestamp - t; |
| 244 | if diff < 0xc00000 { | 245 | if diff < 0xc00000 { |
| 245 | r.intenset().write(|w| w.0 = compare_n(n)); | 246 | r.intenset().write(|w| w.0 = compare_n(n)); |
| 246 | } else { | 247 | } else { |
| 247 | // If it's too far in the future, don't setup the compare channel yet. | 248 | // If it's too far in the future, don't setup the compare channel yet. |
| 248 | // It will be setup later by `next_period`. | 249 | // It will be setup later by `next_period`. |
| 249 | r.intenclr().write(|w| w.0 = compare_n(n)); | 250 | r.intenclr().write(|w| w.0 = compare_n(n)); |
| 250 | } | 251 | } |
| 251 | 252 | ||
| 252 | true | 253 | // If we have not passed the safe timestamp, we can be sure the alarm will be invoked. Otherwise, |
| 254 | // we need to retry setting the alarm. | ||
| 255 | if self.now() <= safe_timestamp { | ||
| 256 | return true; | ||
| 257 | } | ||
| 258 | } | ||
| 253 | } | 259 | } |
| 254 | } | 260 | } |
| 255 | 261 | ||
