esp_hal/timer/
systimer.rs

1//! # System Timer (SYSTIMER)
2//!
3//! ## Overview
4//! The System Timer is a
5#![cfg_attr(esp32s2, doc = "64-bit")]
6#![cfg_attr(not(esp32s2), doc = "52-bit")]
7//! timer which can be used, for example, to generate tick interrupts for an
8//! operating system, or simply as a general-purpose timer.
9//!
10//! ## Configuration
11//!
12//! The timer consists of two counters, `Unit0` and `Unit1`. The counter values
13//! can be monitored by 3 [`Alarm`]s
14//!
15//! It is recommended to pass the [`Alarm`]s into a high level driver like
16//! [`OneShotTimer`](super::OneShotTimer) and
17//! [`PeriodicTimer`](super::PeriodicTimer). Using the System timer directly is
18//! only possible through the low level [`Timer`](crate::timer::Timer) trait.
19
20use core::{fmt::Debug, marker::PhantomData};
21
22use super::{Error, Timer as _};
23use crate::{
24    asynch::AtomicWaker,
25    interrupt::{self, InterruptHandler},
26    peripherals::{Interrupt, SYSTIMER},
27    sync::{RawMutex, lock},
28    system::{Cpu, Peripheral as PeripheralEnable, PeripheralClockControl},
29    time::{Duration, Instant},
30};
31
32/// The configuration of a unit.
33#[derive(Copy, Clone)]
34pub enum UnitConfig {
35    /// Unit is not counting.
36    Disabled,
37
38    /// Unit is counting unless the Cpu is stalled.
39    DisabledIfCpuIsStalled(Cpu),
40
41    /// Unit is counting.
42    Enabled,
43}
44
45/// System Timer driver.
46pub struct SystemTimer<'d> {
47    /// Alarm 0.
48    pub alarm0: Alarm<'d>,
49
50    /// Alarm 1.
51    pub alarm1: Alarm<'d>,
52
53    /// Alarm 2.
54    pub alarm2: Alarm<'d>,
55}
56
57impl<'d> SystemTimer<'d> {
58    cfg_if::cfg_if! {
59        if #[cfg(esp32s2)] {
60            /// Bitmask to be applied to the raw register value.
61            pub const BIT_MASK: u64 = u64::MAX;
62            // Bitmask to be applied to the raw period register value.
63            const PERIOD_MASK: u64 = 0x1FFF_FFFF;
64        } else {
65            /// Bitmask to be applied to the raw register value.
66            pub const BIT_MASK: u64 = 0xF_FFFF_FFFF_FFFF;
67            // Bitmask to be applied to the raw period register value.
68            const PERIOD_MASK: u64 = 0x3FF_FFFF;
69        }
70    }
71
72    /// Returns the tick frequency of the underlying timer unit.
73    #[inline]
74    pub fn ticks_per_second() -> u64 {
75        cfg_if::cfg_if! {
76            if #[cfg(esp32s2)] {
77                const MULTIPLIER: u32 = 2;
78                const DIVIDER: u32 = 1;
79            } else if #[cfg(esp32h2)] {
80                // The counters and comparators are driven using `XTAL_CLK`.
81                // The average clock frequency is fXTAL_CLK/2, which is 16 MHz.
82                // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
83                const MULTIPLIER: u32 = 1;
84                const DIVIDER: u32 = 2;
85            } else {
86                // The counters and comparators are driven using `XTAL_CLK`.
87                // The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz.
88                // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
89                const MULTIPLIER: u32 = 4;
90                const DIVIDER: u32 = 10;
91            }
92        }
93        let xtal_freq_mhz = crate::clock::Clocks::xtal_freq().as_hz();
94        ((xtal_freq_mhz * MULTIPLIER) / DIVIDER) as u64
95    }
96
97    /// Create a new instance.
98    pub fn new(_systimer: SYSTIMER<'d>) -> Self {
99        // Don't reset Systimer as it will break `time::Instant::now`, only enable it
100        PeripheralClockControl::enable(PeripheralEnable::Systimer);
101
102        #[cfg(soc_has_etm)]
103        etm::enable_etm();
104
105        Self {
106            alarm0: Alarm::new(0),
107            alarm1: Alarm::new(1),
108            alarm2: Alarm::new(2),
109        }
110    }
111
112    /// Get the current count of the given unit in the System Timer.
113    pub fn unit_value(unit: Unit) -> u64 {
114        // This should be safe to access from multiple contexts
115        // worst case scenario the second accessor ends up reading
116        // an older time stamp
117
118        unit.read_count()
119    }
120
121    #[cfg(not(esp32s2))]
122    /// Configures when this counter can run.
123    /// It can be configured to stall or continue running when CPU stalls
124    /// or enters on-chip-debugging mode.
125    ///
126    /// # Safety
127    ///
128    /// - Disabling a `Unit` whilst [`Alarm`]s are using it will affect the [`Alarm`]s operation.
129    /// - Disabling Unit0 will affect [`Instant::now`].
130    pub unsafe fn configure_unit(unit: Unit, config: UnitConfig) {
131        unit.configure(config)
132    }
133
134    /// Set the value of the counter immediately. If the unit is at work,
135    /// the counter will continue to count up from the new reloaded value.
136    ///
137    /// This can be used to load back the sleep time recorded by RTC timer
138    /// via software after Light-sleep
139    ///
140    /// # Safety
141    ///
142    /// - Modifying a unit's count whilst [`Alarm`]s are using it may cause unexpected behaviour
143    /// - Any modification of the unit0 count will affect [`Instant::now`]
144    pub unsafe fn set_unit_value(unit: Unit, value: u64) {
145        unit.set_count(value)
146    }
147}
148
149/// A
150#[cfg_attr(esp32s2, doc = "64-bit")]
151#[cfg_attr(not(esp32s2), doc = "52-bit")]
152/// counter.
153#[derive(Copy, Clone, Debug, PartialEq, Eq)]
154#[cfg_attr(feature = "defmt", derive(defmt::Format))]
155pub enum Unit {
156    /// Unit 0
157    Unit0 = 0,
158    #[cfg(not(esp32s2))]
159    /// Unit 1
160    Unit1 = 1,
161}
162
163impl Unit {
164    #[inline]
165    fn channel(&self) -> u8 {
166        *self as _
167    }
168
169    #[cfg(not(esp32s2))]
170    fn configure(&self, config: UnitConfig) {
171        lock(&CONF_LOCK, || {
172            SYSTIMER::regs().conf().modify(|_, w| match config {
173                UnitConfig::Disabled => match self.channel() {
174                    0 => w.timer_unit0_work_en().clear_bit(),
175                    1 => w.timer_unit1_work_en().clear_bit(),
176                    _ => unreachable!(),
177                },
178                UnitConfig::DisabledIfCpuIsStalled(cpu) => match self.channel() {
179                    0 => {
180                        w.timer_unit0_work_en().set_bit();
181                        w.timer_unit0_core0_stall_en().bit(cpu == Cpu::ProCpu);
182                        w.timer_unit0_core1_stall_en().bit(cpu != Cpu::ProCpu)
183                    }
184                    1 => {
185                        w.timer_unit1_work_en().set_bit();
186                        w.timer_unit1_core0_stall_en().bit(cpu == Cpu::ProCpu);
187                        w.timer_unit1_core1_stall_en().bit(cpu != Cpu::ProCpu)
188                    }
189                    _ => unreachable!(),
190                },
191                UnitConfig::Enabled => match self.channel() {
192                    0 => {
193                        w.timer_unit0_work_en().set_bit();
194                        w.timer_unit0_core0_stall_en().clear_bit();
195                        w.timer_unit0_core1_stall_en().clear_bit()
196                    }
197                    1 => {
198                        w.timer_unit1_work_en().set_bit();
199                        w.timer_unit1_core0_stall_en().clear_bit();
200                        w.timer_unit1_core1_stall_en().clear_bit()
201                    }
202                    _ => unreachable!(),
203                },
204            });
205        });
206    }
207
208    fn set_count(&self, value: u64) {
209        let systimer = SYSTIMER::regs();
210        #[cfg(not(esp32s2))]
211        {
212            let unitload = systimer.unitload(self.channel() as _);
213            let unit_load = systimer.unit_load(self.channel() as _);
214
215            unitload.hi().write(|w| w.load_hi().set((value << 32) as _));
216            unitload
217                .lo()
218                .write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _));
219
220            unit_load.write(|w| w.load().set_bit());
221        }
222        #[cfg(esp32s2)]
223        {
224            systimer
225                .load_hi()
226                .write(|w| w.load_hi().set((value << 32) as _));
227            systimer
228                .load_lo()
229                .write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _));
230
231            systimer.load().write(|w| w.load().set_bit());
232        }
233    }
234
235    fn read_count(&self) -> u64 {
236        // This can be a shared reference as long as this type isn't Sync.
237
238        let channel = self.channel() as usize;
239        let systimer = SYSTIMER::regs();
240
241        systimer.unit_op(channel).write(|w| w.update().set_bit());
242        while !systimer.unit_op(channel).read().value_valid().bit_is_set() {}
243
244        // Read LO, HI, then LO again, check that LO returns the same value.
245        // This accounts for the case when an interrupt may happen between reading
246        // HI and LO values (or the other core updates the counter mid-read), and this
247        // function may get called from the ISR. In this case, the repeated read
248        // will return consistent values.
249        let unit_value = systimer.unit_value(channel);
250        let mut lo_prev = unit_value.lo().read().bits();
251        loop {
252            let lo = lo_prev;
253            let hi = unit_value.hi().read().bits();
254            lo_prev = unit_value.lo().read().bits();
255
256            if lo == lo_prev {
257                return ((hi as u64) << 32) | lo as u64;
258            }
259        }
260    }
261}
262
263/// An alarm unit
264#[derive(Debug)]
265#[cfg_attr(feature = "defmt", derive(defmt::Format))]
266pub struct Alarm<'d> {
267    comp: u8,
268    unit: Unit,
269    _lifetime: PhantomData<&'d mut ()>,
270}
271
272impl Alarm<'_> {
273    const fn new(comp: u8) -> Self {
274        Alarm {
275            comp,
276            unit: Unit::Unit0,
277            _lifetime: PhantomData,
278        }
279    }
280
281    /// Unsafely clone this peripheral reference.
282    ///
283    /// # Safety
284    ///
285    /// You must ensure that you're only using one instance of this type at a
286    /// time.
287    pub unsafe fn clone_unchecked(&self) -> Self {
288        Self {
289            comp: self.comp,
290            unit: self.unit,
291            _lifetime: PhantomData,
292        }
293    }
294
295    /// Creates a new peripheral reference with a shorter lifetime.
296    ///
297    /// Use this method if you would like to keep working with the peripheral
298    /// after you dropped the driver that consumes this.
299    ///
300    /// See [Peripheral singleton] section for more information.
301    ///
302    /// [Peripheral singleton]: crate#peripheral-singletons
303    pub fn reborrow(&mut self) -> Alarm<'_> {
304        unsafe { self.clone_unchecked() }
305    }
306
307    /// Returns the comparator's number.
308    #[inline]
309    fn channel(&self) -> u8 {
310        self.comp
311    }
312
313    /// Enables/disables the comparator. If enabled, this means
314    /// it will generate interrupt based on its configuration.
315    fn set_enable(&self, enable: bool) {
316        lock(&CONF_LOCK, || {
317            #[cfg(not(esp32s2))]
318            SYSTIMER::regs().conf().modify(|_, w| match self.channel() {
319                0 => w.target0_work_en().bit(enable),
320                1 => w.target1_work_en().bit(enable),
321                2 => w.target2_work_en().bit(enable),
322                _ => unreachable!(),
323            });
324        });
325
326        // Note: The ESP32-S2 doesn't require a lock because each
327        // comparator's enable bit in a different register.
328        #[cfg(esp32s2)]
329        SYSTIMER::regs()
330            .target_conf(self.channel() as usize)
331            .modify(|_r, w| w.work_en().bit(enable));
332    }
333
334    /// Returns true if the comparator has been enabled. This means
335    /// it will generate interrupt based on its configuration.
336    fn is_enabled(&self) -> bool {
337        #[cfg(not(esp32s2))]
338        match self.channel() {
339            0 => SYSTIMER::regs().conf().read().target0_work_en().bit(),
340            1 => SYSTIMER::regs().conf().read().target1_work_en().bit(),
341            2 => SYSTIMER::regs().conf().read().target2_work_en().bit(),
342            _ => unreachable!(),
343        }
344
345        #[cfg(esp32s2)]
346        SYSTIMER::regs()
347            .target_conf(self.channel() as usize)
348            .read()
349            .work_en()
350            .bit()
351    }
352
353    /// Sets the unit this comparator uses as a reference count.
354    #[cfg(not(esp32s2))]
355    pub fn set_unit(&self, unit: Unit) {
356        SYSTIMER::regs()
357            .target_conf(self.channel() as usize)
358            .modify(|_, w| w.timer_unit_sel().bit(matches!(unit, Unit::Unit1)));
359    }
360
361    /// Set the mode of the comparator to be either target or periodic.
362    fn set_mode(&self, mode: ComparatorMode) {
363        let is_period_mode = match mode {
364            ComparatorMode::Period => true,
365            ComparatorMode::Target => false,
366        };
367        SYSTIMER::regs()
368            .target_conf(self.channel() as usize)
369            .modify(|_, w| w.period_mode().bit(is_period_mode));
370    }
371
372    /// Get the current mode of the comparator, which is either target or
373    /// periodic.
374    fn mode(&self) -> ComparatorMode {
375        if SYSTIMER::regs()
376            .target_conf(self.channel() as usize)
377            .read()
378            .period_mode()
379            .bit()
380        {
381            ComparatorMode::Period
382        } else {
383            ComparatorMode::Target
384        }
385    }
386
387    /// Set how often the comparator should generate an interrupt when in
388    /// periodic mode.
389    fn set_period(&self, value: u32) {
390        let systimer = SYSTIMER::regs();
391        let tconf = systimer.target_conf(self.channel() as usize);
392        unsafe { tconf.modify(|_, w| w.period().bits(value)) };
393        #[cfg(not(esp32s2))]
394        {
395            let comp_load = systimer.comp_load(self.channel() as usize);
396            comp_load.write(|w| w.load().set_bit());
397        }
398    }
399
400    /// Set when the comparator should generate an interrupt in target mode.
401    fn set_target(&self, value: u64) {
402        let systimer = SYSTIMER::regs();
403        let target = systimer.trgt(self.channel() as usize);
404        target.hi().write(|w| w.hi().set((value >> 32) as u32));
405        target
406            .lo()
407            .write(|w| w.lo().set((value & 0xFFFF_FFFF) as u32));
408        #[cfg(not(esp32s2))]
409        {
410            let comp_load = systimer.comp_load(self.channel() as usize);
411            comp_load.write(|w| w.load().set_bit());
412        }
413    }
414
415    /// Set the interrupt handler for this comparator.
416    fn set_interrupt_handler(&self, handler: InterruptHandler) {
417        let interrupt = match self.channel() {
418            0 => Interrupt::SYSTIMER_TARGET0,
419            1 => Interrupt::SYSTIMER_TARGET1,
420            2 => Interrupt::SYSTIMER_TARGET2,
421            _ => unreachable!(),
422        };
423
424        for core in crate::system::Cpu::other() {
425            crate::interrupt::disable(core, interrupt);
426        }
427
428        #[cfg(not(esp32s2))]
429        unsafe {
430            interrupt::bind_interrupt(interrupt, handler.handler());
431        }
432
433        #[cfg(esp32s2)]
434        {
435            // ESP32-S2 Systimer interrupts are edge triggered. Our interrupt
436            // handler calls each of the handlers, regardless of which one triggered the
437            // interrupt. This mess registers an intermediate handler that
438            // checks if an interrupt is active before calling the associated
439            // handler functions.
440
441            static mut HANDLERS: [Option<extern "C" fn()>; 3] = [None, None, None];
442
443            #[crate::ram]
444            unsafe extern "C" fn _handle_interrupt<const CH: u8>() {
445                if SYSTIMER::regs().int_raw().read().target(CH).bit_is_set() {
446                    let handler = unsafe { HANDLERS[CH as usize] };
447                    if let Some(handler) = handler {
448                        handler();
449                    }
450                }
451            }
452
453            unsafe {
454                HANDLERS[self.channel() as usize] = Some(handler.handler());
455                let handler = match self.channel() {
456                    0 => _handle_interrupt::<0>,
457                    1 => _handle_interrupt::<1>,
458                    2 => _handle_interrupt::<2>,
459                    _ => unreachable!(),
460                };
461                interrupt::bind_interrupt(interrupt, handler);
462            }
463        }
464        unwrap!(interrupt::enable(interrupt, handler.priority()));
465    }
466}
467
468/// The modes of a comparator.
469#[derive(Copy, Clone)]
470enum ComparatorMode {
471    /// The comparator will generate interrupts periodically.
472    Period,
473
474    /// The comparator will generate an interrupt when the unit reaches the
475    /// target.
476    Target,
477}
478
479impl super::Timer for Alarm<'_> {
480    fn start(&self) {
481        self.set_enable(true);
482    }
483
484    fn stop(&self) {
485        self.set_enable(false);
486    }
487
488    fn reset(&self) {
489        #[cfg(esp32s2)]
490        // Run at XTAL freq, not 80 * XTAL freq:
491        SYSTIMER::regs()
492            .step()
493            .modify(|_, w| unsafe { w.xtal_step().bits(0x1) });
494
495        #[cfg(not(esp32s2))]
496        SYSTIMER::regs()
497            .conf()
498            .modify(|_, w| w.timer_unit0_core0_stall_en().clear_bit());
499    }
500
501    fn is_running(&self) -> bool {
502        self.is_enabled()
503    }
504
505    fn now(&self) -> Instant {
506        // This should be safe to access from multiple contexts; worst case
507        // scenario the second accessor ends up reading an older time stamp.
508
509        let ticks = self.unit.read_count();
510
511        let us = ticks / (SystemTimer::ticks_per_second() / 1_000_000);
512
513        Instant::from_ticks(us)
514    }
515
516    fn load_value(&self, value: Duration) -> Result<(), Error> {
517        let mode = self.mode();
518
519        let us = value.as_micros();
520        let ticks = us * (SystemTimer::ticks_per_second() / 1_000_000);
521
522        if matches!(mode, ComparatorMode::Period) {
523            // Period mode
524
525            // The `SYSTIMER_TARGETx_PERIOD` field is 26-bits wide (or
526            // 29-bits on the ESP32-S2), so we must ensure that the provided
527            // value is not too wide:
528            if (ticks & !SystemTimer::PERIOD_MASK) != 0 {
529                return Err(Error::InvalidTimeout);
530            }
531
532            self.set_period(ticks as u32);
533
534            // Clear and then set SYSTIMER_TARGETx_PERIOD_MODE to configure COMPx into
535            // period mode
536            self.set_mode(ComparatorMode::Target);
537            self.set_mode(ComparatorMode::Period);
538        } else {
539            // Target mode
540
541            // The counters/comparators are 52-bits wide (except on ESP32-S2,
542            // which is 64-bits), so we must ensure that the provided value
543            // is not too wide:
544            #[cfg(not(esp32s2))]
545            if (ticks & !SystemTimer::BIT_MASK) != 0 {
546                return Err(Error::InvalidTimeout);
547            }
548
549            let v = self.unit.read_count();
550            let t = v + ticks;
551
552            self.set_target(t);
553        }
554
555        Ok(())
556    }
557
558    fn enable_auto_reload(&self, auto_reload: bool) {
559        // If `auto_reload` is true use Period Mode, otherwise use Target Mode:
560        let mode = if auto_reload {
561            ComparatorMode::Period
562        } else {
563            ComparatorMode::Target
564        };
565        self.set_mode(mode)
566    }
567
568    fn enable_interrupt(&self, state: bool) {
569        lock(&INT_ENA_LOCK, || {
570            SYSTIMER::regs()
571                .int_ena()
572                .modify(|_, w| w.target(self.channel()).bit(state));
573        });
574    }
575
576    fn clear_interrupt(&self) {
577        SYSTIMER::regs()
578            .int_clr()
579            .write(|w| w.target(self.channel()).clear_bit_by_one());
580    }
581
582    fn is_interrupt_set(&self) -> bool {
583        SYSTIMER::regs()
584            .int_raw()
585            .read()
586            .target(self.channel())
587            .bit_is_set()
588    }
589
590    fn async_interrupt_handler(&self) -> InterruptHandler {
591        match self.channel() {
592            0 => asynch::target0_handler,
593            1 => asynch::target1_handler,
594            2 => asynch::target2_handler,
595            _ => unreachable!(),
596        }
597    }
598
599    fn peripheral_interrupt(&self) -> Interrupt {
600        match self.channel() {
601            0 => Interrupt::SYSTIMER_TARGET0,
602            1 => Interrupt::SYSTIMER_TARGET1,
603            2 => Interrupt::SYSTIMER_TARGET2,
604            _ => unreachable!(),
605        }
606    }
607
608    fn set_interrupt_handler(&self, handler: InterruptHandler) {
609        self.set_interrupt_handler(handler)
610    }
611
612    fn waker(&self) -> &AtomicWaker {
613        asynch::waker(self)
614    }
615}
616
617impl crate::private::Sealed for Alarm<'_> {}
618
619static CONF_LOCK: RawMutex = RawMutex::new();
620static INT_ENA_LOCK: RawMutex = RawMutex::new();
621
622// Async functionality of the system timer.
623mod asynch {
624    use core::marker::PhantomData;
625
626    use procmacros::handler;
627
628    use super::*;
629    use crate::asynch::AtomicWaker;
630
631    const NUM_ALARMS: usize = 3;
632    static WAKERS: [AtomicWaker; NUM_ALARMS] = [const { AtomicWaker::new() }; NUM_ALARMS];
633
634    pub(super) fn waker(alarm: &Alarm<'_>) -> &'static AtomicWaker {
635        &WAKERS[alarm.channel() as usize]
636    }
637
638    #[inline]
639    fn handle_alarm(alarm: u8) {
640        Alarm {
641            comp: alarm,
642            unit: Unit::Unit0,
643            _lifetime: PhantomData,
644        }
645        .enable_interrupt(false);
646
647        WAKERS[alarm as usize].wake();
648    }
649
650    #[handler]
651    pub(crate) fn target0_handler() {
652        handle_alarm(0);
653    }
654
655    #[handler]
656    pub(crate) fn target1_handler() {
657        handle_alarm(1);
658    }
659
660    #[handler]
661    pub(crate) fn target2_handler() {
662        handle_alarm(2);
663    }
664}
665
666#[cfg(soc_has_etm)]
667pub mod etm {
668    #![cfg_attr(docsrs, procmacros::doc_replace)]
669    //! # Event Task Matrix Function
670    //!
671    //! ## Overview
672    //!
673    //! The system timer supports the Event Task Matrix (ETM) function, which
674    //! allows the system timer’s ETM events to trigger any peripherals’ ETM
675    //! tasks.
676    //!
677    //! The system timer can generate the following ETM events:
678    //! - SYSTIMER_EVT_CNT_CMPx: Indicates the alarm pulses generated by COMPx
679    //! ## Example
680    //! ```rust, no_run
681    //! # {before_snippet}
682    //! # use esp_hal::timer::systimer::{etm::Event, SystemTimer};
683    //! # use esp_hal::timer::PeriodicTimer;
684    //! # use esp_hal::etm::Etm;
685    //! # use esp_hal::gpio::{
686    //! #     etm::{Channels, OutputConfig},
687    //! #     Level,
688    //! #     Pull,
689    //! # };
690    //! let syst = SystemTimer::new(peripherals.SYSTIMER);
691    //! let etm = Etm::new(peripherals.ETM);
692    //! let gpio_ext = Channels::new(peripherals.GPIO_SD);
693    //! let alarm0 = syst.alarm0;
694    //! let mut led = peripherals.GPIO1;
695    //!
696    //! let timer_event = Event::new(&alarm0);
697    //! let led_task = gpio_ext.channel0_task.toggle(
698    //!     led,
699    //!     OutputConfig {
700    //!         open_drain: false,
701    //!         pull: Pull::None,
702    //!         initial_state: Level::High,
703    //!     },
704    //! );
705    //!
706    //! let _configured_etm_channel = etm.channel0.setup(&timer_event, &led_task);
707    //!
708    //! let timer = PeriodicTimer::new(alarm0);
709    //! // configure the timer as usual
710    //! // when it fires it will toggle the GPIO
711    //! # {after_snippet}
712    //! ```
713
714    use super::*;
715
716    /// An ETM controlled SYSTIMER event
717    pub struct Event {
718        id: u8,
719    }
720
721    impl Event {
722        /// Creates an ETM event from the given [Alarm]
723        pub fn new(alarm: &Alarm<'_>) -> Self {
724            Self {
725                id: 50 + alarm.channel(),
726            }
727        }
728    }
729
730    impl crate::private::Sealed for Event {}
731
732    impl crate::etm::EtmEvent for Event {
733        fn id(&self) -> u8 {
734            self.id
735        }
736    }
737
738    pub(super) fn enable_etm() {
739        SYSTIMER::regs().conf().modify(|_, w| w.etm_en().set_bit());
740    }
741}