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 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//!     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        Level,
59        Pull,
60        interconnect::{InputSignal, OutputSignal},
61    },
62    peripherals::GPIO_SD,
63    private,
64};
65
66/// All the GPIO ETM channels
67#[non_exhaustive]
68pub struct Channels<'d> {
69    _gpio_sd: GPIO_SD<'d>,
70    /// Task channel 0 for triggering GPIO tasks.
71    pub channel0_task: TaskChannel<0>,
72    /// Event channel 0 for handling GPIO events.
73    pub channel0_event: EventChannel<0>,
74    /// Task channel 1 for triggering GPIO tasks.
75    pub channel1_task: TaskChannel<1>,
76    /// Event channel 1 for handling GPIO events.
77    pub channel1_event: EventChannel<1>,
78    /// Task channel 2 for triggering GPIO tasks.
79    pub channel2_task: TaskChannel<2>,
80    /// Event channel 2 for handling GPIO events.
81    pub channel2_event: EventChannel<2>,
82    /// Task channel 3 for triggering GPIO tasks.
83    pub channel3_task: TaskChannel<3>,
84    /// Event channel 3 for handling GPIO events.
85    pub channel3_event: EventChannel<3>,
86    /// Task channel 4 for triggering GPIO tasks.
87    pub channel4_task: TaskChannel<4>,
88    /// Event channel 4 for handling GPIO events.
89    pub channel4_event: EventChannel<4>,
90    /// Task channel 5 for triggering GPIO tasks.
91    pub channel5_task: TaskChannel<5>,
92    /// Event channel 5 for handling GPIO events.
93    pub channel5_event: EventChannel<5>,
94    /// Task channel 6 for triggering GPIO tasks.
95    pub channel6_task: TaskChannel<6>,
96    /// Event channel 6 for handling GPIO events.
97    pub channel6_event: EventChannel<6>,
98    /// Task channel 7 for triggering GPIO tasks.
99    pub channel7_task: TaskChannel<7>,
100    /// Event channel 7 for handling GPIO events.
101    pub channel7_event: EventChannel<7>,
102}
103
104impl<'d> Channels<'d> {
105    /// Create a new instance
106    pub fn new(peripheral: GPIO_SD<'d>) -> Self {
107        Self {
108            _gpio_sd: peripheral,
109            channel0_task: TaskChannel {},
110            channel0_event: EventChannel {},
111            channel1_task: TaskChannel {},
112            channel1_event: EventChannel {},
113            channel2_task: TaskChannel {},
114            channel2_event: EventChannel {},
115            channel3_task: TaskChannel {},
116            channel3_event: EventChannel {},
117            channel4_task: TaskChannel {},
118            channel4_event: EventChannel {},
119            channel5_task: TaskChannel {},
120            channel5_event: EventChannel {},
121            channel6_task: TaskChannel {},
122            channel6_event: EventChannel {},
123            channel7_task: TaskChannel {},
124            channel7_event: EventChannel {},
125        }
126    }
127}
128
129/// Configuration for an ETM controlled GPIO input pin
130// TODO: remove this
131#[derive(Clone, Copy, Debug)]
132#[cfg_attr(feature = "defmt", derive(defmt::Format))]
133pub struct InputConfig {
134    /// Configuration for the internal pull-up resistors
135    pub pull: Pull,
136}
137
138impl Default for InputConfig {
139    fn default() -> Self {
140        Self { pull: Pull::None }
141    }
142}
143
144/// An ETM controlled GPIO event
145pub struct EventChannel<const C: u8> {}
146
147impl<const C: u8> EventChannel<C> {
148    /// Trigger at rising edge
149    pub fn rising_edge<'d>(
150        self,
151        pin: impl Into<InputSignal<'d>>,
152        pin_config: InputConfig,
153    ) -> Event<'d> {
154        self.into_event(pin, pin_config, EventKind::Rising)
155    }
156
157    /// Trigger at falling edge
158    pub fn falling_edge<'d>(
159        self,
160        pin: impl Into<InputSignal<'d>>,
161        pin_config: InputConfig,
162    ) -> Event<'d> {
163        self.into_event(pin, pin_config, EventKind::Falling)
164    }
165
166    /// Trigger at any edge
167    pub fn any_edge<'d>(
168        self,
169        pin: impl Into<InputSignal<'d>>,
170        pin_config: InputConfig,
171    ) -> Event<'d> {
172        self.into_event(pin, pin_config, EventKind::Any)
173    }
174
175    fn into_event<'d>(
176        self,
177        pin: impl Into<InputSignal<'d>>,
178        pin_config: InputConfig,
179        kind: EventKind,
180    ) -> Event<'d> {
181        let pin = pin.into();
182        if let Some(number) = pin.gpio_number() {
183            pin.apply_input_config(&crate::gpio::InputConfig::default().with_pull(pin_config.pull));
184            pin.set_input_enable(true);
185
186            enable_event_channel(C, number);
187        }
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>(self, pin: impl Into<OutputSignal<'d>>, pin_config: OutputConfig) -> Task<'d> {
260        self.into_task(pin, pin_config, TaskKind::Set)
261    }
262
263    /// Task to set a low level
264    pub fn clear<'d>(self, pin: impl Into<OutputSignal<'d>>, pin_config: OutputConfig) -> Task<'d> {
265        self.into_task(pin, pin_config, TaskKind::Clear)
266    }
267
268    /// Task to toggle the level
269    pub fn toggle<'d>(
270        self,
271        pin: impl Into<OutputSignal<'d>>,
272        pin_config: OutputConfig,
273    ) -> Task<'d> {
274        self.into_task(pin, pin_config, TaskKind::Toggle)
275    }
276
277    fn into_task<'d>(
278        self,
279        pin: impl Into<OutputSignal<'d>>,
280        pin_config: OutputConfig,
281        kind: TaskKind,
282    ) -> Task<'d> {
283        let pin = pin.into();
284
285        if let Some(number) = pin.gpio_number() {
286            let config = if pin_config.open_drain {
287                super::OutputConfig::default()
288                    .with_drive_mode(super::DriveMode::OpenDrain)
289                    .with_pull(pin_config.pull)
290            } else {
291                super::OutputConfig::default()
292            };
293
294            pin.set_output_high(pin_config.initial_state.into());
295            pin.apply_output_config(&config);
296            pin.set_output_enable(true);
297
298            // TODO: what should we do if the user passes a Level/NoPin?
299            enable_task_channel(C, number);
300        }
301        Task {
302            id: kind.id() + C,
303            _pin: PhantomData,
304        }
305    }
306}
307
308#[derive(Clone, Copy, Debug)]
309#[cfg_attr(feature = "defmt", derive(defmt::Format))]
310enum TaskKind {
311    Set,
312    Clear,
313    Toggle,
314}
315
316impl TaskKind {
317    fn id(&self) -> u8 {
318        match self {
319            TaskKind::Set => 1,
320            TaskKind::Clear => 9,
321            TaskKind::Toggle => 17,
322        }
323    }
324}
325
326/// Task for set operation
327pub struct Task<'d> {
328    _pin: PhantomData<&'d mut ()>,
329    id: u8,
330}
331
332impl private::Sealed for Task<'_> {}
333
334impl crate::etm::EtmTask for Task<'_> {
335    fn id(&self) -> u8 {
336        self.id
337    }
338}
339
340fn enable_task_channel(channel: u8, pin: u8) {
341    let gpio_sd = GPIO_SD::regs();
342    let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) };
343    let shift = 8 * (pin as usize % 4);
344    // bit 0 = en, bit 1-3 = channel
345    unsafe {
346        ptr.write_volatile(
347            ptr.read_volatile() & !(0xf << shift)
348                | (1 << shift)
349                | ((channel as u32) << (shift + 1)),
350        );
351    }
352}
353
354fn enable_event_channel(channel: u8, pin: u8) {
355    let gpio_sd = GPIO_SD::regs();
356    gpio_sd
357        .etm_event_ch_cfg(channel as usize)
358        .modify(|_, w| w.event_en().clear_bit());
359    gpio_sd
360        .etm_event_ch_cfg(channel as usize)
361        .modify(|_, w| unsafe { w.event_sel().bits(pin) });
362    gpio_sd
363        .etm_event_ch_cfg(channel as usize)
364        .modify(|_, w| w.event_en().set_bit());
365}