294 lines
9.4 KiB
Rust
294 lines
9.4 KiB
Rust
//! Timer driver.
|
|
//!
|
|
//! Important note! This driver is very low level. For most time-related use cases, like
|
|
//! "sleep for X seconds", "do something every X seconds", or measuring time, you should
|
|
//! use [`embassy-time`](https://crates.io/crates/embassy-time) instead!
|
|
|
|
#![macro_use]
|
|
|
|
use embassy_hal_internal::{Peri, PeripheralType};
|
|
|
|
use crate::pac;
|
|
use crate::pac::timer::vals;
|
|
use crate::ppi::{Event, Task};
|
|
|
|
pub(crate) trait SealedInstance {
|
|
/// The number of CC registers this instance has.
|
|
const CCS: usize;
|
|
fn regs() -> pac::timer::Timer;
|
|
}
|
|
|
|
/// Basic Timer instance.
|
|
#[allow(private_bounds)]
|
|
pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
|
|
/// Interrupt for this peripheral.
|
|
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
|
}
|
|
|
|
/// Extended timer instance.
|
|
pub trait ExtendedInstance: Instance {}
|
|
|
|
macro_rules! impl_timer {
|
|
($type:ident, $pac_type:ident, $irq:ident, $ccs:literal) => {
|
|
impl crate::timer::SealedInstance for peripherals::$type {
|
|
const CCS: usize = $ccs;
|
|
fn regs() -> pac::timer::Timer {
|
|
unsafe { pac::timer::Timer::from_ptr(pac::$pac_type.as_ptr()) }
|
|
}
|
|
}
|
|
impl crate::timer::Instance for peripherals::$type {
|
|
type Interrupt = crate::interrupt::typelevel::$irq;
|
|
}
|
|
};
|
|
($type:ident, $pac_type:ident, $irq:ident) => {
|
|
impl_timer!($type, $pac_type, $irq, 4);
|
|
};
|
|
($type:ident, $pac_type:ident, $irq:ident, extended) => {
|
|
impl_timer!($type, $pac_type, $irq, 6);
|
|
impl crate::timer::ExtendedInstance for peripherals::$type {}
|
|
};
|
|
}
|
|
|
|
/// Timer frequency
|
|
#[repr(u8)]
|
|
pub enum Frequency {
|
|
/// 16MHz
|
|
F16MHz = 0,
|
|
/// 8MHz
|
|
F8MHz = 1,
|
|
/// 4MHz
|
|
F4MHz = 2,
|
|
/// 2MHz
|
|
F2MHz = 3,
|
|
/// 1MHz
|
|
F1MHz = 4,
|
|
/// 500kHz
|
|
F500kHz = 5,
|
|
/// 250kHz
|
|
F250kHz = 6,
|
|
/// 125kHz
|
|
F125kHz = 7,
|
|
/// 62500Hz
|
|
F62500Hz = 8,
|
|
/// 31250Hz
|
|
F31250Hz = 9,
|
|
}
|
|
|
|
/// nRF Timer driver.
|
|
///
|
|
/// The timer has an internal counter, which is incremented for every tick of the timer.
|
|
/// The counter is 32-bit, so it wraps back to 0 when it reaches 2^32.
|
|
///
|
|
/// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter
|
|
/// or trigger an event when the counter reaches a certain value.
|
|
|
|
/// Timer driver.
|
|
pub struct Timer<'d, T: Instance> {
|
|
_p: Peri<'d, T>,
|
|
}
|
|
|
|
impl<'d, T: Instance> Timer<'d, T> {
|
|
/// Create a new `Timer` driver.
|
|
///
|
|
/// This can be useful for triggering tasks via PPI
|
|
/// `Uarte` uses this internally.
|
|
pub fn new(timer: Peri<'d, T>) -> Self {
|
|
Self::new_inner(timer, false)
|
|
}
|
|
|
|
/// Create a new `Timer` driver in counter mode.
|
|
///
|
|
/// This can be useful for triggering tasks via PPI
|
|
/// `Uarte` uses this internally.
|
|
pub fn new_counter(timer: Peri<'d, T>) -> Self {
|
|
Self::new_inner(timer, true)
|
|
}
|
|
|
|
fn new_inner(timer: Peri<'d, T>, _is_counter: bool) -> Self {
|
|
let regs = T::regs();
|
|
|
|
let this = Self { _p: timer };
|
|
|
|
// Stop the timer before doing anything else,
|
|
// since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.
|
|
this.stop();
|
|
|
|
regs.mode().write(|w| {
|
|
w.set_mode(match _is_counter {
|
|
#[cfg(not(feature = "_nrf51"))]
|
|
true => vals::Mode::LOW_POWER_COUNTER,
|
|
#[cfg(feature = "_nrf51")]
|
|
true => vals::Mode::COUNTER,
|
|
false => vals::Mode::TIMER,
|
|
})
|
|
});
|
|
|
|
// Make the counter's max value as high as possible.
|
|
// TODO: is there a reason someone would want to set this lower?
|
|
regs.bitmode().write(|w| w.set_bitmode(vals::Bitmode::_32BIT));
|
|
|
|
// Initialize the counter at 0.
|
|
this.clear();
|
|
|
|
// Default to the max frequency of the lower power clock
|
|
this.set_frequency(Frequency::F1MHz);
|
|
|
|
for n in 0..T::CCS {
|
|
let cc = this.cc(n);
|
|
// Initialize all the shorts as disabled.
|
|
cc.unshort_compare_clear();
|
|
cc.unshort_compare_stop();
|
|
// Initialize the CC registers as 0.
|
|
cc.write(0);
|
|
}
|
|
|
|
this
|
|
}
|
|
|
|
/// Starts the timer.
|
|
pub fn start(&self) {
|
|
T::regs().tasks_start().write_value(1)
|
|
}
|
|
|
|
/// Stops the timer.
|
|
pub fn stop(&self) {
|
|
T::regs().tasks_stop().write_value(1)
|
|
}
|
|
|
|
/// Reset the timer's counter to 0.
|
|
pub fn clear(&self) {
|
|
T::regs().tasks_clear().write_value(1)
|
|
}
|
|
|
|
/// Returns the START task, for use with PPI.
|
|
///
|
|
/// When triggered, this task starts the timer.
|
|
pub fn task_start(&self) -> Task<'d> {
|
|
Task::from_reg(T::regs().tasks_start())
|
|
}
|
|
|
|
/// Returns the STOP task, for use with PPI.
|
|
///
|
|
/// When triggered, this task stops the timer.
|
|
pub fn task_stop(&self) -> Task<'d> {
|
|
Task::from_reg(T::regs().tasks_stop())
|
|
}
|
|
|
|
/// Returns the CLEAR task, for use with PPI.
|
|
///
|
|
/// When triggered, this task resets the timer's counter to 0.
|
|
pub fn task_clear(&self) -> Task<'d> {
|
|
Task::from_reg(T::regs().tasks_clear())
|
|
}
|
|
|
|
/// Returns the COUNT task, for use with PPI.
|
|
///
|
|
/// When triggered, this task increments the timer's counter by 1.
|
|
/// Only works in counter mode.
|
|
pub fn task_count(&self) -> Task<'d> {
|
|
Task::from_reg(T::regs().tasks_count())
|
|
}
|
|
|
|
/// Change the timer's frequency.
|
|
///
|
|
/// This will stop the timer if it isn't already stopped,
|
|
/// because the timer may exhibit 'unpredictable behaviour' if it's frequency is changed while it's running.
|
|
pub fn set_frequency(&self, frequency: Frequency) {
|
|
self.stop();
|
|
|
|
T::regs()
|
|
.prescaler()
|
|
// SAFETY: `frequency` is a variant of `Frequency`,
|
|
// whose values are all in the range of 0-9 (the valid range of `prescaler`).
|
|
.write(|w| w.set_prescaler(frequency as u8))
|
|
}
|
|
|
|
/// Returns this timer's `n`th CC register.
|
|
///
|
|
/// # Panics
|
|
/// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer).
|
|
pub fn cc(&self, n: usize) -> Cc<'d, T> {
|
|
if n >= T::CCS {
|
|
panic!("Cannot get CC register {} of timer with {} CC registers.", n, T::CCS);
|
|
}
|
|
Cc {
|
|
n,
|
|
_p: unsafe { self._p.clone_unchecked() },
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A representation of a timer's Capture/Compare (CC) register.
|
|
///
|
|
/// A CC register holds a 32-bit value.
|
|
/// 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.
|
|
///
|
|
/// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register.
|
|
/// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register
|
|
pub struct Cc<'d, T: Instance> {
|
|
n: usize,
|
|
_p: Peri<'d, T>,
|
|
}
|
|
|
|
impl<'d, T: Instance> Cc<'d, T> {
|
|
/// Get the current value stored in the register.
|
|
pub fn read(&self) -> u32 {
|
|
return T::regs().cc(self.n).read();
|
|
}
|
|
|
|
/// Set the value stored in the register.
|
|
///
|
|
/// `event_compare` will fire when the timer's counter reaches this value.
|
|
pub fn write(&self, value: u32) {
|
|
T::regs().cc(self.n).write_value(value);
|
|
}
|
|
|
|
/// Capture the current value of the timer's counter in this register, and return it.
|
|
pub fn capture(&self) -> u32 {
|
|
T::regs().tasks_capture(self.n).write_value(1);
|
|
self.read()
|
|
}
|
|
|
|
/// Returns this CC register's CAPTURE task, for use with PPI.
|
|
///
|
|
/// When triggered, this task will capture the current value of the timer's counter in this register.
|
|
pub fn task_capture(&self) -> Task<'d> {
|
|
Task::from_reg(T::regs().tasks_capture(self.n))
|
|
}
|
|
|
|
/// Returns this CC register's COMPARE event, for use with PPI.
|
|
///
|
|
/// This event will fire when the timer's counter reaches the value in this CC register.
|
|
pub fn event_compare(&self) -> Event<'d> {
|
|
Event::from_reg(T::regs().events_compare(self.n))
|
|
}
|
|
|
|
/// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.
|
|
///
|
|
/// This means that when the COMPARE event is fired, the CLEAR task will be triggered.
|
|
///
|
|
/// So, when the timer's counter reaches the value stored in this register, the timer's counter will be reset to 0.
|
|
pub fn short_compare_clear(&self) {
|
|
T::regs().shorts().modify(|w| w.set_compare_clear(self.n, true))
|
|
}
|
|
|
|
/// Disable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.
|
|
pub fn unshort_compare_clear(&self) {
|
|
T::regs().shorts().modify(|w| w.set_compare_clear(self.n, false))
|
|
}
|
|
|
|
/// Enable the shortcut between this CC register's COMPARE event and the timer's STOP task.
|
|
///
|
|
/// This means that when the COMPARE event is fired, the STOP task will be triggered.
|
|
///
|
|
/// So, when the timer's counter reaches the value stored in this register, the timer will stop counting up.
|
|
pub fn short_compare_stop(&self) {
|
|
T::regs().shorts().modify(|w| w.set_compare_stop(self.n, true))
|
|
}
|
|
|
|
/// Disable the shortcut between this CC register's COMPARE event and the timer's STOP task.
|
|
pub fn unshort_compare_stop(&self) {
|
|
T::regs().shorts().modify(|w| w.set_compare_stop(self.n, false))
|
|
}
|
|
}
|