esp_hal/gpio/
etm.rs

1//! # Event Task Matrix (ETM)
2//!
3//! ## Overview
4//! GPIO supports ETM function, that is, the ETM task of GPIO can be
5//! triggered by the ETM event of any peripheral, or the ETM task of any
6//! peripheral can be triggered by the ETM event of GPIO.
7//!
8//! ## Configuration
9//! The GPIO ETM provides several task channels. The ETM tasks that each task
10//! channel can receive are:
11//! - SET: GPIO goes high when triggered
12//! - CLEAR: GPIO goes low when triggered
13//! - TOGGLE: GPIO toggle level when triggered.
14//!
15//! GPIO has several event channels, and the ETM events that each event
16//! channel can generate are:
17//! - RISE_EDGE: Indicates that the output signal of the corresponding GPIO has
18//!   a rising edge
19//! - FALL_EDGE: Indicates that the output signal of the corresponding GPIO has
20//!   a falling edge
21//! - ANY_EDGE: Indicates that the output signal of the corresponding GPIO is
22//!   reversed
23//!
24//! ## Examples
25//! ### Toggle an LED When a Button is Pressed
26//! ```rust, no_run
27#![doc = crate::before_snippet!()]
28//! # use esp_hal::gpio::etm::Channels;
29//! # use esp_hal::etm::Etm;
30//! # use esp_hal::gpio::etm::InputConfig;
31//! # use esp_hal::gpio::etm::OutputConfig;
32//! # use esp_hal::gpio::Pull;
33//! # use esp_hal::gpio::Level;
34//! #
35//! # let mut led = peripherals.GPIO1;
36//! # let button = peripherals.GPIO9;
37//!
38//! let gpio_ext = Channels::new(peripherals.GPIO_SD);
39//! let led_task = gpio_ext.channel0_task.toggle(
40//!     &mut led,
41//!     OutputConfig {
42//!         open_drain: false,
43//!         pull: Pull::None,
44//!         initial_state: Level::Low,
45//!     },
46//! );
47//! let button_event = gpio_ext
48//!     .channel0_event
49//!     .falling_edge(button, InputConfig { pull: Pull::Down });
50//! # Ok(())
51//! # }
52//! ```
53
54use core::marker::PhantomData;
55
56use crate::{
57    gpio::{
58        interconnect::{InputSignal, OutputSignal},
59        Level,
60        Pull,
61    },
62    peripheral::{Peripheral, PeripheralRef},
63    peripherals::GPIO_SD,
64    private,
65};
66
67/// All the GPIO ETM channels
68#[non_exhaustive]
69pub struct Channels<'d> {
70    _gpio_sd: PeripheralRef<'d, GPIO_SD>,
71    /// Task channel 0 for triggering GPIO tasks.
72    pub channel0_task: TaskChannel<0>,
73    /// Event channel 0 for handling GPIO events.
74    pub channel0_event: EventChannel<0>,
75    /// Task channel 1 for triggering GPIO tasks.
76    pub channel1_task: TaskChannel<1>,
77    /// Event channel 1 for handling GPIO events.
78    pub channel1_event: EventChannel<1>,
79    /// Task channel 2 for triggering GPIO tasks.
80    pub channel2_task: TaskChannel<2>,
81    /// Event channel 2 for handling GPIO events.
82    pub channel2_event: EventChannel<2>,
83    /// Task channel 3 for triggering GPIO tasks.
84    pub channel3_task: TaskChannel<3>,
85    /// Event channel 3 for handling GPIO events.
86    pub channel3_event: EventChannel<3>,
87    /// Task channel 4 for triggering GPIO tasks.
88    pub channel4_task: TaskChannel<4>,
89    /// Event channel 4 for handling GPIO events.
90    pub channel4_event: EventChannel<4>,
91    /// Task channel 5 for triggering GPIO tasks.
92    pub channel5_task: TaskChannel<5>,
93    /// Event channel 5 for handling GPIO events.
94    pub channel5_event: EventChannel<5>,
95    /// Task channel 6 for triggering GPIO tasks.
96    pub channel6_task: TaskChannel<6>,
97    /// Event channel 6 for handling GPIO events.
98    pub channel6_event: EventChannel<6>,
99    /// Task channel 7 for triggering GPIO tasks.
100    pub channel7_task: TaskChannel<7>,
101    /// Event channel 7 for handling GPIO events.
102    pub channel7_event: EventChannel<7>,
103}
104
105impl<'d> Channels<'d> {
106    /// Create a new instance
107    pub fn new(peripheral: impl Peripheral<P = GPIO_SD> + 'd) -> Self {
108        crate::into_ref!(peripheral);
109
110        Self {
111            _gpio_sd: peripheral,
112            channel0_task: TaskChannel {},
113            channel0_event: EventChannel {},
114            channel1_task: TaskChannel {},
115            channel1_event: EventChannel {},
116            channel2_task: TaskChannel {},
117            channel2_event: EventChannel {},
118            channel3_task: TaskChannel {},
119            channel3_event: EventChannel {},
120            channel4_task: TaskChannel {},
121            channel4_event: EventChannel {},
122            channel5_task: TaskChannel {},
123            channel5_event: EventChannel {},
124            channel6_task: TaskChannel {},
125            channel6_event: EventChannel {},
126            channel7_task: TaskChannel {},
127            channel7_event: EventChannel {},
128        }
129    }
130}
131
132/// Configuration for an ETM controlled GPIO input pin
133#[derive(Clone, Copy, Debug)]
134#[cfg_attr(feature = "defmt", derive(defmt::Format))]
135pub struct InputConfig {
136    /// Configuration for the internal pull-up resistors
137    pub pull: Pull,
138}
139
140impl Default for InputConfig {
141    fn default() -> Self {
142        Self { pull: Pull::None }
143    }
144}
145
146/// An ETM controlled GPIO event
147pub struct EventChannel<const C: u8> {}
148
149impl<const C: u8> EventChannel<C> {
150    /// Trigger at rising edge
151    pub fn rising_edge<'d>(
152        self,
153        pin: impl Peripheral<P = impl Into<InputSignal>> + 'd,
154        pin_config: InputConfig,
155    ) -> Event<'d> {
156        self.into_event(pin, pin_config, EventKind::Rising)
157    }
158
159    /// Trigger at falling edge
160    pub fn falling_edge<'d>(
161        self,
162        pin: impl Peripheral<P = impl Into<InputSignal>> + 'd,
163        pin_config: InputConfig,
164    ) -> Event<'d> {
165        self.into_event(pin, pin_config, EventKind::Falling)
166    }
167
168    /// Trigger at any edge
169    pub fn any_edge<'d>(
170        self,
171        pin: impl Peripheral<P = impl Into<InputSignal>> + 'd,
172        pin_config: InputConfig,
173    ) -> Event<'d> {
174        self.into_event(pin, pin_config, EventKind::Any)
175    }
176
177    fn into_event<'d>(
178        self,
179        pin: impl Peripheral<P = impl Into<InputSignal>> + 'd,
180        pin_config: InputConfig,
181        kind: EventKind,
182    ) -> Event<'d> {
183        crate::into_mapped_ref!(pin);
184
185        pin.init_input(pin_config.pull);
186
187        enable_event_channel(C, pin.number());
188        Event {
189            id: kind.id() + C,
190            _pin: PhantomData,
191        }
192    }
193}
194
195#[derive(Clone, Copy, Debug)]
196#[cfg_attr(feature = "defmt", derive(defmt::Format))]
197enum EventKind {
198    Rising,
199    Falling,
200    Any,
201}
202
203impl EventKind {
204    fn id(&self) -> u8 {
205        match self {
206            EventKind::Rising => 1,
207            EventKind::Falling => 9,
208            EventKind::Any => 17,
209        }
210    }
211}
212
213/// Event for rising edge
214pub struct Event<'d> {
215    _pin: PhantomData<&'d mut ()>,
216    id: u8,
217}
218
219impl private::Sealed for Event<'_> {}
220
221impl crate::etm::EtmEvent for Event<'_> {
222    fn id(&self) -> u8 {
223        self.id
224    }
225}
226
227/// Configuration for an ETM controlled GPIO output pin
228#[derive(Clone, Copy, Debug)]
229#[cfg_attr(feature = "defmt", derive(defmt::Format))]
230pub struct OutputConfig {
231    /// Set to open-drain output
232    pub open_drain: bool,
233    /// Only used when open-drain
234    pub pull: Pull,
235    /// Initial pin state
236    pub initial_state: Level,
237}
238
239impl Default for OutputConfig {
240    fn default() -> Self {
241        Self {
242            open_drain: false,
243            pull: Pull::None,
244            initial_state: Level::Low,
245        }
246    }
247}
248
249/// An ETM controlled GPIO task
250pub struct TaskChannel<const C: u8> {}
251
252impl<const C: u8> TaskChannel<C> {
253    // In theory we could have multiple pins assigned to the same task. Not sure how
254    // useful that would be. If we want to support it, the easiest would be
255    // to offer additional functions like `set2`, `set3` etc. where the
256    // number is the pin-count
257
258    /// Task to set a high level
259    pub fn set<'d>(
260        self,
261        pin: impl Peripheral<P = impl Into<OutputSignal>> + 'd,
262        pin_config: OutputConfig,
263    ) -> Task<'d> {
264        self.into_task(pin, pin_config, TaskKind::Set)
265    }
266
267    /// Task to set a low level
268    pub fn clear<'d>(
269        self,
270        pin: impl Peripheral<P = impl Into<OutputSignal>> + 'd,
271        pin_config: OutputConfig,
272    ) -> Task<'d> {
273        self.into_task(pin, pin_config, TaskKind::Clear)
274    }
275
276    /// Task to toggle the level
277    pub fn toggle<'d>(
278        self,
279        pin: impl Peripheral<P = impl Into<OutputSignal>> + 'd,
280        pin_config: OutputConfig,
281    ) -> Task<'d> {
282        self.into_task(pin, pin_config, TaskKind::Toggle)
283    }
284
285    fn into_task<'d>(
286        self,
287        pin: impl Peripheral<P = impl Into<OutputSignal>> + 'd,
288        pin_config: OutputConfig,
289        kind: TaskKind,
290    ) -> Task<'d> {
291        crate::into_mapped_ref!(pin);
292
293        pin.set_output_high(pin_config.initial_state.into());
294        if pin_config.open_drain {
295            pin.pull_direction(pin_config.pull);
296            pin.set_to_open_drain_output();
297        } else {
298            pin.set_to_push_pull_output();
299        }
300
301        enable_task_channel(C, pin.number());
302        Task {
303            id: kind.id() + C,
304            _pin: PhantomData,
305        }
306    }
307}
308
309#[derive(Clone, Copy, Debug)]
310#[cfg_attr(feature = "defmt", derive(defmt::Format))]
311enum TaskKind {
312    Set,
313    Clear,
314    Toggle,
315}
316
317impl TaskKind {
318    fn id(&self) -> u8 {
319        match self {
320            TaskKind::Set => 1,
321            TaskKind::Clear => 9,
322            TaskKind::Toggle => 17,
323        }
324    }
325}
326
327/// Task for set operation
328pub struct Task<'d> {
329    _pin: PhantomData<&'d mut ()>,
330    id: u8,
331}
332
333impl private::Sealed for Task<'_> {}
334
335impl crate::etm::EtmTask for Task<'_> {
336    fn id(&self) -> u8 {
337        self.id
338    }
339}
340
341fn enable_task_channel(channel: u8, pin: u8) {
342    let gpio_sd = GPIO_SD::regs();
343    let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) };
344    let shift = 8 * (pin as usize % 4);
345    // bit 0 = en, bit 1-3 = channel
346    unsafe {
347        ptr.write_volatile(
348            ptr.read_volatile() & !(0xf << shift)
349                | (1 << shift)
350                | ((channel as u32) << (shift + 1)),
351        );
352    }
353}
354
355fn enable_event_channel(channel: u8, pin: u8) {
356    let gpio_sd = GPIO_SD::regs();
357    gpio_sd
358        .etm_event_ch_cfg(channel as usize)
359        .modify(|_, w| w.event_en().clear_bit());
360    gpio_sd
361        .etm_event_ch_cfg(channel as usize)
362        .modify(|_, w| unsafe { w.event_sel().bits(pin) });
363    gpio_sd
364        .etm_event_ch_cfg(channel as usize)
365        .modify(|_, w| w.event_en().set_bit());
366}