diff options
| author | Liam Murphy <[email protected]> | 2021-06-26 17:58:36 +1000 |
|---|---|---|
| committer | Liam Murphy <[email protected]> | 2021-06-26 17:58:36 +1000 |
| commit | 02781ed744b6e76d3790844f898235088b0fd8aa (patch) | |
| tree | 4f5cd29aebc7b54beee6f7e3541fcc583ed9d755 /embassy-nrf/src/timer.rs | |
| parent | e6d6e82e54bca88ab1144802d2b716b867934225 (diff) | |
Add an nRF Timer driver
Resolves #189
Diffstat (limited to 'embassy-nrf/src/timer.rs')
| -rw-r--r-- | embassy-nrf/src/timer.rs | 377 |
1 files changed, 373 insertions, 4 deletions
diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 2490bfd93..3b2678a0b 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs | |||
| @@ -1,15 +1,30 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 3 | use embassy::interrupt::Interrupt; | 6 | use embassy::interrupt::Interrupt; |
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::util::OnDrop; | ||
| 4 | use embassy::util::Unborrow; | 9 | use embassy::util::Unborrow; |
| 10 | use embassy_extras::unborrow; | ||
| 11 | use futures::future::poll_fn; | ||
| 5 | 12 | ||
| 6 | use crate::pac; | 13 | use crate::pac; |
| 14 | use crate::ppi::Event; | ||
| 15 | use crate::ppi::Task; | ||
| 7 | 16 | ||
| 8 | pub(crate) mod sealed { | 17 | pub(crate) mod sealed { |
| 18 | use embassy::util::AtomicWaker; | ||
| 19 | |||
| 9 | use super::*; | 20 | use super::*; |
| 10 | 21 | ||
| 11 | pub trait Instance { | 22 | pub trait Instance { |
| 12 | fn regs(&self) -> &pac::timer0::RegisterBlock; | 23 | /// The number of CC registers this instance has. |
| 24 | const CCS: usize; | ||
| 25 | fn regs() -> &'static pac::timer0::RegisterBlock; | ||
| 26 | /// Storage for the waker for CC register `n`. | ||
| 27 | fn waker(n: usize) -> &'static AtomicWaker; | ||
| 13 | } | 28 | } |
| 14 | pub trait ExtendedInstance {} | 29 | pub trait ExtendedInstance {} |
| 15 | } | 30 | } |
| @@ -20,19 +35,373 @@ pub trait Instance: Unborrow<Target = Self> + sealed::Instance + 'static { | |||
| 20 | pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} | 35 | pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} |
| 21 | 36 | ||
| 22 | macro_rules! impl_timer { | 37 | macro_rules! impl_timer { |
| 23 | ($type:ident, $pac_type:ident, $irq:ident) => { | 38 | ($type:ident, $pac_type:ident, $irq:ident, $ccs:literal) => { |
| 24 | impl crate::timer::sealed::Instance for peripherals::$type { | 39 | impl crate::timer::sealed::Instance for peripherals::$type { |
| 25 | fn regs(&self) -> &pac::timer0::RegisterBlock { | 40 | const CCS: usize = $ccs; |
| 41 | fn regs() -> &'static pac::timer0::RegisterBlock { | ||
| 26 | unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } | 42 | unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } |
| 27 | } | 43 | } |
| 44 | fn waker(n: usize) -> &'static ::embassy::util::AtomicWaker { | ||
| 45 | use ::embassy::util::AtomicWaker; | ||
| 46 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | ||
| 47 | static WAKERS: [AtomicWaker; $ccs] = [NEW_AW; $ccs]; | ||
| 48 | &WAKERS[n] | ||
| 49 | } | ||
| 28 | } | 50 | } |
| 29 | impl crate::timer::Instance for peripherals::$type { | 51 | impl crate::timer::Instance for peripherals::$type { |
| 30 | type Interrupt = crate::interrupt::$irq; | 52 | type Interrupt = crate::interrupt::$irq; |
| 31 | } | 53 | } |
| 32 | }; | 54 | }; |
| 55 | ($type:ident, $pac_type:ident, $irq:ident) => { | ||
| 56 | impl_timer!($type, $pac_type, $irq, 4); | ||
| 57 | }; | ||
| 33 | ($type:ident, $pac_type:ident, $irq:ident, extended) => { | 58 | ($type:ident, $pac_type:ident, $irq:ident, extended) => { |
| 34 | impl_timer!($type, $pac_type, $irq); | 59 | impl_timer!($type, $pac_type, $irq, 6); |
| 35 | impl crate::timer::sealed::ExtendedInstance for peripherals::$type {} | 60 | impl crate::timer::sealed::ExtendedInstance for peripherals::$type {} |
| 36 | impl crate::timer::ExtendedInstance for peripherals::$type {} | 61 | impl crate::timer::ExtendedInstance for peripherals::$type {} |
| 37 | }; | 62 | }; |
| 38 | } | 63 | } |
| 64 | |||
| 65 | #[repr(u8)] | ||
| 66 | pub enum Frequency { | ||
| 67 | // TODO: These variant names are terrible, what should they be? | ||
| 68 | F16MHz = 0, | ||
| 69 | F8MHz = 1, | ||
| 70 | F4MHz = 2, | ||
| 71 | F2MHz = 3, | ||
| 72 | F1MHz = 4, | ||
| 73 | F500kHz = 5, | ||
| 74 | F250kHz = 6, | ||
| 75 | F125kHz = 7, | ||
| 76 | F62500Hz = 8, | ||
| 77 | F31250Hz = 9, | ||
| 78 | } | ||
| 79 | |||
| 80 | /// nRF Timer driver. | ||
| 81 | /// | ||
| 82 | /// The timer has an internal counter, which is incremented for every tick of the timer. | ||
| 83 | /// The counter is 32-bit, so it wraps back to 0 at 4294967296. | ||
| 84 | /// | ||
| 85 | /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter | ||
| 86 | /// or trigger an event when the counter reaches a certain value. | ||
| 87 | pub struct Timer<'d, T: Instance> { | ||
| 88 | phantom: PhantomData<&'d mut T>, | ||
| 89 | } | ||
| 90 | |||
| 91 | impl<'d, T: Instance> Timer<'d, T> { | ||
| 92 | pub fn new( | ||
| 93 | timer: impl Unborrow<Target = T> + 'd, | ||
| 94 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | ||
| 95 | ) -> Self { | ||
| 96 | unborrow!(irq); | ||
| 97 | |||
| 98 | irq.set_handler(Self::on_interrupt); | ||
| 99 | irq.unpend(); | ||
| 100 | irq.enable(); | ||
| 101 | |||
| 102 | Self::new_irqless(timer) | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. | ||
| 106 | /// | ||
| 107 | /// This is used by `Uarte` internally. | ||
| 108 | pub(crate) fn new_irqless(_timer: impl Unborrow<Target = T> + 'd) -> Self { | ||
| 109 | let regs = T::regs(); | ||
| 110 | |||
| 111 | // Set the instance to timer mode. | ||
| 112 | regs.mode.write(|w| w.mode().timer()); | ||
| 113 | |||
| 114 | // Make the counter's max value as high as possible. | ||
| 115 | // TODO: is there a reason someone would want to set this lower? | ||
| 116 | regs.bitmode.write(|w| w.bitmode()._32bit()); | ||
| 117 | |||
| 118 | let this = Self { | ||
| 119 | phantom: PhantomData, | ||
| 120 | }; | ||
| 121 | |||
| 122 | // Initialize the timer as stopped. | ||
| 123 | this.stop(); | ||
| 124 | |||
| 125 | // Initialize the counter at 0. | ||
| 126 | this.clear(); | ||
| 127 | |||
| 128 | // Initialize all the shorts as disabled. | ||
| 129 | for n in 0..T::CCS { | ||
| 130 | let cc = Cc::<T> { | ||
| 131 | n, | ||
| 132 | phantom: PhantomData, | ||
| 133 | }; | ||
| 134 | cc.unshort_compare_clear(); | ||
| 135 | cc.unshort_compare_stop(); | ||
| 136 | } | ||
| 137 | |||
| 138 | this | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Starts the timer. | ||
| 142 | pub fn start(&self) { | ||
| 143 | T::regs().tasks_start.write(|w| w.tasks_start().trigger()) | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Stops the timer. | ||
| 147 | pub fn stop(&self) { | ||
| 148 | T::regs().tasks_stop.write(|w| w.tasks_stop().trigger()) | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Reset the timer's counter to 0. | ||
| 152 | pub fn clear(&self) { | ||
| 153 | T::regs().tasks_clear.write(|w| w.tasks_clear().trigger()) | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Returns the START task, for use with PPI. | ||
| 157 | /// | ||
| 158 | /// When triggered, this task starts the timer. | ||
| 159 | pub fn task_start(&self) -> Task { | ||
| 160 | Task::from_reg(&T::regs().tasks_start) | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Returns the STOP task, for use with PPI. | ||
| 164 | /// | ||
| 165 | /// When triggered, this task stops the timer. | ||
| 166 | pub fn task_stop(&self) -> Task { | ||
| 167 | Task::from_reg(&T::regs().tasks_stop) | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Returns the CLEAR task, for use with PPI. | ||
| 171 | /// | ||
| 172 | /// When triggered, this task resets the timer's counter to 0. | ||
| 173 | pub fn task_clear(&self) -> Task { | ||
| 174 | Task::from_reg(&T::regs().tasks_clear) | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Change the timer's frequency. | ||
| 178 | /// | ||
| 179 | /// This will stop the timer if it isn't already stopped, | ||
| 180 | /// because the timer may exhibit 'unpredictable behaviour' if it's frequency is changed while it's running. | ||
| 181 | pub fn set_frequency(&self, frequency: Frequency) { | ||
| 182 | self.stop(); | ||
| 183 | |||
| 184 | T::regs() | ||
| 185 | .prescaler | ||
| 186 | // SAFETY: `frequency` is a variant of `Frequency`, | ||
| 187 | // whose values are all in the range of 0-9 (the valid range of `prescaler`). | ||
| 188 | .write(|w| unsafe { w.prescaler().bits(frequency as u8) }) | ||
| 189 | } | ||
| 190 | |||
| 191 | fn on_interrupt(_: *mut ()) { | ||
| 192 | let regs = T::regs(); | ||
| 193 | for n in 0..T::CCS { | ||
| 194 | if regs.events_compare[n] | ||
| 195 | .read() | ||
| 196 | .events_compare() | ||
| 197 | .is_generated() | ||
| 198 | { | ||
| 199 | T::waker(n).wake(); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Returns the 0th CC register. | ||
| 205 | pub fn cc0<'a>(&'a self) -> Cc<'a, T> { | ||
| 206 | Cc { | ||
| 207 | n: 0, | ||
| 208 | phantom: PhantomData, | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | /// Returns the 1st CC register. | ||
| 213 | pub fn cc1<'a>(&'a self) -> Cc<'a, T> { | ||
| 214 | Cc { | ||
| 215 | n: 1, | ||
| 216 | phantom: PhantomData, | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Returns the 2nd CC register. | ||
| 221 | pub fn cc2<'a>(&'a self) -> Cc<'a, T> { | ||
| 222 | Cc { | ||
| 223 | n: 2, | ||
| 224 | phantom: PhantomData, | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Returns the 3rd CC register. | ||
| 229 | pub fn cc3<'a>(&'a self) -> Cc<'a, T> { | ||
| 230 | Cc { | ||
| 231 | n: 3, | ||
| 232 | phantom: PhantomData, | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | impl<'d, T: ExtendedInstance> Timer<'d, T> { | ||
| 238 | /// Returns the 4th CC register. | ||
| 239 | pub fn cc4<'a>(&'a self) -> Cc<'a, T> { | ||
| 240 | Cc { | ||
| 241 | n: 4, | ||
| 242 | phantom: PhantomData, | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | /// Returns the 5th CC register. | ||
| 247 | pub fn cc5<'a>(&'a self) -> Cc<'a, T> { | ||
| 248 | Cc { | ||
| 249 | n: 5, | ||
| 250 | phantom: PhantomData, | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | /// A representation of a timer's Capture/Compare (CC) register. | ||
| 256 | /// | ||
| 257 | /// A CC register holds a 32-bit value. | ||
| 258 | /// This is used either to store a capture of the timer's current count, or to specify the value for the timer to compare against. | ||
| 259 | /// | ||
| 260 | /// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register. | ||
| 261 | /// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register | ||
| 262 | pub struct Cc<'a, T: Instance> { | ||
| 263 | n: usize, | ||
| 264 | phantom: PhantomData<&'a mut T>, | ||
| 265 | } | ||
| 266 | |||
| 267 | impl<'a, T: Instance> Cc<'a, T> { | ||
| 268 | /// Get the current value stored in the register. | ||
| 269 | pub fn value(&self) -> u32 { | ||
| 270 | T::regs().cc[self.n].read().cc().bits() | ||
| 271 | } | ||
| 272 | |||
| 273 | /// Set the value stored in the register. | ||
| 274 | /// | ||
| 275 | /// `event_compare` will fire when the timer's counter reaches this value. | ||
| 276 | pub fn set(&self, value: u32) { | ||
| 277 | // SAFETY: there are no invalid values for the CC register. | ||
| 278 | T::regs().cc[self.n].write(|w| unsafe { w.cc().bits(value) }) | ||
| 279 | } | ||
| 280 | |||
| 281 | /// Capture the current value of the timer's counter in this register, and return it. | ||
| 282 | pub fn capture(&self) -> u32 { | ||
| 283 | T::regs().tasks_capture[self.n].write(|w| w.tasks_capture().trigger()); | ||
| 284 | self.value() | ||
| 285 | } | ||
| 286 | |||
| 287 | /// Returns this CC register's CAPTURE task, for use with PPI. | ||
| 288 | /// | ||
| 289 | /// When triggered, this task will capture the current value of the timer's counter in this register. | ||
| 290 | pub fn task_capture(&self) -> Task { | ||
| 291 | Task::from_reg(&T::regs().tasks_capture[self.n]) | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Returns this CC register's COMPARE event, for use with PPI. | ||
| 295 | /// | ||
| 296 | /// This event will fire when the timer's counter reaches the value in this CC register. | ||
| 297 | pub fn event_compare(&self) -> Event { | ||
| 298 | Event::from_reg(&T::regs().events_compare) | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task. | ||
| 302 | /// | ||
| 303 | /// This means that when the COMPARE event is fired, the CLEAR task will be triggered. | ||
| 304 | /// | ||
| 305 | /// So, when the timer's counter reaches the value stored in this register, the timer's counter will be reset to 0. | ||
| 306 | pub fn short_compare_clear(&self) { | ||
| 307 | T::regs().shorts.write(|w| match self.n { | ||
| 308 | 0 => w.compare0_clear().enabled(), | ||
| 309 | 1 => w.compare1_clear().enabled(), | ||
| 310 | 2 => w.compare2_clear().enabled(), | ||
| 311 | 3 => w.compare3_clear().enabled(), | ||
| 312 | 4 => w.compare4_clear().enabled(), | ||
| 313 | 5 => w.compare5_clear().enabled(), | ||
| 314 | _ => unreachable!("a `Cc` cannot be created with `n > 5`"), | ||
| 315 | }) | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Disable the shortcut between this CC register's COMPARE event and the timer's CLEAR task. | ||
| 319 | pub fn unshort_compare_clear(&self) { | ||
| 320 | T::regs().shorts.write(|w| match self.n { | ||
| 321 | 0 => w.compare0_clear().disabled(), | ||
| 322 | 1 => w.compare1_clear().disabled(), | ||
| 323 | 2 => w.compare2_clear().disabled(), | ||
| 324 | 3 => w.compare3_clear().disabled(), | ||
| 325 | 4 => w.compare4_clear().disabled(), | ||
| 326 | 5 => w.compare5_clear().disabled(), | ||
| 327 | _ => unreachable!("a `Cc` cannot be created with `n > 5`"), | ||
| 328 | }) | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Enable the shortcut between this CC register's COMPARE event and the timer's STOP task. | ||
| 332 | /// | ||
| 333 | /// This means that when the COMPARE event is fired, the STOP task will be triggered. | ||
| 334 | /// | ||
| 335 | /// So, when the timer's counter reaches the value stored in this register, the timer will stop counting up. | ||
| 336 | pub fn short_compare_stop(&self) { | ||
| 337 | T::regs().shorts.write(|w| match self.n { | ||
| 338 | 0 => w.compare0_stop().enabled(), | ||
| 339 | 1 => w.compare1_stop().enabled(), | ||
| 340 | 2 => w.compare2_stop().enabled(), | ||
| 341 | 3 => w.compare3_stop().enabled(), | ||
| 342 | 4 => w.compare4_stop().enabled(), | ||
| 343 | 5 => w.compare5_stop().enabled(), | ||
| 344 | _ => unreachable!("a `Cc` cannot be created with `n > 5`"), | ||
| 345 | }) | ||
| 346 | } | ||
| 347 | |||
| 348 | /// Disable the shortcut between this CC register's COMPARE event and the timer's STOP task. | ||
| 349 | pub fn unshort_compare_stop(&self) { | ||
| 350 | T::regs().shorts.write(|w| match self.n { | ||
| 351 | 0 => w.compare0_stop().disabled(), | ||
| 352 | 1 => w.compare1_stop().disabled(), | ||
| 353 | 2 => w.compare2_stop().disabled(), | ||
| 354 | 3 => w.compare3_stop().disabled(), | ||
| 355 | 4 => w.compare4_stop().disabled(), | ||
| 356 | 5 => w.compare5_stop().disabled(), | ||
| 357 | _ => unreachable!("a `Cc` cannot be created with `n > 5`"), | ||
| 358 | }) | ||
| 359 | } | ||
| 360 | |||
| 361 | /// Wait until the timer's counter reaches the value stored in this register. | ||
| 362 | pub async fn wait(&self) { | ||
| 363 | let regs = T::regs(); | ||
| 364 | |||
| 365 | // Enable the interrupt for this CC's COMPARE event. | ||
| 366 | regs.intenset.write(|w| match self.n { | ||
| 367 | 0 => w.compare0().set(), | ||
| 368 | 1 => w.compare1().set(), | ||
| 369 | 2 => w.compare2().set(), | ||
| 370 | 3 => w.compare3().set(), | ||
| 371 | 4 => w.compare4().set(), | ||
| 372 | 5 => w.compare5().set(), | ||
| 373 | _ => unreachable!("a `Cc` cannot be created with `n > 5`"), | ||
| 374 | }); | ||
| 375 | |||
| 376 | // Disable the interrupt if the future is dropped. | ||
| 377 | let on_drop = OnDrop::new(|| { | ||
| 378 | regs.intenclr.write(|w| match self.n { | ||
| 379 | 0 => w.compare0().clear(), | ||
| 380 | 1 => w.compare1().clear(), | ||
| 381 | 2 => w.compare2().clear(), | ||
| 382 | 3 => w.compare3().clear(), | ||
| 383 | 4 => w.compare4().clear(), | ||
| 384 | 5 => w.compare5().clear(), | ||
| 385 | _ => unreachable!("a `Cc` cannot be created with `n > 5`"), | ||
| 386 | }); | ||
| 387 | }); | ||
| 388 | |||
| 389 | poll_fn(|cx| { | ||
| 390 | T::waker(self.n).register(cx.waker()); | ||
| 391 | |||
| 392 | if regs.events_compare[self.n] | ||
| 393 | .read() | ||
| 394 | .events_compare() | ||
| 395 | .is_generated() | ||
| 396 | { | ||
| 397 | Poll::Ready(()) | ||
| 398 | } else { | ||
| 399 | Poll::Pending | ||
| 400 | } | ||
| 401 | }) | ||
| 402 | .await; | ||
| 403 | |||
| 404 | // Trigger the interrupt to be disabled. | ||
| 405 | drop(on_drop); | ||
| 406 | } | ||
| 407 | } | ||
