esp_hal/
etm.rs

1//! # Event Task Matrix (ETM)
2//!
3//! ## Overview
4//!
5//! Normally, if a peripheral X needs to notify peripheral Y of a particular
6//! event, this could only be done via a CPU interrupt from peripheral X, where
7//! the CPU notifies peripheral Y on behalf of peripheral X. However, in
8//! time-critical applications, the latency introduced by CPU interrupts is
9//! non-negligible.
10//!
11//! With the help of the Event Task Matrix (ETM) module, some peripherals can
12//! directly notify other peripherals of events through pre-set connections
13//! without the intervention of CPU interrupts. This allows precise and low
14//! latency synchronization between peripherals, and lessens the CPU’s workload
15//! as the CPU no longer needs to handle these events.
16//!
17//! The ETM module has multiple programmable channels, they are used to connect
18//! a particular Event to a particular Task. When an event is activated, the ETM
19//! channel will trigger the corresponding task automatically.
20//!
21//! For more information, please refer to the
22#![doc = concat!("[ESP-IDF documentation](https://docs.espressif.com/projects/esp-idf/en/latest/", crate::soc::chip!(), "/api-reference/peripherals/etm.html)")]
23//! ## Examples
24//!
25//! ### Control LED by the button via ETM
26//! ```rust, no_run
27#![doc = crate::before_snippet!()]
28//! # use esp_hal::gpio::etm::{Channels, InputConfig, OutputConfig};
29//! # use esp_hal::etm::Etm;
30//! # use esp_hal::gpio::Pull;
31//! # use esp_hal::gpio::Level;
32//!
33//! let mut led = peripherals.GPIO1;
34//! let button = peripherals.GPIO9;
35//!
36//! // setup ETM
37//! let gpio_ext = Channels::new(peripherals.GPIO_SD);
38//! let led_task = gpio_ext.channel0_task.toggle(
39//!     &mut led,
40//!     OutputConfig {
41//!         open_drain: false,
42//!         pull: Pull::None,
43//!         initial_state: Level::Low,
44//!     },
45//! );
46//! let button_event = gpio_ext
47//! .channel0_event
48//! .falling_edge(button, InputConfig { pull: Pull::Down });
49//!
50//! let etm = Etm::new(peripherals.SOC_ETM);
51//! let channel0 = etm.channel0;
52//!
53//! // make sure the configured channel doesn't get dropped - dropping it will
54//! // disable the channel
55//! let _configured_channel = channel0.setup(&button_event, &led_task);
56//!
57//! // the LED is controlled by the button without involving the CPU
58//! loop {}
59//! # }
60//! ```
61//! 
62//! ### Control LED by the systimer via ETM
63//! ```rust, no_run
64#![doc = crate::before_snippet!()]
65//! # use esp_hal::gpio::etm::{Channels, InputConfig, OutputConfig};
66//! # use esp_hal::etm::Etm;
67//! # use esp_hal::gpio::Pull;
68//! # use esp_hal::gpio::Level;
69//! # use esp_hal::timer::systimer::{etm::Event, SystemTimer};
70//! # use esp_hal::timer::PeriodicTimer;
71//! use esp_hal::time::Duration;
72//!
73//! let syst = SystemTimer::new(peripherals.SYSTIMER);
74//! let mut alarm0 = syst.alarm0;
75//! let mut timer = PeriodicTimer::new(&mut alarm0);
76//! timer.start(Duration::from_secs(1));
77//!
78//! let mut led = peripherals.GPIO1;
79//!
80//! // setup ETM
81//! let gpio_ext = Channels::new(peripherals.GPIO_SD);
82//! let led_task = gpio_ext.channel0_task.toggle(
83//!     &mut led,
84//!     OutputConfig {
85//!         open_drain: false,
86//!         pull: Pull::None,
87//!         initial_state: Level::Low,
88//!     },
89//! );
90//!
91//! let timer_event = Event::new(&mut alarm0);
92//!
93//! let etm = Etm::new(peripherals.SOC_ETM);
94//! let channel0 = etm.channel0;
95//!
96//! // make sure the configured channel doesn't get dropped - dropping it will
97//! // disable the channel
98//! let _configured_channel = channel0.setup(&timer_event, &led_task);
99//!
100//! // the LED is controlled by the timer without involving the CPU
101//! loop {}
102//! # }
103//! ```
104
105use crate::{
106    peripheral::{Peripheral, PeripheralRef},
107    peripherals::SOC_ETM,
108    system::GenericPeripheralGuard,
109};
110
111/// Unconfigured EtmChannel.
112#[non_exhaustive]
113pub struct EtmChannel<const C: u8> {}
114
115impl<const C: u8> EtmChannel<C> {
116    /// Setup the channel
117    ///
118    /// Enabled the channel and configures the assigned event and task.
119    pub fn setup<'a, E, T>(self, event: &'a E, task: &'a T) -> EtmConfiguredChannel<'a, E, T, C>
120    where
121        E: EtmEvent,
122        T: EtmTask,
123    {
124        let etm = SOC_ETM::regs();
125        let guard = GenericPeripheralGuard::new();
126
127        etm.ch(C as usize)
128            .evt_id()
129            .modify(|_, w| unsafe { w.evt_id().bits(event.id()) });
130        etm.ch(C as usize)
131            .task_id()
132            .modify(|_, w| unsafe { w.task_id().bits(task.id()) });
133        if C < 32 {
134            etm.ch_ena_ad0_set().write(|w| w.ch_set(C).set_bit());
135        } else {
136            etm.ch_ena_ad1_set().write(|w| w.ch_set(C - 32).set_bit());
137        }
138
139        EtmConfiguredChannel {
140            _event: event,
141            _task: task,
142            _guard: guard,
143        }
144    }
145}
146
147fn disable_channel(channel: u8) {
148    let etm = SOC_ETM::regs();
149    if channel < 32 {
150        etm.ch_ena_ad0_clr().write(|w| w.ch_clr(channel).set_bit());
151    } else {
152        etm.ch_ena_ad1_clr()
153            .write(|w| w.ch_clr(channel - 32).set_bit());
154    }
155}
156
157/// A readily configured channel
158///
159/// The channel is enabled and event and task are configured.
160#[non_exhaustive]
161pub struct EtmConfiguredChannel<'a, E, T, const C: u8>
162where
163    E: EtmEvent,
164    T: EtmTask,
165{
166    _event: &'a E,
167    _task: &'a T,
168    _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Etm as u8 }>,
169}
170
171impl<E, T, const C: u8> Drop for EtmConfiguredChannel<'_, E, T, C>
172where
173    E: EtmEvent,
174    T: EtmTask,
175{
176    fn drop(&mut self) {
177        debug!("Drop ETM channel {}", C);
178        disable_channel(C);
179    }
180}
181
182macro_rules! create_etm {
183    ($($num:literal),+) => {
184        paste::paste! {
185            /// ETM Instance
186            ///
187            /// Provides access to all the [EtmChannel]
188            pub struct Etm<'d> {
189                _peripheral: PeripheralRef<'d, crate::peripherals::SOC_ETM>,
190                $(
191                    /// An individual ETM channel, identified by its index number.
192                    pub [< channel $num >]: EtmChannel<$num>,
193                )+
194            }
195
196            impl<'d> Etm<'d> {
197                /// Creates a new `Etm` instance.
198                pub fn new(peripheral: impl Peripheral<P = crate::peripherals::SOC_ETM> + 'd) -> Self {
199                    crate::into_ref!(peripheral);
200
201                    Self {
202                        _peripheral: peripheral,
203                        $([< channel $num >]: EtmChannel { },)+
204                    }
205                }
206            }
207        }
208    };
209}
210
211create_etm!(
212    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
213    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49
214);
215
216#[doc(hidden)]
217pub trait EtmEvent: crate::private::Sealed {
218    fn id(&self) -> u8;
219}
220
221#[doc(hidden)]
222pub trait EtmTask: crate::private::Sealed {
223    fn id(&self) -> u8;
224}