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