-
Notifications
You must be signed in to change notification settings - Fork 308
Description
Rationale
Copying from #1318 (comment):
We currently have no shared API between timers, i.e TIMG and SYSTIMER are completely different and cannot be abstracted over, but in reality they all do the same things:
- Count (clock)
- One-shot alarm
- Periodic alarm
If we can abstract over this, I see several benefits:
- esp-wifi can be decoupled from specific timers, and in general it will be possible to write code that can be abstract over timers. Thanks to runtime interrupt binding, we can even create a
InterruptHandler
in esp-wifi and assign it to the chosen timer. - We might be able remove the embassy time features, though this is still unclear it might need some runtime selection because of how
embassy_time_driver::time_driver_impl!
works - De duplicate code between systimer and timg
- Discoverability, and consistency in the timer APIs
Initial design
In a nutshell, I believe the way to go about this is to abstract over the building blocks of timer behavior, like set_target
, clear_interrupt
, enable_interrupt
. After we have that we can then create concrete structures for specific timer behaviors.
The timer trait
This is probably where we need the most input, as it needs to support the use cases we need for all the timers we have. As a starting point this is my definition:
pub trait Timer: crate::private::Sealed {
/// Start the timer.
fn start(&mut self);
// Stop the timer.
fn stop(&mut self);
/// Reset the timer value to 0.
fn reset(&mut self);
/// Is the timer running.
fn is_running(&self) -> bool;
/// The current timer value.
fn now(&self) -> u64;
/// Load a target value into the timer.
fn load_value(&mut self, value: u64);
/// Enable auto reload of the `load`ed value.
fn enable_auto_reload(&mut self, auto_reload: bool);
/// Enable or disable the timer's interrupt.
fn enable_interrupt(&mut self, state: bool);
/// Clear the timer's interrupt.
fn clear_interrupt(&mut self);
/// Has the timer triggered?
fn is_interrupt_set(&self) -> bool;
}
Behaviour encapsulation
To encapsulate one shot timer behaviour we can create a OneShotTimer
that could look like this:
pub struct OneShotTimer<T> {
inner: T
}
impl<T> OneShotTimer<T>
where T: Timer
{
// candidate timers:
// TIMG
// SYSTIMER
// others?
pub fn new(inner: T) -> Self {
Self {
inner
}
}
}
// All trait impls in once place
impl<T> embedded_hal::delay::Delay for OneShotTimer<T>
where T: Timer {
/// ... impl omitted
}
What about the generic parameter?
In the OP I stated that we might be able to remove the embassy features, but if we know we need to store a timer in a static
and it has a generic paramter we're going to have a bad time. I am hoping that for esp-wifi's usecase this won't matter, but regardless, I have an idea: AnyTimer
.
AnyTimer
, much like AnyPin
allows for runtime selection of a timer. Now that the Timer
trait exists, we can impl Timer
for AnyTimer
and boom, we have a single type than can be used for more than one timer.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status