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::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 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//!     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(alarm0.reborrow());
76//! timer.start(Duration::from_secs(1));
77//!
78//! let 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//!     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(&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::{peripherals::SOC_ETM, system::GenericPeripheralGuard};
106
107/// Unconfigured EtmChannel.
108#[non_exhaustive]
109pub struct EtmChannel<const C: u8> {}
110
111impl<const C: u8> EtmChannel<C> {
112    /// Setup the channel
113    ///
114    /// Enabled the channel and configures the assigned event and task.
115    pub fn setup<'a, E, T>(self, event: &'a E, task: &'a T) -> EtmConfiguredChannel<'a, E, T, C>
116    where
117        E: EtmEvent,
118        T: EtmTask,
119    {
120        let etm = SOC_ETM::regs();
121        let guard = GenericPeripheralGuard::new();
122
123        etm.ch(C as usize)
124            .evt_id()
125            .modify(|_, w| unsafe { w.evt_id().bits(event.id()) });
126        etm.ch(C as usize)
127            .task_id()
128            .modify(|_, w| unsafe { w.task_id().bits(task.id()) });
129        if C < 32 {
130            etm.ch_ena_ad0_set().write(|w| w.ch_set(C).set_bit());
131        } else {
132            etm.ch_ena_ad1_set().write(|w| w.ch_set(C - 32).set_bit());
133        }
134
135        EtmConfiguredChannel {
136            _event: event,
137            _task: task,
138            _guard: guard,
139        }
140    }
141}
142
143fn disable_channel(channel: u8) {
144    let etm = SOC_ETM::regs();
145    if channel < 32 {
146        etm.ch_ena_ad0_clr().write(|w| w.ch_clr(channel).set_bit());
147    } else {
148        etm.ch_ena_ad1_clr()
149            .write(|w| w.ch_clr(channel - 32).set_bit());
150    }
151}
152
153/// A readily configured channel
154///
155/// The channel is enabled and event and task are configured.
156#[non_exhaustive]
157pub struct EtmConfiguredChannel<'a, E, T, const C: u8>
158where
159    E: EtmEvent,
160    T: EtmTask,
161{
162    _event: &'a E,
163    _task: &'a T,
164    _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Etm as u8 }>,
165}
166
167impl<E, T, const C: u8> Drop for EtmConfiguredChannel<'_, E, T, C>
168where
169    E: EtmEvent,
170    T: EtmTask,
171{
172    fn drop(&mut self) {
173        debug!("Drop ETM channel {}", C);
174        disable_channel(C);
175    }
176}
177
178macro_rules! create_etm {
179    ($($num:literal),+) => {
180        paste::paste! {
181            /// ETM Instance
182            ///
183            /// Provides access to all the [EtmChannel]
184            pub struct Etm<'d> {
185                _peripheral: crate::peripherals::SOC_ETM<'d>,
186                $(
187                    /// An individual ETM channel, identified by its index number.
188                    pub [< channel $num >]: EtmChannel<$num>,
189                )+
190            }
191
192            impl<'d> Etm<'d> {
193                /// Creates a new `Etm` instance.
194                pub fn new(peripheral: crate::peripherals::SOC_ETM<'d>) -> Self {
195                    Self {
196                        _peripheral: peripheral,
197                        $([< channel $num >]: EtmChannel { },)+
198                    }
199                }
200            }
201        }
202    };
203}
204
205create_etm!(
206    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,
207    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49
208);
209
210#[doc(hidden)]
211pub trait EtmEvent: crate::private::Sealed {
212    fn id(&self) -> u8;
213}
214
215#[doc(hidden)]
216pub trait EtmTask: crate::private::Sealed {
217    fn id(&self) -> u8;
218}