mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-01 14:20:44 +00:00
[2/3] Timer refactor: TIMG (#2581)
* system timer simplfication * Removes _all_ type params on Alarm * Systimer no longer implements peripheral ref, the peripheral ref pattern is instead intended to be used on the higher level timer drivers * Removed `Unit` as a type, in favour of an enum * Alarms are back in the main `SystemTimer` "driver" * Made all `Unit` modification methods unsafe, it's not possible to modify the `Unit`'s safely whilst timers and or the `time::now` API is in use * fix examples and tests (by removing them :D) * changelog and migration * Review feedback * changelog and migration * /unit_count/unit_value/g * changelog and migration * /unit_count/unit_value/g * system timer simplfication * Removes _all_ type params on Alarm * Systimer no longer implements peripheral ref, the peripheral ref pattern is instead intended to be used on the higher level timer drivers * Removed `Unit` as a type, in favour of an enum * Alarms are back in the main `SystemTimer` "driver" * Made all `Unit` modification methods unsafe, it's not possible to modify the `Unit`'s safely whilst timers and or the `time::now` API is in use * Make TimerGroup Timer's dumb and untyped * changelog * review * review
This commit is contained in:
parent
e98674e8fa
commit
79ca9d07aa
@ -75,12 +75,7 @@ trait IntoAnyTimer: Into<AnyTimer> {}
|
||||
|
||||
impl IntoAnyTimer for AnyTimer {}
|
||||
|
||||
impl<T, DM> IntoAnyTimer for TimgTimer<T, DM>
|
||||
where
|
||||
DM: esp_hal::Mode,
|
||||
Self: Into<AnyTimer>,
|
||||
{
|
||||
}
|
||||
impl IntoAnyTimer for TimgTimer where Self: Into<AnyTimer> {}
|
||||
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
impl IntoAnyTimer for Alarm where Self: Into<AnyTimer> {}
|
||||
|
@ -21,13 +21,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- DMA channel objects are no longer wrapped in `Channel`. The `Channel` drivers are now managed by DMA enabled peripheral drivers. (#2526)
|
||||
- The `Dpi` driver and `DpiTransfer` now have a `Mode` type parameter. The driver's asyncness is determined by the asyncness of the `Lcd` used to create it. (#2526)
|
||||
- `dma::{Channel, ChannelRx, ChannelTx}::set_priority` for GDMA devices (#2403)
|
||||
- `SystemTimer::set_unit_count` & `SystemTimer::configure_unit` (#2576)
|
||||
- `SystemTimer::set_unit_value` & `SystemTimer::configure_unit` (#2576)
|
||||
|
||||
### Changed
|
||||
|
||||
- `SystemTimer` no longer uses peripheral ref (#2576)
|
||||
- `TIMGX` no longer uses peripheral ref (#2581)
|
||||
- `SystemTimer::now` has been renamed `SystemTimer::unit_value(Unit)` (#2576)
|
||||
- `dma::{Channel, ChannelRx, ChannelTx}::set_priority` for GDMA devices (#2403)
|
||||
- `SystemTimer`s `Alarm`s are now type erased (#2576)
|
||||
- `TimerGroup` `Timer`s are now type erased (#2581)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -116,3 +116,12 @@ let systimer = SystemTimer::new(peripherals.SYSTIMER);
|
||||
+ let mut timer = PeriodicTimer::new(alarm0);
|
||||
+ timer.start(1u64.secs());
|
||||
```
|
||||
|
||||
### TIMG
|
||||
|
||||
Timer group timers have been type erased.
|
||||
|
||||
```diff
|
||||
- timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>
|
||||
+ timg::Timer
|
||||
```
|
||||
|
@ -33,10 +33,7 @@ mod imp {
|
||||
},
|
||||
};
|
||||
#[cfg(any(timg0, timg1))]
|
||||
pub use crate::timer::timg::{
|
||||
Instance as _esp_hal_timer_timg_Instance,
|
||||
TimerGroupInstance as _esp_hal_timer_timg_TimerGroupInstance,
|
||||
};
|
||||
pub use crate::timer::timg::TimerGroupInstance as _esp_hal_timer_timg_TimerGroupInstance;
|
||||
#[cfg(any(systimer, timg0, timg1))]
|
||||
pub use crate::timer::Timer as _esp_hal_timer_Timer;
|
||||
pub use crate::{clock::CpuClock, entry, macros::*, InterruptConfigurable};
|
||||
|
@ -45,7 +45,6 @@ use fugit::{ExtU64, Instant, MicrosDurationU64};
|
||||
use crate::{
|
||||
interrupt::InterruptHandler,
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
Blocking,
|
||||
InterruptConfigurable,
|
||||
};
|
||||
|
||||
@ -360,16 +359,7 @@ impl<T> embedded_hal_02::timer::Periodic for PeriodicTimer<'_, T> where T: Timer
|
||||
/// An enum of all timer types
|
||||
enum AnyTimerInner {
|
||||
/// Timer 0 of the TIMG0 peripheral in blocking mode.
|
||||
Timg0Timer0(timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>),
|
||||
/// Timer 1 of the TIMG0 peripheral in blocking mode.
|
||||
#[cfg(timg_timer1)]
|
||||
Timg0Timer1(timg::Timer<timg::Timer1<crate::peripherals::TIMG0>, Blocking>),
|
||||
/// Timer 0 of the TIMG1 peripheral in blocking mode.
|
||||
#[cfg(timg1)]
|
||||
Timg1Timer0(timg::Timer<timg::Timer0<crate::peripherals::TIMG1>, Blocking>),
|
||||
/// Timer 1 of the TIMG1 peripheral in blocking mode.
|
||||
#[cfg(all(timg1, timg_timer1))]
|
||||
Timg1Timer1(timg::Timer<timg::Timer1<crate::peripherals::TIMG1>, Blocking>),
|
||||
TimgTimer(timg::Timer),
|
||||
/// Systimer Alarm
|
||||
#[cfg(systimer)]
|
||||
SystimerAlarm(systimer::Alarm),
|
||||
@ -382,30 +372,9 @@ pub struct AnyTimer(AnyTimerInner);
|
||||
|
||||
impl crate::private::Sealed for AnyTimer {}
|
||||
|
||||
impl From<timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>> for AnyTimer {
|
||||
fn from(value: timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>) -> Self {
|
||||
Self(AnyTimerInner::Timg0Timer0(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(timg_timer1)]
|
||||
impl From<timg::Timer<timg::Timer1<crate::peripherals::TIMG0>, Blocking>> for AnyTimer {
|
||||
fn from(value: timg::Timer<timg::Timer1<crate::peripherals::TIMG0>, Blocking>) -> Self {
|
||||
Self(AnyTimerInner::Timg0Timer1(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(timg1)]
|
||||
impl From<timg::Timer<timg::Timer0<crate::peripherals::TIMG1>, Blocking>> for AnyTimer {
|
||||
fn from(value: timg::Timer<timg::Timer0<crate::peripherals::TIMG1>, Blocking>) -> Self {
|
||||
Self(AnyTimerInner::Timg1Timer0(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(timg1, timg_timer1))]
|
||||
impl From<timg::Timer<timg::Timer1<crate::peripherals::TIMG1>, Blocking>> for AnyTimer {
|
||||
fn from(value: timg::Timer<timg::Timer1<crate::peripherals::TIMG1>, Blocking>) -> Self {
|
||||
Self(AnyTimerInner::Timg1Timer1(value))
|
||||
impl From<timg::Timer> for AnyTimer {
|
||||
fn from(value: timg::Timer) -> Self {
|
||||
Self(AnyTimerInner::TimgTimer(value))
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,13 +388,7 @@ impl From<systimer::Alarm> for AnyTimer {
|
||||
impl Timer for AnyTimer {
|
||||
delegate::delegate! {
|
||||
to match &self.0 {
|
||||
AnyTimerInner::Timg0Timer0(inner) => inner,
|
||||
#[cfg(timg_timer1)]
|
||||
AnyTimerInner::Timg0Timer1(inner) => inner,
|
||||
#[cfg(timg1)]
|
||||
AnyTimerInner::Timg1Timer0(inner) => inner,
|
||||
#[cfg(all(timg1,timg_timer1))]
|
||||
AnyTimerInner::Timg1Timer1(inner) => inner,
|
||||
AnyTimerInner::TimgTimer(inner) => inner,
|
||||
#[cfg(systimer)]
|
||||
AnyTimerInner::SystimerAlarm(inner) => inner,
|
||||
} {
|
||||
|
@ -64,10 +64,7 @@
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use fugit::{HertzU32, Instant, MicrosDurationU64};
|
||||
|
||||
@ -77,15 +74,12 @@ use crate::soc::constants::TIMG_DEFAULT_CLK_SRC;
|
||||
use crate::{
|
||||
clock::Clocks,
|
||||
interrupt::{self, InterruptHandler},
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
peripheral::Peripheral,
|
||||
peripherals::{timg0::RegisterBlock, Interrupt, TIMG0},
|
||||
private::Sealed,
|
||||
sync::{lock, Lock},
|
||||
system::PeripheralClockControl,
|
||||
Async,
|
||||
Blocking,
|
||||
InterruptConfigurable,
|
||||
Mode,
|
||||
};
|
||||
|
||||
const NUM_TIMG: usize = 1 + cfg!(timg1) as usize;
|
||||
@ -96,17 +90,16 @@ static INT_ENA_LOCK: [Lock; NUM_TIMG] = [const { Lock::new() }; NUM_TIMG];
|
||||
#[cfg_attr(not(timg_timer1), doc = "a general purpose timer")]
|
||||
#[cfg_attr(timg_timer1, doc = "2 timers")]
|
||||
/// and a watchdog timer.
|
||||
pub struct TimerGroup<'d, T, DM>
|
||||
pub struct TimerGroup<T>
|
||||
where
|
||||
T: TimerGroupInstance,
|
||||
DM: Mode,
|
||||
{
|
||||
_timer_group: PeripheralRef<'d, T>,
|
||||
_timer_group: PhantomData<T>,
|
||||
/// Timer 0
|
||||
pub timer0: Timer<Timer0<T>, DM>,
|
||||
pub timer0: Timer,
|
||||
/// Timer 1
|
||||
#[cfg(timg_timer1)]
|
||||
pub timer1: Timer<Timer1<T>, DM>,
|
||||
pub timer1: Timer,
|
||||
/// Watchdog timer
|
||||
pub wdt: Wdt<T>,
|
||||
}
|
||||
@ -240,283 +233,69 @@ impl TimerGroupInstance for crate::peripherals::TIMG1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T, DM> TimerGroup<'d, T, DM>
|
||||
impl<T> TimerGroup<T>
|
||||
where
|
||||
T: TimerGroupInstance,
|
||||
DM: Mode,
|
||||
{
|
||||
/// Construct a new instance of [`TimerGroup`] in blocking mode
|
||||
pub fn new_inner(_timer_group: impl Peripheral<P = T> + 'd) -> Self {
|
||||
crate::into_ref!(_timer_group);
|
||||
|
||||
pub fn new(_timer_group: T) -> Self {
|
||||
T::reset_peripheral();
|
||||
T::enable_peripheral();
|
||||
|
||||
T::configure_src_clk();
|
||||
|
||||
let clocks = Clocks::get();
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32h2)] {
|
||||
// ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK
|
||||
let apb_clk_freq = clocks.pll_48m_clock;
|
||||
} else {
|
||||
let apb_clk_freq = clocks.apb_clock;
|
||||
}
|
||||
}
|
||||
|
||||
let timer0 = Timer::new(
|
||||
Timer0 {
|
||||
phantom: PhantomData,
|
||||
},
|
||||
apb_clk_freq,
|
||||
);
|
||||
|
||||
#[cfg(timg_timer1)]
|
||||
let timer1 = Timer::new(
|
||||
Timer1 {
|
||||
phantom: PhantomData,
|
||||
},
|
||||
apb_clk_freq,
|
||||
);
|
||||
|
||||
Self {
|
||||
_timer_group,
|
||||
timer0,
|
||||
_timer_group: PhantomData,
|
||||
timer0: Timer {
|
||||
timer: 0,
|
||||
tg: T::id(),
|
||||
register_block: T::register_block(),
|
||||
},
|
||||
#[cfg(timg_timer1)]
|
||||
timer1,
|
||||
timer1: Timer {
|
||||
timer: 1,
|
||||
tg: T::id(),
|
||||
register_block: T::register_block(),
|
||||
},
|
||||
wdt: Wdt::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T> TimerGroup<'d, T, Blocking>
|
||||
where
|
||||
T: TimerGroupInstance,
|
||||
{
|
||||
/// Construct a new instance of [`TimerGroup`] in blocking mode
|
||||
pub fn new(_timer_group: impl Peripheral<P = T> + 'd) -> Self {
|
||||
Self::new_inner(_timer_group)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T> TimerGroup<'d, T, Async>
|
||||
where
|
||||
T: TimerGroupInstance,
|
||||
{
|
||||
/// Construct a new instance of [`TimerGroup`] in asynchronous mode
|
||||
pub fn new_async(_timer_group: impl Peripheral<P = T> + 'd) -> Self {
|
||||
match T::id() {
|
||||
0 => {
|
||||
use crate::timer::timg::asynch::timg0_timer0_handler;
|
||||
unsafe {
|
||||
interrupt::bind_interrupt(
|
||||
Interrupt::TG0_T0_LEVEL,
|
||||
timg0_timer0_handler.handler(),
|
||||
);
|
||||
interrupt::enable(Interrupt::TG0_T0_LEVEL, timg0_timer0_handler.priority())
|
||||
.unwrap();
|
||||
|
||||
#[cfg(timg_timer1)]
|
||||
{
|
||||
use crate::timer::timg::asynch::timg0_timer1_handler;
|
||||
|
||||
interrupt::bind_interrupt(
|
||||
Interrupt::TG0_T1_LEVEL,
|
||||
timg0_timer1_handler.handler(),
|
||||
);
|
||||
interrupt::enable(Interrupt::TG0_T1_LEVEL, timg0_timer1_handler.priority())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(timg1)]
|
||||
1 => {
|
||||
use crate::timer::timg::asynch::timg1_timer0_handler;
|
||||
unsafe {
|
||||
{
|
||||
interrupt::bind_interrupt(
|
||||
Interrupt::TG1_T0_LEVEL,
|
||||
timg1_timer0_handler.handler(),
|
||||
);
|
||||
interrupt::enable(Interrupt::TG1_T0_LEVEL, timg1_timer0_handler.priority())
|
||||
.unwrap();
|
||||
}
|
||||
#[cfg(timg_timer1)]
|
||||
{
|
||||
use crate::timer::timg::asynch::timg1_timer1_handler;
|
||||
interrupt::bind_interrupt(
|
||||
Interrupt::TG1_T1_LEVEL,
|
||||
timg1_timer1_handler.handler(),
|
||||
);
|
||||
interrupt::enable(Interrupt::TG1_T1_LEVEL, timg1_timer1_handler.priority())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
Self::new_inner(_timer_group)
|
||||
}
|
||||
}
|
||||
|
||||
/// General-purpose timer.
|
||||
pub struct Timer<T, DM>
|
||||
where
|
||||
DM: Mode,
|
||||
{
|
||||
timg: T,
|
||||
apb_clk_freq: HertzU32,
|
||||
phantom: PhantomData<DM>,
|
||||
}
|
||||
|
||||
impl<T, DM> Timer<T, DM>
|
||||
where
|
||||
T: Instance,
|
||||
DM: Mode,
|
||||
{
|
||||
/// Construct a new instance of [`Timer`]
|
||||
pub fn new(timg: T, apb_clk_freq: HertzU32) -> Self {
|
||||
timg.set_counter_active(true);
|
||||
|
||||
Self {
|
||||
timg,
|
||||
apb_clk_freq,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the timer has elapsed
|
||||
pub fn has_elapsed(&mut self) -> bool {
|
||||
if !self.timg.is_counter_active() {
|
||||
panic!("Called wait on an inactive timer!")
|
||||
}
|
||||
|
||||
if self.timg.is_interrupt_set() {
|
||||
self.timg.clear_interrupt();
|
||||
self.timg.set_alarm_active(true);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Block until the timer has elapsed.
|
||||
pub fn wait(&mut self) {
|
||||
while !self.has_elapsed() {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DM> Deref for Timer<T, DM>
|
||||
where
|
||||
T: Instance,
|
||||
DM: Mode,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.timg
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DM> DerefMut for Timer<T, DM>
|
||||
where
|
||||
T: Instance,
|
||||
DM: Mode,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.timg
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DM> Sealed for Timer<T, DM>
|
||||
where
|
||||
T: Instance,
|
||||
DM: Mode,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, DM> super::Timer for Timer<T, DM>
|
||||
where
|
||||
T: Instance,
|
||||
DM: Mode,
|
||||
{
|
||||
impl super::Timer for Timer {
|
||||
fn start(&self) {
|
||||
self.timg.set_counter_active(false);
|
||||
self.timg.set_alarm_active(false);
|
||||
self.set_counter_active(false);
|
||||
self.set_alarm_active(false);
|
||||
|
||||
self.timg.reset_counter();
|
||||
self.timg.set_counter_decrementing(false);
|
||||
self.reset_counter();
|
||||
self.set_counter_decrementing(false);
|
||||
|
||||
self.timg.set_counter_active(true);
|
||||
self.timg.set_alarm_active(true);
|
||||
self.set_counter_active(true);
|
||||
self.set_alarm_active(true);
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
self.timg.set_counter_active(false);
|
||||
self.set_counter_active(false);
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
let t = self.register_block().t(self.timer_number().into());
|
||||
|
||||
t.loadlo().write(|w| unsafe { w.load_lo().bits(0) });
|
||||
t.loadhi().write(|w| unsafe { w.load_hi().bits(0) });
|
||||
|
||||
t.load().write(|w| unsafe { w.load().bits(1) });
|
||||
self.reset_counter()
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.register_block()
|
||||
.t(self.timer_number().into())
|
||||
.config()
|
||||
.read()
|
||||
.en()
|
||||
.bit_is_set()
|
||||
self.is_counter_active()
|
||||
}
|
||||
|
||||
fn now(&self) -> Instant<u64, 1, 1_000_000> {
|
||||
let t = self.register_block().t(self.timer_number().into());
|
||||
|
||||
t.update().write(|w| w.update().set_bit());
|
||||
while t.update().read().update().bit_is_set() {
|
||||
// Wait for the update to complete
|
||||
}
|
||||
|
||||
let value_lo = t.lo().read().bits() as u64;
|
||||
let value_hi = t.hi().read().bits() as u64;
|
||||
|
||||
let ticks = (value_hi << 32) | value_lo;
|
||||
let micros = ticks_to_timeout(ticks, self.apb_clk_freq, self.timg.divider());
|
||||
|
||||
Instant::<u64, 1, 1_000_000>::from_ticks(micros)
|
||||
self.now()
|
||||
}
|
||||
|
||||
fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> {
|
||||
let ticks = timeout_to_ticks(value, self.apb_clk_freq, self.timg.divider());
|
||||
|
||||
// The counter is 54-bits wide, so we must ensure that the provided
|
||||
// value is not too wide:
|
||||
if (ticks & !0x3F_FFFF_FFFF_FFFF) != 0 {
|
||||
return Err(Error::InvalidTimeout);
|
||||
}
|
||||
|
||||
let high = (ticks >> 32) as u32;
|
||||
let low = (ticks & 0xFFFF_FFFF) as u32;
|
||||
|
||||
let t = self.register_block().t(self.timer_number().into());
|
||||
|
||||
t.alarmlo().write(|w| unsafe { w.alarm_lo().bits(low) });
|
||||
t.alarmhi().write(|w| unsafe { w.alarm_hi().bits(high) });
|
||||
|
||||
Ok(())
|
||||
self.load_value(value)
|
||||
}
|
||||
|
||||
fn enable_auto_reload(&self, auto_reload: bool) {
|
||||
self.register_block()
|
||||
.t(self.timer_number().into())
|
||||
.config()
|
||||
.modify(|_, w| w.autoreload().bit(auto_reload));
|
||||
self.set_auto_reload(auto_reload)
|
||||
}
|
||||
|
||||
fn enable_interrupt(&self, state: bool) {
|
||||
@ -535,9 +314,7 @@ where
|
||||
}
|
||||
|
||||
fn clear_interrupt(&self) {
|
||||
self.register_block()
|
||||
.int_clr()
|
||||
.write(|w| w.t(self.timer_number()).clear_bit_by_one());
|
||||
self.clear_interrupt()
|
||||
}
|
||||
|
||||
fn set_interrupt_handler(&self, handler: InterruptHandler) {
|
||||
@ -560,35 +337,15 @@ where
|
||||
}
|
||||
|
||||
fn is_interrupt_set(&self) -> bool {
|
||||
self.register_block()
|
||||
.int_raw()
|
||||
.read()
|
||||
.t(self.timer_number())
|
||||
.bit_is_set()
|
||||
self.is_interrupt_set()
|
||||
}
|
||||
|
||||
fn set_alarm_active(&self, state: bool) {
|
||||
self.register_block()
|
||||
.t(self.timer_number().into())
|
||||
.config()
|
||||
.modify(|_, w| w.alarm_en().bit(state));
|
||||
self.set_alarm_active(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> InterruptConfigurable for Timer<T, Blocking>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn set_interrupt_handler(&mut self, handler: interrupt::InterruptHandler) {
|
||||
<Self as super::Timer>::set_interrupt_handler(self, handler);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DM> Peripheral for Timer<T, DM>
|
||||
where
|
||||
T: Instance,
|
||||
DM: Mode,
|
||||
{
|
||||
impl Peripheral for Timer {
|
||||
type P = Self;
|
||||
|
||||
#[inline]
|
||||
@ -597,92 +354,41 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Instance: Sealed {
|
||||
fn register_block(&self) -> &RegisterBlock;
|
||||
|
||||
fn timer_group(&self) -> u8;
|
||||
|
||||
fn timer_number(&self) -> u8;
|
||||
|
||||
fn reset_counter(&self);
|
||||
|
||||
fn set_counter_active(&self, state: bool);
|
||||
|
||||
fn is_counter_active(&self) -> bool;
|
||||
|
||||
fn set_counter_decrementing(&self, decrementing: bool);
|
||||
|
||||
fn set_auto_reload(&self, auto_reload: bool);
|
||||
|
||||
fn set_alarm_active(&self, state: bool);
|
||||
|
||||
fn is_alarm_active(&self) -> bool;
|
||||
|
||||
fn load_alarm_value(&self, value: u64);
|
||||
|
||||
fn listen(&self);
|
||||
|
||||
fn unlisten(&self);
|
||||
|
||||
fn clear_interrupt(&self);
|
||||
|
||||
fn now(&self) -> u64;
|
||||
|
||||
fn divider(&self) -> u32;
|
||||
|
||||
fn set_divider(&self, divider: u16);
|
||||
|
||||
fn is_interrupt_set(&self) -> bool;
|
||||
}
|
||||
|
||||
/// A timer within a Timer Group.
|
||||
pub struct TimerX<TG, const T: u8 = 0> {
|
||||
phantom: PhantomData<TG>,
|
||||
pub struct Timer {
|
||||
/// Pointer to the register block for this TimerGroup instance.
|
||||
pub register_block: *const RegisterBlock,
|
||||
|
||||
/// The timer number inside the TimerGroup
|
||||
pub timer: u8,
|
||||
|
||||
/// The TimerGroup number
|
||||
pub tg: u8,
|
||||
}
|
||||
|
||||
impl<TG, const T: u8> Sealed for TimerX<TG, T> {}
|
||||
|
||||
impl<TG, const T: u8> TimerX<TG, T>
|
||||
where
|
||||
TG: TimerGroupInstance,
|
||||
{
|
||||
/// Unsafely create an instance of this peripheral out of thin air.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you're only using one instance of this type at a
|
||||
/// time.
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn t() -> &'static crate::peripherals::timg0::T {
|
||||
(*TG::register_block()).t(T as usize)
|
||||
}
|
||||
}
|
||||
impl Sealed for Timer {}
|
||||
unsafe impl Send for Timer {}
|
||||
|
||||
/// Timer peripheral instance
|
||||
impl<TG, const T: u8> Instance for TimerX<TG, T>
|
||||
where
|
||||
TG: TimerGroupInstance,
|
||||
{
|
||||
impl Timer {
|
||||
fn register_block(&self) -> &RegisterBlock {
|
||||
unsafe { &*TG::register_block() }
|
||||
unsafe { &*self.register_block }
|
||||
}
|
||||
|
||||
fn timer_group(&self) -> u8 {
|
||||
TG::id()
|
||||
self.tg
|
||||
}
|
||||
|
||||
fn timer_number(&self) -> u8 {
|
||||
T
|
||||
self.timer
|
||||
}
|
||||
|
||||
fn t(&self) -> &crate::peripherals::timg0::T {
|
||||
self.register_block().t(self.timer_number().into())
|
||||
}
|
||||
|
||||
fn reset_counter(&self) {
|
||||
let t = unsafe { Self::t() };
|
||||
let t = self.t();
|
||||
|
||||
t.loadlo().write(|w| unsafe { w.load_lo().bits(0) });
|
||||
t.loadhi().write(|w| unsafe { w.load_hi().bits(0) });
|
||||
@ -691,91 +397,74 @@ where
|
||||
}
|
||||
|
||||
fn set_counter_active(&self, state: bool) {
|
||||
unsafe { Self::t() }
|
||||
.config()
|
||||
.modify(|_, w| w.en().bit(state));
|
||||
self.t().config().modify(|_, w| w.en().bit(state));
|
||||
}
|
||||
|
||||
fn is_counter_active(&self) -> bool {
|
||||
unsafe { Self::t() }.config().read().en().bit_is_set()
|
||||
self.t().config().read().en().bit_is_set()
|
||||
}
|
||||
|
||||
fn set_counter_decrementing(&self, decrementing: bool) {
|
||||
unsafe { Self::t() }
|
||||
self.t()
|
||||
.config()
|
||||
.modify(|_, w| w.increase().bit(!decrementing));
|
||||
}
|
||||
|
||||
fn set_auto_reload(&self, auto_reload: bool) {
|
||||
unsafe { Self::t() }
|
||||
self.t()
|
||||
.config()
|
||||
.modify(|_, w| w.autoreload().bit(auto_reload));
|
||||
}
|
||||
|
||||
fn set_alarm_active(&self, state: bool) {
|
||||
unsafe { Self::t() }
|
||||
.config()
|
||||
.modify(|_, w| w.alarm_en().bit(state));
|
||||
self.t().config().modify(|_, w| w.alarm_en().bit(state));
|
||||
}
|
||||
|
||||
fn is_alarm_active(&self) -> bool {
|
||||
unsafe { Self::t() }.config().read().alarm_en().bit_is_set()
|
||||
}
|
||||
fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> {
|
||||
let ticks = timeout_to_ticks(value, Clocks::get().apb_clock, self.divider());
|
||||
|
||||
fn load_alarm_value(&self, value: u64) {
|
||||
let value = value & 0x3F_FFFF_FFFF_FFFF;
|
||||
let high = (value >> 32) as u32;
|
||||
let low = (value & 0xFFFF_FFFF) as u32;
|
||||
// The counter is 54-bits wide, so we must ensure that the provided
|
||||
// value is not too wide:
|
||||
if (ticks & !0x3F_FFFF_FFFF_FFFF) != 0 {
|
||||
return Err(Error::InvalidTimeout);
|
||||
}
|
||||
|
||||
let t = unsafe { Self::t() };
|
||||
let high = (ticks >> 32) as u32;
|
||||
let low = (ticks & 0xFFFF_FFFF) as u32;
|
||||
|
||||
let t = self.t();
|
||||
|
||||
t.alarmlo().write(|w| unsafe { w.alarm_lo().bits(low) });
|
||||
|
||||
t.alarmhi().write(|w| unsafe { w.alarm_hi().bits(high) });
|
||||
}
|
||||
|
||||
fn listen(&self) {
|
||||
// always use level interrupt
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
unsafe { Self::t() }
|
||||
.config()
|
||||
.modify(|_, w| w.level_int_en().set_bit());
|
||||
|
||||
lock(&INT_ENA_LOCK[self.timer_group() as usize], || {
|
||||
self.register_block()
|
||||
.int_ena()
|
||||
.modify(|_, w| w.t(T).set_bit());
|
||||
});
|
||||
}
|
||||
|
||||
fn unlisten(&self) {
|
||||
lock(&INT_ENA_LOCK[self.timer_group() as usize], || {
|
||||
self.register_block()
|
||||
.int_ena()
|
||||
.modify(|_, w| w.t(T).clear_bit());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_interrupt(&self) {
|
||||
self.register_block()
|
||||
.int_clr()
|
||||
.write(|w| w.t(T).clear_bit_by_one());
|
||||
.write(|w| w.t(self.timer).clear_bit_by_one());
|
||||
}
|
||||
|
||||
fn now(&self) -> u64 {
|
||||
let t = unsafe { Self::t() };
|
||||
fn now(&self) -> Instant<u64, 1, 1_000_000> {
|
||||
let t = self.t();
|
||||
|
||||
t.update().write(|w| w.update().set_bit());
|
||||
while t.update().read().update().bit_is_set() {}
|
||||
while t.update().read().update().bit_is_set() {
|
||||
// Wait for the update to complete
|
||||
}
|
||||
|
||||
let value_lo = t.lo().read().bits() as u64;
|
||||
let value_hi = (t.hi().read().bits() as u64) << 32;
|
||||
let value_hi = t.hi().read().bits() as u64;
|
||||
|
||||
value_lo | value_hi
|
||||
let ticks = (value_hi << 32) | value_lo;
|
||||
let micros = ticks_to_timeout(ticks, Clocks::get().apb_clock, self.divider());
|
||||
|
||||
Instant::<u64, 1, 1_000_000>::from_ticks(micros)
|
||||
}
|
||||
|
||||
fn divider(&self) -> u32 {
|
||||
let t = unsafe { Self::t() };
|
||||
let t = self.t();
|
||||
|
||||
// From the ESP32 TRM, "11.2.1 16-bit Prescaler and Clock Selection":
|
||||
//
|
||||
@ -791,23 +480,14 @@ where
|
||||
}
|
||||
|
||||
fn is_interrupt_set(&self) -> bool {
|
||||
self.register_block().int_raw().read().t(T).bit_is_set()
|
||||
}
|
||||
|
||||
fn set_divider(&self, divider: u16) {
|
||||
unsafe { Self::t() }
|
||||
.config()
|
||||
.modify(|_, w| unsafe { w.divider().bits(divider) });
|
||||
self.register_block()
|
||||
.int_raw()
|
||||
.read()
|
||||
.t(self.timer)
|
||||
.bit_is_set()
|
||||
}
|
||||
}
|
||||
|
||||
/// Timer 0 in the Timer Group.
|
||||
pub type Timer0<TG> = TimerX<TG, 0>;
|
||||
|
||||
/// Timer 1 in the Timer Group.
|
||||
#[cfg(timg_timer1)]
|
||||
pub type Timer1<TG> = TimerX<TG, 1>;
|
||||
|
||||
fn ticks_to_timeout<F>(ticks: u64, clock: F, divider: u32) -> u64
|
||||
where
|
||||
F: Into<HertzU32>,
|
||||
@ -836,57 +516,6 @@ where
|
||||
(1_000_000 * micros / period as u64) as u64
|
||||
}
|
||||
|
||||
impl<T, DM> embedded_hal_02::timer::CountDown for Timer<T, DM>
|
||||
where
|
||||
T: Instance + super::Timer,
|
||||
DM: Mode,
|
||||
{
|
||||
type Time = MicrosDurationU64;
|
||||
|
||||
fn start<Time>(&mut self, timeout: Time)
|
||||
where
|
||||
Time: Into<Self::Time>,
|
||||
{
|
||||
self.timg.load_value(timeout.into()).unwrap();
|
||||
self.timg.start();
|
||||
}
|
||||
|
||||
fn wait(&mut self) -> nb::Result<(), void::Void> {
|
||||
if self.has_elapsed() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DM> embedded_hal_02::timer::Cancel for Timer<T, DM>
|
||||
where
|
||||
T: Instance + super::Timer,
|
||||
DM: Mode,
|
||||
{
|
||||
type Error = super::Error;
|
||||
|
||||
fn cancel(&mut self) -> Result<(), super::Error> {
|
||||
if !self.timg.is_counter_active() {
|
||||
return Err(super::Error::TimerInactive);
|
||||
} else if !self.timg.is_alarm_active() {
|
||||
return Err(super::Error::AlarmInactive);
|
||||
}
|
||||
|
||||
self.timg.set_counter_active(false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DM> embedded_hal_02::timer::Periodic for Timer<T, DM>
|
||||
where
|
||||
T: Instance + super::Timer,
|
||||
DM: Mode,
|
||||
{
|
||||
}
|
||||
|
||||
/// Behavior of the MWDT stage if it times out.
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -1153,6 +782,7 @@ where
|
||||
|
||||
// Async functionality of the timer groups.
|
||||
mod asynch {
|
||||
#![allow(unused)] // FIXME(mabez)
|
||||
use core::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
@ -1175,18 +805,12 @@ mod asynch {
|
||||
|
||||
static WAKERS: [AtomicWaker; NUM_WAKERS] = [const { AtomicWaker::new() }; NUM_WAKERS];
|
||||
|
||||
pub(crate) struct TimerFuture<'a, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
timer: &'a Timer<T, crate::Async>,
|
||||
pub(crate) struct TimerFuture<'a> {
|
||||
timer: &'a Timer,
|
||||
}
|
||||
|
||||
impl<'a, T> TimerFuture<'a, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
pub(crate) fn new(timer: &'a Timer<T, crate::Async>) -> Self {
|
||||
impl<'a> TimerFuture<'a> {
|
||||
pub(crate) fn new(timer: &'a Timer) -> Self {
|
||||
use crate::timer::Timer;
|
||||
|
||||
timer.enable_interrupt(true);
|
||||
@ -1204,10 +828,7 @@ mod asynch {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::future::Future for TimerFuture<'_, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
impl core::future::Future for TimerFuture<'_> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
@ -1222,35 +843,16 @@ mod asynch {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for TimerFuture<'_, T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
impl Drop for TimerFuture<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.timer.clear_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> embedded_hal_async::delay::DelayNs for Timer<T, crate::Async>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
async fn delay_ns(&mut self, ns: u32) {
|
||||
use crate::timer::Timer as _;
|
||||
|
||||
let period = MicrosDurationU64::from_ticks(ns.div_ceil(1000) as u64);
|
||||
self.load_value(period).unwrap();
|
||||
self.start();
|
||||
self.listen();
|
||||
|
||||
TimerFuture::new(self).await;
|
||||
}
|
||||
}
|
||||
|
||||
// INT_ENA means that when the interrupt occurs, it will show up in the INT_ST.
|
||||
// Clearing INT_ENA that it won't show up on INT_ST but if interrupt is
|
||||
// already there, it won't clear it - that's why we need to clear the INT_CLR as
|
||||
// well.
|
||||
// INT_ENA means that when the interrupt occurs, it will show up in the
|
||||
// INT_ST. Clearing INT_ENA that it won't show up on INT_ST but if
|
||||
// interrupt is already there, it won't clear it - that's why we need to
|
||||
// clear the INT_CLR as well.
|
||||
#[handler]
|
||||
pub(crate) fn timg0_timer0_handler() {
|
||||
lock(&INT_ENA_LOCK[0], || {
|
||||
@ -1346,13 +948,13 @@ pub mod etm {
|
||||
impl Sealed for Task {}
|
||||
|
||||
/// General purpose timer ETM events.
|
||||
pub trait Events<TG> {
|
||||
pub trait Events {
|
||||
/// ETM event triggered on alarm
|
||||
fn on_alarm(&self) -> Event;
|
||||
}
|
||||
|
||||
/// General purpose timer ETM tasks
|
||||
pub trait Tasks<TG> {
|
||||
pub trait Tasks {
|
||||
/// ETM task to start the counter
|
||||
fn cnt_start(&self) -> Task;
|
||||
|
||||
@ -1370,37 +972,43 @@ pub mod etm {
|
||||
fn alarm_start(&self) -> Task;
|
||||
}
|
||||
|
||||
impl<TG> Events<TG> for Timer0<TG>
|
||||
where
|
||||
TG: TimerGroupInstance,
|
||||
{
|
||||
impl Events for Timer {
|
||||
fn on_alarm(&self) -> Event {
|
||||
Event { id: 48 + TG::id() }
|
||||
Event {
|
||||
id: 48 + self.timer_group(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<TG> Tasks<TG> for Timer0<TG>
|
||||
where
|
||||
TG: TimerGroupInstance,
|
||||
{
|
||||
impl Tasks for Timer {
|
||||
fn cnt_start(&self) -> Task {
|
||||
Task { id: 88 + TG::id() }
|
||||
Task {
|
||||
id: 88 + self.timer_group(),
|
||||
}
|
||||
}
|
||||
|
||||
fn alarm_start(&self) -> Task {
|
||||
Task { id: 90 + TG::id() }
|
||||
Task {
|
||||
id: 90 + self.timer_group(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cnt_stop(&self) -> Task {
|
||||
Task { id: 92 + TG::id() }
|
||||
Task {
|
||||
id: 92 + self.timer_group(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cnt_reload(&self) -> Task {
|
||||
Task { id: 94 + TG::id() }
|
||||
Task {
|
||||
id: 94 + self.timer_group(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cnt_cap(&self) -> Task {
|
||||
Task { id: 96 + TG::id() }
|
||||
Task {
|
||||
id: 96 + self.timer_group(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,12 +285,7 @@ pub trait EspWifiTimerSource: private::Sealed {
|
||||
/// conflicting implementations.
|
||||
trait IntoAnyTimer: Into<AnyTimer> {}
|
||||
|
||||
impl<T, DM> IntoAnyTimer for TimgTimer<T, DM>
|
||||
where
|
||||
DM: esp_hal::Mode,
|
||||
Self: Into<AnyTimer>,
|
||||
{
|
||||
}
|
||||
impl IntoAnyTimer for TimgTimer where Self: Into<AnyTimer> {}
|
||||
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
impl IntoAnyTimer for Alarm where Self: Into<AnyTimer> {}
|
||||
@ -314,12 +309,7 @@ impl EspWifiTimerSource for TimeBase {
|
||||
}
|
||||
|
||||
impl private::Sealed for TimeBase {}
|
||||
impl<T, DM> private::Sealed for TimgTimer<T, DM>
|
||||
where
|
||||
DM: esp_hal::Mode,
|
||||
Self: Into<AnyTimer>,
|
||||
{
|
||||
}
|
||||
impl private::Sealed for TimgTimer where Self: Into<AnyTimer> {}
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
impl private::Sealed for Alarm where Self: Into<AnyTimer> {}
|
||||
|
||||
|
@ -1,84 +0,0 @@
|
||||
//! This shows how to use the general purpose timers ETM tasks and events
|
||||
//! Notice you need to import the traits esp_hal::timer::etm::{Events, Tasks}
|
||||
|
||||
//% CHIPS: esp32c6 esp32h2
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::cell::RefCell;
|
||||
|
||||
use critical_section::Mutex;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
delay::Delay,
|
||||
etm::Etm,
|
||||
peripherals::TIMG0,
|
||||
prelude::*,
|
||||
timer::timg::{
|
||||
etm::{Events, Tasks},
|
||||
Timer,
|
||||
Timer0,
|
||||
TimerGroup,
|
||||
},
|
||||
};
|
||||
|
||||
static TIMER0: Mutex<RefCell<Option<Timer<Timer0<TIMG0>, esp_hal::Blocking>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
let timer0 = timg0.timer0;
|
||||
timer0.set_interrupt_handler(tg0_t0_level);
|
||||
|
||||
// Configure ETM to stop timer0 when alarm is triggered
|
||||
let event = timer0.on_alarm();
|
||||
let task = timer0.cnt_stop();
|
||||
|
||||
let etm = Etm::new(peripherals.SOC_ETM);
|
||||
|
||||
let channel0 = etm.channel0;
|
||||
|
||||
let _configured_channel = channel0.setup(&event, &task);
|
||||
|
||||
// Setup alarm at 100ms
|
||||
// 80 / 2 (default divider) timer clock cycles == 1 us
|
||||
timer0.load_alarm_value(100 * 1_000 * 40);
|
||||
timer0.set_alarm_active(true);
|
||||
timer0.set_counter_active(true);
|
||||
|
||||
critical_section::with(|cs| {
|
||||
timer0.listen();
|
||||
TIMER0.borrow_ref_mut(cs).replace(timer0);
|
||||
});
|
||||
|
||||
let delay = Delay::new();
|
||||
|
||||
loop {
|
||||
delay.delay_millis(500u32);
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let mut timer0 = TIMER0.borrow_ref_mut(cs);
|
||||
let timer0 = timer0.as_mut().unwrap();
|
||||
// Counter value should be the same than in interrupt
|
||||
esp_println::println!("counter in main: {}", timer0.now());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[handler]
|
||||
fn tg0_t0_level() {
|
||||
critical_section::with(|cs| {
|
||||
let mut timer0 = TIMER0.borrow_ref_mut(cs);
|
||||
let timer0 = timer0.as_mut().unwrap();
|
||||
|
||||
timer0.clear_interrupt();
|
||||
|
||||
// Counter value should be a very small number as the alarm triggered a counter reload to 0
|
||||
// and ETM stopped the counter quickly after
|
||||
esp_println::println!("counter in interrupt: {}", timer0.now());
|
||||
});
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
//! This shows how to use the TIMG peripheral interrupts.
|
||||
//!
|
||||
//! There is TIMG0 which contains a general purpose timer and a watchdog timer.
|
||||
|
||||
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::cell::RefCell;
|
||||
|
||||
use critical_section::Mutex;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
interrupt::{self, Priority},
|
||||
peripherals::{Interrupt, TIMG0},
|
||||
prelude::*,
|
||||
timer::timg::{Timer, Timer0, TimerGroup},
|
||||
};
|
||||
|
||||
static TIMER0: Mutex<RefCell<Option<Timer<Timer0<TIMG0>, esp_hal::Blocking>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
let timer0 = timg0.timer0;
|
||||
timer0.set_interrupt_handler(tg0_t0_level);
|
||||
|
||||
interrupt::enable(Interrupt::TG0_T0_LEVEL, Priority::Priority1).unwrap();
|
||||
timer0.load_value(500u64.millis()).unwrap();
|
||||
timer0.start();
|
||||
timer0.listen();
|
||||
|
||||
critical_section::with(|cs| {
|
||||
TIMER0.borrow_ref_mut(cs).replace(timer0);
|
||||
});
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[handler]
|
||||
fn tg0_t0_level() {
|
||||
critical_section::with(|cs| {
|
||||
esp_println::println!(
|
||||
"Interrupt at {} ms",
|
||||
esp_hal::time::now().duration_since_epoch().to_millis()
|
||||
);
|
||||
|
||||
let mut timer0 = TIMER0.borrow_ref_mut(cs);
|
||||
let timer0 = timer0.as_mut().unwrap();
|
||||
|
||||
timer0.clear_interrupt();
|
||||
timer0.load_value(500u64.millis()).unwrap();
|
||||
timer0.start();
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user