1
#![cfg_attr(docsrs, procmacros::doc_replace)]
2
//! Dedicated GPIO access.
3
//!
4
//! This module implements fast GPIO access using special-purpose CPU features.
5
//! Dedicated GPIO access works by connecting peripheral signals between the CPU
6
//! and the GPIO pins, instead of using the GPIO registers via the peripheral bus.
7
//!
8
//! To use dedicated GPIOs, first you must obtain the channel objects. The channels
9
//! are created by calling the [`DedicatedGpio::new`] function. The fields of the
10
//! [`DedicatedGpio`] struct represent the channels. The channels are [`DedicatedGpioChannel`]
11
//! objects, which by default represent both input and output channels. The channels can be split
12
//! into [`DedicatedGpioInputChannel`] and [`DedicatedGpioOutputChannel`] halves, or may be
13
//! used unsplit.
14
#![doc = concat!(r#"
15

            
16
 > "#, chip!() , r#" specific: There are "#, property!("dedicated_gpio.channel_count", str) , r#" dedicated GPIO channels available.
17

            
18
 "#)]
19
//! Next, configure the pins you want to use as normal GPIO pin drivers. Then you can wrap them
20
//! into the dedicated GPIO drivers:
21
//! - [`DedicatedGpioInput`]
22
//! - [`DedicatedGpioOutput`]
23
//! - [`DedicatedGpioFlex`]
24
//!
25
//! The drivers can take channels and pins by value or by reference. Pass these objects by reference
26
//! if you plan on reusing them again, after dropping the dedicated driver.
27
//!
28
//! [`DedicatedGpioOutput`] can drive any number of GPIO pins. Each connected pin follows the
29
//! current dedicated GPIO channel level immediately.
30
//!
31
//! ## Bundles
32
//!
33
//! If you need to read or update multiple channels together, you can use the bundle helpers:
34
//! - [`DedicatedGpioInputBundle`]
35
//! - [`DedicatedGpioOutputBundle`]
36
//! - [`DedicatedGpioFlexBundle`]
37
//!
38
//! Bundles are lightweight objects that precompute a channel mask from one or more drivers,
39
//! allowing multi-channel operations with a single low-level read/write.
40
//!
41
//! ## Low-level functions
42
//!
43
//! This module also exposes low-level helpers for direct, channel-bitmask-based access:
44
//! - [`write_ll`]: write output levels for a selected set of channels in one operation
45
//! - [`read_all_ll`]: read the current input levels of all channels
46
//! - [`output_levels_ll`]: read the current output levels of all channels
47
//!
48
//! These functions operate purely on channel bitmasks (bit 0 -> channel 0, bit 1 -> channel 1, ...)
49
//! and do not track pin configuration. Prefer the higher-level drivers and bundles unless you
50
//! specifically need the lowest overhead.
51

            
52
#![cfg_attr(
53
    multi_core,
54
    doc = r#"
55

            
56
<section class="warning">
57
Dedicated GPIOs are tied to CPU cores and a driver must only be used on the core that has created it.
58
Do not send the drivers to another core, either directly, or indirectly via a thread that is not pinned to a core.
59
</section>
60
"#
61
)]
62
//! ## Examples
63
//! ### sharing drivers across multiple bundles
64
//!
65
//! The same driver can be borrowed by multiple bundles at the same time.
66
//!
67
//! In this example, we build two *input* bundles:
68
//! - `bundle_a` reads channels 0, 1, 2
69
//! - `bundle_b` reads channels 0, 2, 4
70
//!
71
//! Both bundles share the same `in0` and `in2` drivers, and we use
72
//! [`DedicatedGpioInputBundle::all_high`] to check if all channels in each bundle are currently
73
//! high.
74
//!
75
//! ```rust, no_run
76
//! # {before_snippet}
77
//! use esp_hal::gpio::{
78
//!     Input,
79
//!     InputConfig,
80
//!     dedicated::{DedicatedGpio, DedicatedGpioInput, DedicatedGpioInputBundle},
81
//! };
82
//!
83
//! // Create channels.
84
//! let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
85
//!
86
//! // Configure GPIO inputs
87
//! let p0 = Input::new(peripherals.GPIO0, InputConfig::default());
88
//! let p1 = Input::new(peripherals.GPIO1, InputConfig::default());
89
//! let p2 = Input::new(peripherals.GPIO2, InputConfig::default());
90
//! let p4 = Input::new(peripherals.GPIO4, InputConfig::default());
91
//!
92
//! let in0 = DedicatedGpioInput::new(channels.channel0.input, p0);
93
//! let in1 = DedicatedGpioInput::new(channels.channel1.input, p1);
94
//! let in2 = DedicatedGpioInput::new(channels.channel2.input, p2);
95
//! let in4 = DedicatedGpioInput::new(channels.channel4.input, p4);
96
//!
97
//! // Bundle A reads channels 0, 1, 2.
98
//! let mut bundle_a = DedicatedGpioInputBundle::new();
99
//! bundle_a
100
//!     .enable_input(&in0)
101
//!     .enable_input(&in1)
102
//!     .enable_input(&in2);
103
//!
104
//! // Bundle B reads channels 0, 2, 4.
105
//! // Note: `in0` and `in2` are *shared* with bundle A.
106
//! let mut bundle_b = DedicatedGpioInputBundle::new();
107
//! bundle_b
108
//!     .enable_input(&in0)
109
//!     .enable_input(&in2)
110
//!     .enable_input(&in4);
111
//!
112
//! // Check whether all channels in each bundle are currently high.
113
//! let a_all_high = bundle_a.all_high(); // true if ch0, ch1, ch2 are all high
114
//! let b_all_high = bundle_b.all_high(); // true if ch0, ch2, ch4 are all high
115
//!
116
//! # {after_snippet}
117
//! ```
118
//!
119
//! This pattern is useful when different subsystems want different views of the same set of
120
//! input channels without duplicating driver setup.
121

            
122
use core::{convert::Infallible, marker::PhantomData};
123

            
124
use procmacros::doc_replace;
125
use strum::EnumCount as _;
126

            
127
#[cfg_attr(dedicated_gpio_version = "esp32s2", path = "low_level/esp32s2.rs")]
128
#[cfg_attr(dedicated_gpio_version = "esp32s3", path = "low_level/esp32s3.rs")]
129
#[cfg_attr(dedicated_gpio_version = "riscv_v1", path = "low_level/riscv_v1.rs")]
130
#[cfg_attr(dedicated_gpio_version = "riscv_v2", path = "low_level/riscv_v2.rs")]
131
mod low_level;
132

            
133
use crate::{
134
    gpio::{
135
        AnyPin,
136
        GpioBank,
137
        InputSignal,
138
        Level,
139
        OutputSignal,
140
        interconnect::{PeripheralOutput, PeripheralSignal},
141
    },
142
    peripherals::GPIO_DEDICATED,
143
    private::Sealed,
144
    system::Cpu,
145
};
146

            
147
for_each_dedicated_gpio!(
148
    (channels $(($ch:literal)),*) => {
149
        paste::paste!(
150
            /// Dedicated GPIO channels.
151
            pub struct DedicatedGpio<'lt> {
152
                $(
153
                    #[doc = concat!("Channel ", stringify!($ch))]
154
                    pub [< channel $ch >]: DedicatedGpioChannel<'lt, $ch>,
155
                )*
156
            }
157
        );
158

            
159
        impl<'lt> DedicatedGpio<'lt> {
160
            /// Initializes the dedicated GPIO features and returns the channels.
161
            pub fn new(_: GPIO_DEDICATED<'lt>) -> Self {
162
                #[cfg(dedicated_gpio_needs_initialization)]
163
                Self::initialize();
164

            
165
                paste::paste!(
166
                    Self {
167
                        $([<channel $ch>]: DedicatedGpioChannel {
168
                            input: DedicatedGpioInputChannel { _marker: PhantomData },
169
                            output: DedicatedGpioOutputChannel { _marker: PhantomData },
170
                        },)*
171
                    }
172
                )
173
            }
174
        }
175
    };
176
);
177

            
178
impl DedicatedGpio<'_> {
179
    #[cfg(dedicated_gpio_needs_initialization)]
180
    fn initialize() {
181
        use crate::system::{Peripheral, PeripheralClockControl};
182
        if PeripheralClockControl::enable(Peripheral::DedicatedGpio) {
183
            PeripheralClockControl::reset(Peripheral::DedicatedGpio);
184
        } else {
185
            // Refcount was more than 0. Decrement to avoid overflow because we don't handle
186
            // dropping the driver.
187
            PeripheralClockControl::disable(Peripheral::DedicatedGpio);
188
        }
189

            
190
        low_level::initialize();
191
    }
192
}
193

            
194
// Channel traits allow us to implement DedicatedInputOutput, and users can choose to work with 8 IO
195
// channels for inputs/outputs instead of 8+8.
196

            
197
/// Marker trait for dedicated GPIO input channels.
198
pub trait InputChannel: Sealed {
199
    #[doc(hidden)]
200
    const CH: u8;
201

            
202
    #[doc(hidden)]
203
    fn mask(&self) -> u32 {
204
        1 << Self::CH
205
    }
206

            
207
    #[doc(hidden)]
208
    fn input_signal(&self) -> InputSignal {
209
        for_each_dedicated_gpio!(
210
            (signals $( ($cpu:literal, $signal_idx:literal, $signal_name:ident) ),*) => {
211
                const CHANNEL_COUNT: usize = property!("dedicated_gpio.channel_count");
212
                const SIGNALS: [[InputSignal; CHANNEL_COUNT]; Cpu::COUNT] = const {
213
                    let mut signals = [[core::mem::MaybeUninit::uninit(); CHANNEL_COUNT]; Cpu::COUNT];
214

            
215
                    $(
216
                        signals[$cpu][$signal_idx].write(InputSignal::$signal_name);
217
                    )*
218

            
219
                    unsafe { core::mem::transmute(signals) }
220
                };
221
            };
222
        );
223
        SIGNALS[Cpu::current() as usize][Self::CH as usize]
224
    }
225
}
226

            
227
/// Marker trait for dedicated GPIO output channels.
228
pub trait OutputChannel: Sealed {
229
    #[doc(hidden)]
230
    const CH: u8;
231

            
232
    #[doc(hidden)]
233
    fn mask(&self) -> u32 {
234
        1 << Self::CH
235
    }
236

            
237
    #[doc(hidden)]
238
    fn output_signal(&self) -> OutputSignal {
239
        for_each_dedicated_gpio!(
240
            (signals $( ($cpu:literal, $signal_idx:literal, $signal_name:ident) ),* ) => {
241
                const CHANNEL_COUNT: usize = property!("dedicated_gpio.channel_count");
242
                const SIGNALS: [[OutputSignal; CHANNEL_COUNT]; Cpu::COUNT] = const {
243
                    let mut signals = [[core::mem::MaybeUninit::uninit(); CHANNEL_COUNT]; Cpu::COUNT];
244

            
245
                    $(
246
                        signals[$cpu][$signal_idx].write(OutputSignal::$signal_name);
247
                    )*
248

            
249
                    unsafe { core::mem::transmute(signals) }
250
                };
251
            };
252
        );
253
        SIGNALS[Cpu::current() as usize][Self::CH as usize]
254
    }
255
}
256

            
257
// Driver traits, to allow working with Flex. These are implemented for both owned types and
258
// references, so that the user is not forced to give up the pin driver, and that we're not forced
259
// to figure out Drop semantics with `reborrow`, and the pins aren't remembered by the dedicated
260
// drivers anyway.
261
// We can't use the interconnect traits, because we want them to be implemented for references, too.
262

            
263
/// Marker trait for GPIO input drivers.
264
pub trait InputDriver: Sealed {
265
    #[doc(hidden)]
266
    fn pin(&self) -> u8;
267

            
268
    /// Connects the pin's peripheral input to an input signal source.
269
    #[doc(hidden)]
270
    fn set_input_connection(&self, signal: InputSignal);
271
}
272

            
273
#[instability::unstable]
274
impl InputDriver for super::Input<'_> {
275
    fn pin(&self) -> u8 {
276
        self.pin.pin.pin
277
    }
278

            
279
4
    fn set_input_connection(&self, signal: InputSignal) {
280
        self.connect_input_to_peripheral(signal);
281
    }
282
}
283

            
284
#[instability::unstable]
285
impl InputDriver for &mut super::Input<'_> {
286
    fn pin(&self) -> u8 {
287
        self.pin.pin.pin
288
    }
289

            
290
    fn set_input_connection(&self, signal: InputSignal) {
291
        self.connect_input_to_peripheral(signal);
292
    }
293
}
294

            
295
#[instability::unstable]
296
impl InputDriver for super::Flex<'_> {
297
    fn pin(&self) -> u8 {
298
        self.pin.pin
299
    }
300

            
301
    fn set_input_connection(&self, signal: InputSignal) {
302
        self.connect_input_to_peripheral(signal);
303
    }
304
}
305

            
306
#[instability::unstable]
307
impl InputDriver for &mut super::Flex<'_> {
308
    fn pin(&self) -> u8 {
309
        self.pin.pin
310
    }
311

            
312
    fn set_input_connection(&self, signal: InputSignal) {
313
        self.connect_input_to_peripheral(signal);
314
    }
315
}
316

            
317
/// Marker trait for GPIO output drivers.
318
pub trait OutputDriver: Sealed {
319
    #[doc(hidden)]
320
    fn pin(&self) -> u8;
321

            
322
    #[doc(hidden)]
323
    fn set_output_connection(&self, signal: OutputSignal);
324
}
325

            
326
#[instability::unstable]
327
impl OutputDriver for super::Output<'_> {
328
    fn pin(&self) -> u8 {
329
        self.pin.pin.pin
330
    }
331

            
332
4
    fn set_output_connection(&self, signal: OutputSignal) {
333
4
        self.pin.pin.connect_peripheral_to_output(signal);
334
4
    }
335
}
336

            
337
#[instability::unstable]
338
impl OutputDriver for &mut super::Output<'_> {
339
    fn pin(&self) -> u8 {
340
        self.pin.pin.pin
341
    }
342

            
343
    fn set_output_connection(&self, signal: OutputSignal) {
344
        self.pin.pin.connect_peripheral_to_output(signal);
345
    }
346
}
347

            
348
#[instability::unstable]
349
impl OutputDriver for super::Flex<'_> {
350
    fn pin(&self) -> u8 {
351
        self.pin.pin
352
    }
353

            
354
    fn set_output_connection(&self, signal: OutputSignal) {
355
        self.pin.connect_peripheral_to_output(signal);
356
    }
357
}
358

            
359
#[instability::unstable]
360
impl OutputDriver for &mut super::Flex<'_> {
361
    fn pin(&self) -> u8 {
362
        self.pin.pin
363
    }
364

            
365
    fn set_output_connection(&self, signal: OutputSignal) {
366
        self.pin.connect_peripheral_to_output(signal);
367
    }
368
}
369

            
370
/// A single dedicated GPIO channel, both input and output.
371
///
372
/// The channel can be split by moving its fields into separate input/output channel variables.
373
pub struct DedicatedGpioChannel<'lt, const CH: u8> {
374
    /// Channel input
375
    pub input: DedicatedGpioInputChannel<'lt, CH>,
376

            
377
    /// Channel output
378
    pub output: DedicatedGpioOutputChannel<'lt, CH>,
379
}
380

            
381
impl<const CH: u8> Sealed for DedicatedGpioChannel<'_, CH> {}
382
impl<const CH: u8> Sealed for &mut DedicatedGpioChannel<'_, CH> {}
383

            
384
impl<const CH: u8> DedicatedGpioChannel<'_, CH> {
385
    /// Conjures a new dedicated GPIO channel out of thin air.
386
    ///
387
    /// # Safety
388
    ///
389
    /// The [`DedicatedGpio`] struct must be initialized before this function is called. There
390
    /// should only be one reference to the channel in use at one time.
391
    pub unsafe fn steal() -> Self {
392
        unsafe {
393
            Self {
394
                input: DedicatedGpioInputChannel::steal(),
395
                output: DedicatedGpioOutputChannel::steal(),
396
            }
397
        }
398
    }
399
}
400

            
401
impl<const CH: u8> InputChannel for DedicatedGpioChannel<'_, CH> {
402
    const CH: u8 = CH;
403
}
404

            
405
impl<const CH: u8> OutputChannel for DedicatedGpioChannel<'_, CH> {
406
    const CH: u8 = CH;
407
}
408

            
409
impl<const CH: u8> InputChannel for &mut DedicatedGpioChannel<'_, CH> {
410
    const CH: u8 = CH;
411
}
412

            
413
impl<const CH: u8> OutputChannel for &mut DedicatedGpioChannel<'_, CH> {
414
    const CH: u8 = CH;
415
}
416

            
417
/// A single dedicated GPIO input channel.
418
pub struct DedicatedGpioInputChannel<'lt, const CH: u8> {
419
    _marker: PhantomData<&'lt mut ()>,
420
}
421

            
422
impl<const CH: u8> Sealed for DedicatedGpioInputChannel<'_, CH> {}
423
impl<const CH: u8> Sealed for &mut DedicatedGpioInputChannel<'_, CH> {}
424

            
425
impl<const CH: u8> DedicatedGpioInputChannel<'_, CH> {
426
    /// Conjures a new dedicated GPIO input channel out of thin air.
427
    ///
428
    /// # Safety
429
    ///
430
    /// The [`DedicatedGpio`] struct must be initialized before this function is called. There
431
    /// should only be one reference to the channel in use at one time.
432
    pub unsafe fn steal() -> Self {
433
        Self {
434
            _marker: PhantomData,
435
        }
436
    }
437
}
438

            
439
impl<const CH: u8> InputChannel for DedicatedGpioInputChannel<'_, CH> {
440
    const CH: u8 = CH;
441
}
442

            
443
impl<const CH: u8> InputChannel for &mut DedicatedGpioInputChannel<'_, CH> {
444
    const CH: u8 = CH;
445
}
446

            
447
/// A single dedicated GPIO output channel.
448
pub struct DedicatedGpioOutputChannel<'lt, const CH: u8> {
449
    _marker: PhantomData<&'lt mut ()>,
450
}
451

            
452
impl<const CH: u8> Sealed for DedicatedGpioOutputChannel<'_, CH> {}
453
impl<const CH: u8> Sealed for &mut DedicatedGpioOutputChannel<'_, CH> {}
454

            
455
impl<const CH: u8> DedicatedGpioOutputChannel<'_, CH> {
456
    /// Conjures a new dedicated GPIO output channel out of thin air.
457
    ///
458
    /// # Safety
459
    ///
460
    /// The [`DedicatedGpio`] struct must be initialized before this function is called. There
461
    /// should only be one reference to the channel in use at one time.
462
    pub unsafe fn steal() -> Self {
463
        Self {
464
            _marker: PhantomData,
465
        }
466
    }
467
}
468

            
469
impl<const CH: u8> OutputChannel for DedicatedGpioOutputChannel<'_, CH> {
470
    const CH: u8 = CH;
471
}
472

            
473
impl<const CH: u8> OutputChannel for &mut DedicatedGpioOutputChannel<'_, CH> {
474
    const CH: u8 = CH;
475
}
476

            
477
#[doc_replace]
478
/// A dedicated GPIO input driver.
479
#[cfg_attr(
480
    multi_core,
481
    doc = r#"
482

            
483
<section class="warning">
484
The driver must only be used on the core that has created it. Do not send the driver to
485
another core, either directly, or indirectly via a thread that is not pinned to a core.
486
</section>
487
"#
488
)]
489
#[doc = ""]
490
/// # Examples
491
///
492
/// ```rust, no_run
493
/// # {before_snippet}
494
/// #
495
/// use esp_hal::gpio::{
496
///     Input,
497
///     InputConfig,
498
///     Level,
499
///     dedicated::{DedicatedGpio, DedicatedGpioInput},
500
/// };
501
///
502
/// // Create an input pin driver:
503
/// let input = Input::new(peripherals.GPIO0, InputConfig::default());
504
///
505
/// // Create a dedicated GPIO driver:
506
/// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
507
/// let mut dedicated_input = DedicatedGpioInput::new(channels.channel0, input);
508
///
509
/// // Now you can use the pin:
510
/// let level = dedicated_input.level();
511
/// #
512
/// # {after_snippet}
513
/// ```
514
pub struct DedicatedGpioInput<'lt> {
515
    _marker: PhantomData<&'lt mut ()>,
516
    mask: u32,
517
    #[cfg(all(debug_assertions, multi_core))]
518
    core: Cpu,
519
}
520

            
521
impl<'lt> DedicatedGpioInput<'lt> {
522
    /// Creates a new dedicated GPIO input driver.
523
    pub fn new<CH, P>(channel: CH, pin: P) -> Self
524
    where
525
        CH: InputChannel + 'lt,
526
        P: InputDriver + 'lt,
527
    {
528
1
        pin.set_input_connection(channel.input_signal());
529
        Self {
530
            mask: channel.mask(),
531
            _marker: PhantomData,
532
            #[cfg(all(debug_assertions, multi_core))]
533
            core: Cpu::current(),
534
        }
535
    }
536

            
537
    /// Reads the current state of the GPIO pins.
538
    #[inline(always)]
539
    pub fn level(&self) -> Level {
540
        #[cfg(all(debug_assertions, multi_core))]
541
        debug_assert_eq!(
542
            self.core,
543
            Cpu::current(),
544
            "Dedicated GPIO used on a different CPU core than it was created on"
545
        );
546

            
547
        let bits = low_level::read_in();
548
1
        Level::from(bits & self.mask != 0)
549
    }
550
}
551

            
552
impl embedded_hal::digital::ErrorType for DedicatedGpioInput<'_> {
553
    type Error = Infallible;
554
}
555

            
556
impl embedded_hal::digital::InputPin for DedicatedGpioInput<'_> {
557
    #[inline(always)]
558
    fn is_high(&mut self) -> Result<bool, Self::Error> {
559
        Ok(self.level() == Level::High)
560
    }
561

            
562
    #[inline(always)]
563
    fn is_low(&mut self) -> Result<bool, Self::Error> {
564
        Ok(self.level() == Level::Low)
565
    }
566
}
567

            
568
#[doc_replace]
569
/// A dedicated GPIO output driver.
570
///
571
/// Due to how the hardware works, [`DedicatedGpioOutput`] can drive any number of GPIO pins. Use
572
/// [`DedicatedGpioOutput::new`] to create a driver, then [`DedicatedGpioOutput::with_pin`] to add
573
/// output drivers.
574
#[cfg_attr(
575
    multi_core,
576
    doc = r#"
577

            
578
<section class="warning">
579
The driver must only be used on the core that has created it. Do not send the driver to
580
another core, either directly, or indirectly via a thread that is not pinned to a core.
581
</section>
582
"#
583
)]
584
#[doc = ""]
585
/// # Examples
586
///
587
/// ```rust, no_run
588
/// # {before_snippet}
589
/// #
590
/// use esp_hal::gpio::{
591
///     Level,
592
///     Output,
593
///     OutputConfig,
594
///     dedicated::{DedicatedGpio, DedicatedGpioOutput},
595
/// };
596
///
597
/// // Create a dedicated GPIO driver:
598
/// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
599
/// let dedicated_output = DedicatedGpioOutput::new(channels.channel0);
600
///
601
/// // Add any number of output pin drivers:
602
/// let output = Output::new(peripherals.GPIO0, Level::Low, OutputConfig::default());
603
/// let mut dedicated_output = dedicated_output.with_pin(output);
604
///
605
/// // Now you can use the pin.
606
/// dedicated_output.set_level(Level::High);
607
/// #
608
/// # {after_snippet}
609
/// ```
610
pub struct DedicatedGpioOutput<'lt> {
611
    _marker: PhantomData<&'lt mut ()>,
612
    mask: u32,
613
    signal: OutputSignal,
614
    contained_gpios: [u32; GpioBank::COUNT],
615
    #[cfg(all(debug_assertions, multi_core))]
616
    core: Cpu,
617
}
618

            
619
impl<'lt> DedicatedGpioOutput<'lt> {
620
    /// Creates a new dedicated GPIO output driver.
621
    ///
622
    /// Returns an empty driver. Output drivers must be added with the [`Self::with_pin`] method.
623
    pub fn new<CH>(channel: CH) -> Self
624
    where
625
        CH: OutputChannel + 'lt,
626
    {
627
        low_level::set_output_enabled(channel.mask(), true);
628
        Self {
629
            mask: channel.mask(),
630
            signal: channel.output_signal(),
631
            contained_gpios: [0; GpioBank::COUNT],
632
            _marker: PhantomData,
633
            #[cfg(all(debug_assertions, multi_core))]
634
            core: Cpu::current(),
635
        }
636
    }
637

            
638
    /// Adds a GPIO output pin to this dedicated GPIO output.
639
    ///
640
    /// A dedicated GPIO output can drive any number of GPIO pins. The driver releases the pins when
641
    /// it is dropped.
642
    ///
643
    /// The GPIO matrix routes the current output of the dedicated GPIO channel to the pin. The pin
644
    /// follows the channel level immediately. The GPIO output register of the pin does not change.
645
    pub fn with_pin(mut self, pin: impl OutputDriver + 'lt) -> Self {
646
1
        pin.set_output_connection(self.signal);
647

            
648
        let bank = pin.pin() / 32;
649
        let pin_in_bank = pin.pin() % 32;
650
1
        self.contained_gpios[bank as usize] |= 1 << pin_in_bank;
651

            
652
1
        self
653
    }
654

            
655
    /// Changes the current state of the GPIO pins.
656
    #[inline(always)]
657
    pub fn set_level(&mut self, level: Level) {
658
        #[cfg(all(debug_assertions, multi_core))]
659
        debug_assert_eq!(
660
            self.core,
661
            Cpu::current(),
662
            "Dedicated GPIO used on a different CPU core than it was created on"
663
        );
664

            
665
        if level == Level::High {
666
            low_level::write(self.mask, self.mask);
667
        } else {
668
            low_level::write(self.mask, 0);
669
        }
670
    }
671

            
672
    /// Returns the current output state of the GPIO pins.
673
    ///
674
    /// Returns [`Level::High`] if any of the GPIO pins are set high, otherwise [`Level::Low`].
675
    #[inline(always)]
676
    pub fn output_level(&self) -> Level {
677
        #[cfg(all(debug_assertions, multi_core))]
678
        debug_assert_eq!(
679
            self.core,
680
            Cpu::current(),
681
            "Dedicated GPIO used on a different CPU core than it was created on"
682
        );
683

            
684
1
        Level::from(low_level::read_out() & self.mask != 0)
685
    }
686
}
687

            
688
impl Drop for DedicatedGpioOutput<'_> {
689
    fn drop(&mut self) {
690
        #[cfg(all(debug_assertions, multi_core))]
691
        debug_assert_eq!(
692
            self.core,
693
            Cpu::current(),
694
            "Dedicated GPIO used on a different CPU core than it was created on"
695
        );
696

            
697
        // Set the contained GPIOs back to GPIO mode.
698
        for (bank, mut pins) in self.contained_gpios.into_iter().enumerate() {
699
            let bank_offset = bank as u8 * 32;
700
            while pins != 0 {
701
                let pin = pins.trailing_zeros() as u8;
702
                pins &= !(1 << pin);
703

            
704
                let gpio = unsafe { AnyPin::steal(bank_offset + pin) };
705
                gpio.disconnect_from_peripheral_output();
706
                gpio.connect_peripheral_to_output(OutputSignal::GPIO);
707
            }
708
        }
709
    }
710
}
711

            
712
impl embedded_hal::digital::ErrorType for DedicatedGpioOutput<'_> {
713
    type Error = Infallible;
714
}
715

            
716
impl embedded_hal::digital::OutputPin for DedicatedGpioOutput<'_> {
717
    #[inline(always)]
718
    fn set_low(&mut self) -> Result<(), Self::Error> {
719
        self.set_level(Level::Low);
720
        Ok(())
721
    }
722

            
723
    #[inline(always)]
724
    fn set_high(&mut self) -> Result<(), Self::Error> {
725
        self.set_level(Level::High);
726
        Ok(())
727
    }
728
}
729

            
730
#[doc_replace]
731
/// A dedicated GPIO input and output driver.
732
#[cfg_attr(
733
    xtensa,
734
    doc = r#"
735

            
736
On ESP32-S2 and ESP32-S3, the GPIO's output is always enabled.
737
"#
738
)]
739
#[cfg_attr(
740
    multi_core,
741
    doc = r#"
742

            
743
<section class="warning">
744
The driver must only be used on the core that has created it. Do not send the driver to
745
another core, either directly, or indirectly via a thread that is not pinned to a core.
746
</section>
747
"#
748
)]
749
#[doc = ""]
750
/// # Examples
751
///
752
/// ```rust, no_run
753
/// # {before_snippet}
754
/// #
755
/// use esp_hal::gpio::{
756
///     Flex,
757
///     Input,
758
///     Level,
759
///     dedicated::{DedicatedGpio, DedicatedGpioFlex},
760
/// };
761
///
762
/// // Create a pin driver:
763
/// let flex = Flex::new(peripherals.GPIO0);
764
///
765
/// // Create a dedicated GPIO driver:
766
/// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
767
/// let mut dedicated_io = DedicatedGpioFlex::new(channels.channel0, flex);
768
///
769
/// // Now you can use the pin:
770
/// let level = dedicated_io.level();
771
/// #
772
/// # {after_snippet}
773
/// ```
774
pub struct DedicatedGpioFlex<'lt> {
775
    _marker: PhantomData<&'lt mut ()>,
776
    mask: u32,
777
    pin: u8,
778
    #[cfg(all(debug_assertions, multi_core))]
779
    core: Cpu,
780
}
781

            
782
impl<'lt> DedicatedGpioFlex<'lt> {
783
    /// Creates a new dedicated GPIO input/output driver.
784
    pub fn new<CH, P>(channel: CH, pin: P) -> Self
785
    where
786
        CH: InputChannel + OutputChannel + 'lt,
787
        P: InputDriver + OutputDriver + 'lt,
788
    {
789
        pin.set_input_connection(channel.input_signal());
790
        pin.set_output_connection(channel.output_signal());
791
        Self {
792
            mask: <CH as OutputChannel>::mask(&channel),
793
            pin: <P as OutputDriver>::pin(&pin),
794
            _marker: PhantomData,
795
            #[cfg(all(debug_assertions, multi_core))]
796
            core: Cpu::current(),
797
        }
798
    }
799

            
800
    /// Enables or disables the output buffer of the GPIO pin.
801
    #[cfg(riscv)] // Xtensas always have the output enabled.
802
    pub fn set_output_enabled(&mut self, enabled: bool) {
803
        #[cfg(all(debug_assertions, multi_core))]
804
        debug_assert_eq!(
805
            self.core,
806
            Cpu::current(),
807
            "Dedicated GPIO used on a different CPU core than it was created on"
808
        );
809

            
810
        low_level::set_output_enabled(self.mask, enabled);
811
    }
812

            
813
    /// Changes the current state of the GPIO pin.
814
    #[inline(always)]
815
    pub fn set_level(&mut self, level: Level) {
816
        #[cfg(all(debug_assertions, multi_core))]
817
        debug_assert_eq!(
818
            self.core,
819
            Cpu::current(),
820
            "Dedicated GPIO used on a different CPU core than it was created on"
821
        );
822

            
823
        if level == Level::High {
824
            low_level::write(self.mask, self.mask);
825
        } else {
826
            low_level::write(self.mask, 0);
827
        }
828
    }
829

            
830
    /// Returns the current output state of the GPIO pin.
831
    #[inline(always)]
832
    pub fn output_level(&self) -> Level {
833
        #[cfg(all(debug_assertions, multi_core))]
834
        debug_assert_eq!(
835
            self.core,
836
            Cpu::current(),
837
            "Dedicated GPIO used on a different CPU core than it was created on"
838
        );
839

            
840
        Level::from(low_level::read_out() & self.mask != 0)
841
    }
842

            
843
    /// Reads the current state of the GPIO pins.
844
    #[inline(always)]
845
    pub fn level(&self) -> Level {
846
        #[cfg(all(debug_assertions, multi_core))]
847
        debug_assert_eq!(
848
            self.core,
849
            Cpu::current(),
850
            "Dedicated GPIO used on a different CPU core than it was created on"
851
        );
852

            
853
        let bits = low_level::read_in();
854
        Level::from(bits & self.mask != 0)
855
    }
856
}
857

            
858
impl Drop for DedicatedGpioFlex<'_> {
859
    fn drop(&mut self) {
860
        #[cfg(all(debug_assertions, multi_core))]
861
        debug_assert_eq!(
862
            self.core,
863
            Cpu::current(),
864
            "Dedicated GPIO used on a different CPU core than it was created on"
865
        );
866

            
867
        let gpio = unsafe { AnyPin::steal(self.pin) };
868
        gpio.disconnect_from_peripheral_output();
869
        gpio.connect_peripheral_to_output(OutputSignal::GPIO);
870
    }
871
}
872

            
873
#[doc_replace]
874
/// Low-level function to write any number of dedicated output channels at once.
875
///
876
/// The `mask` selects which channels to write to. Channel positions are in
877
/// order: bit 0 marks channel 0, bit 1 marks channel 1, etc.
878
///
879
/// `value` is the value to write to the selected channels. A 0 bit sets the
880
/// channel's output to low, a 1 bit sets it to high.
881
///
882
/// # Examples
883
///
884
/// ```rust,no_run
885
/// # {before_snippet}
886
/// #
887
/// use esp_hal::gpio::{
888
///     Level,
889
///     Output,
890
///     OutputConfig,
891
///     dedicated::{DedicatedGpio, DedicatedGpioOutput, write_ll},
892
/// };
893
///
894
/// // Create dedicated GPIO drivers:
895
/// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
896
/// let dedicated_output0 = DedicatedGpioOutput::new(channels.channel0);
897
/// let dedicated_output1 = DedicatedGpioOutput::new(channels.channel1);
898
/// let dedicated_output2 = DedicatedGpioOutput::new(channels.channel2);
899
///
900
/// // Configure output drivers:
901
/// let output0 = Output::new(peripherals.GPIO0, Level::Low, OutputConfig::default());
902
/// let output1 = Output::new(peripherals.GPIO1, Level::Low, OutputConfig::default());
903
/// let output2 = Output::new(peripherals.GPIO2, Level::Low, OutputConfig::default());
904
/// let output3 = Output::new(peripherals.GPIO3, Level::Low, OutputConfig::default());
905
///
906
/// // Configure dedicated output drivers:
907
/// let dedicated_output0 = dedicated_output0.with_pin(output0).with_pin(output1);
908
/// let dedicated_output1 = dedicated_output1.with_pin(output2);
909
/// let dedicated_output2 = dedicated_output2.with_pin(output3);
910
///
911
/// // Write all pins at once:
912
/// const MASK: u32 = 0b00000111;
913
/// const VALUE: u32 = 0b00000101; // Set GPIOs 0, 1, and 3 to high, 2 to low.
914
/// write_ll(MASK, VALUE);
915
/// #
916
/// # {after_snippet}
917
/// ```
918
#[cfg_attr(
919
    multi_core,
920
    doc = r#"
921

            
922
<section class="warning">
923
Only channels configured on the current CPU core can be used.
924
</section>
925
"#
926
)]
927
#[inline(always)]
928
pub fn write_ll(mask: u32, value: u32) {
929
    low_level::write(mask, value);
930
}
931

            
932
/// Low-level function to read the current state of all dedicated input channels.
933
///
934
/// The returned value is a bitmask where each bit represents the state of a
935
/// channel. Bit 0 represents channel 0, bit 1 represents channel 1, etc.
936
#[inline(always)]
937
pub fn read_all_ll() -> u32 {
938
    low_level::read_in()
939
}
940

            
941
/// Low-level function to read the current output levels of all dedicated GPIO channels.
942
///
943
/// The returned value is a bitmask where each bit represents the output level of a channel:
944
/// bit 0 -> channel 0, bit 1 -> channel 1, etc. A bit value of 1 means the channel output is high.
945
#[inline(always)]
946
pub fn output_levels_ll() -> u32 {
947
    low_level::read_out()
948
}
949

            
950
#[doc_replace]
951
/// A bundle of dedicated GPIO output drivers.
952
///
953
/// An output bundle precomputes a channel mask from one or more
954
/// [`DedicatedGpioOutput`] drivers. Enables updating multiple dedicated output channels with a
955
/// single low-level write.
956
///
957
/// Attaching a driver does **not** change any output state. Dropping the output
958
/// drivers still controls when the underlying GPIO pins are released back to
959
/// normal GPIO mode.
960
///
961
/// ## Relationship to pins
962
///
963
/// Dedicated output channels can be connected to multiple GPIO pins via
964
/// [`DedicatedGpioOutput::with_pin`]. The bundle operates on *channels*, not
965
/// individual pins: writing channel bits affects every pin connected to that
966
/// channel.
967
#[cfg_attr(
968
    multi_core,
969
    doc = r#"
970

            
971
<section class="warning">
972
Dedicated GPIO channels are tied to CPU cores. All output drivers attached to a bundle must be
973
configured on the same core, and the bundle must only be used on the core that created it.
974
</section>
975
"#
976
)]
977
#[doc = ""]
978
/// # Examples
979
///
980
/// ```rust, no_run
981
/// # {before_snippet}
982
/// #
983
/// use esp_hal::gpio::{
984
///     Level,
985
///     Output,
986
///     OutputConfig,
987
///     dedicated::{DedicatedGpio, DedicatedGpioOutput, DedicatedGpioOutputBundle},
988
/// };
989
///
990
/// // Create channels:
991
/// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
992
///
993
/// // Create output drivers for three channels:
994
/// let out0 = DedicatedGpioOutput::new(channels.channel0);
995
/// let out1 = DedicatedGpioOutput::new(channels.channel1);
996
/// let out2 = DedicatedGpioOutput::new(channels.channel2);
997
///
998
/// // Attach GPIO pins to the dedicated outputs (any number of pins per channel):
999
/// let p0 = Output::new(peripherals.GPIO0, Level::Low, OutputConfig::default());
/// let p1 = Output::new(peripherals.GPIO1, Level::Low, OutputConfig::default());
/// let p2 = Output::new(peripherals.GPIO2, Level::Low, OutputConfig::default());
///
/// let out0 = out0.with_pin(p0);
/// let out1 = out1.with_pin(p1);
/// let out2 = out2.with_pin(p2);
///
/// // Build a bundle that controls channels 0..=2:
/// let mut bundle = DedicatedGpioOutputBundle::new();
/// bundle
///     .enable_output(&out0)
///     .enable_output(&out1)
///     .enable_output(&out2);
///
/// // Set channel 0 and 2 high, keep channel 1 unchanged:
/// bundle.set_high(0b101);
///
/// // Now drive all channels in the bundle at once:
/// // - ch0 = 0
/// // - ch1 = 1
/// // - ch2 = 0
/// bundle.write_bits(0b010);
/// #
/// # {after_snippet}
/// ```
pub struct DedicatedGpioOutputBundle<'lt> {
    _marker: PhantomData<&'lt ()>,
    mask: u32,
    #[cfg(all(debug_assertions, multi_core))]
    core: Cpu,
}
impl<'lt> DedicatedGpioOutputBundle<'lt> {
    /// Creates a new, empty dedicated GPIO output bundle.
    ///
    /// A bundle is a *logical* grouping of one or more [`DedicatedGpioOutput`] drivers.
    /// Internally, it stores a precomputed channel mask (see [`Self::mask`]) which allows
    /// writing multiple dedicated GPIO channels efficiently.
    ///
    /// The returned bundle initially contains no channels. Add outputs using
    /// [`Self::enable_output`].
    ///
    /// Creating a bundle does **not** configure any hardware by itself.
    pub fn new() -> Self {
        Self {
            _marker: PhantomData,
            mask: 0,
            #[cfg(all(debug_assertions, multi_core))]
            core: Cpu::current(),
        }
    }
    #[doc_replace]
    /// Returns the channel mask of this bundle.
    ///
    /// Each bit corresponds to one dedicated GPIO channel:
    /// - bit 0 -> channel 0
    /// - bit 1 -> channel 1
    /// - ...
    ///
    /// A bit is set to 1 if that channel is currently included in the bundle.
    ///
    /// # Examples
    ///
    /// ```rust, no_run
    /// # {before_snippet}
    ///
    /// use esp_hal::gpio::{
    ///     Level,
    ///     Output,
    ///     OutputConfig,
    ///     dedicated::{DedicatedGpio, DedicatedGpioOutput, DedicatedGpioOutputBundle},
    /// };
    ///
    /// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
    /// let out0 = DedicatedGpioOutput::new(channels.channel0).with_pin(Output::new(
    ///     peripherals.GPIO0,
    ///     Level::Low,
    ///     OutputConfig::default(),
    /// ));
    /// let out2 = DedicatedGpioOutput::new(channels.channel2).with_pin(Output::new(
    ///     peripherals.GPIO2,
    ///     Level::Low,
    ///     OutputConfig::default(),
    /// ));
    /// let mut bundle = DedicatedGpioOutputBundle::new();
    /// bundle.enable_output(&out0).enable_output(&out2);
    ///
    /// let mask = bundle.mask(); // bit 0 -> ch0, bit 2 -> ch2, ...
    /// // For this bundle: mask == 0b0000_0101 (channels 0 and 2).
    ///
    /// # {after_snippet}
    /// ```
    #[inline(always)]
    pub fn mask(&self) -> u32 {
        self.mask
    }
    /// Attaches (enables) a dedicated output driver in this bundle. After enabling, the output
    /// channels of `out` can be controlled via this bundle.
    ///
    /// Logically borrows the provided [`DedicatedGpioOutput`] for the lifetime `'lt`.
    /// Multiple bundles may borrow the same output driver at the same time. Check examples in the
    /// module-level documentation for more.
    ///
    /// Does not change any GPIO output state.
    #[cfg_attr(
        multi_core,
        doc = r#"
<section class="warning">
All dedicated GPIO drivers in a bundle must be configured on the same core as the bundle itself
</section>
        "#
    )]
    pub fn enable_output<'d>(&mut self, out: &'lt DedicatedGpioOutput<'d>) -> &mut Self {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            out.core, self.core,
            "Trying to enable a dedicated GPIO driver configured on a different core from the bundle."
        );
        self.mask |= out.mask;
        self
    }
    /// Disables a dedicated output driver in this bundle.
    ///
    /// This updates the internal mask by clearing the channel bit(s) of `out`. After disabling,
    /// future *bundle* operations will no longer touch those channels.
    ///
    /// - This does **not** affect `out` itself. It only changes which channels future *bundle*
    ///   operations will touch.
    /// - This does **not** end the lifetime-based borrow of `out`. Even after disabling, `out`
    ///   still cannot be moved or dropped while this bundle exists.
    /// - Can be re-enabled later via [`Self::enable_output`]
    #[cfg_attr(
        multi_core,
        doc = r#"
<section class="warning">
Dedicated GPIO drivers must only be disabled if configured on the same core as the bundle itself.
</section>
"#
    )]
    pub fn disable_output<'d>(&mut self, out: &'lt DedicatedGpioOutput<'d>) -> &mut Self {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            out.core, self.core,
            "Trying to disable a dedicated GPIO driver configured on a different core from the bundle."
        );
        self.mask &= !out.mask;
        self
    }
    /// Sets selected channels **high**.
    ///
    /// For every bit set to 1 in `bits`, the corresponding dedicated output channel is driven
    /// high. Bits set to 0 are left unchanged.
    ///
    /// # Examples
    ///
    /// `bundle.set_high(0b1011_0001)` sets channels 0, 4, 5, and 7 high.
    ///
    /// <section class="warning">
    ///
    /// The caller must ensure that `bits` only contains channels included in this bundle,
    /// i.e. `bits & !self.mask() == 0`.
    ///
    /// For example, if the bundle mask is `0b0000_1011` (channels 0, 1, and 3), then `bits`
    /// must not set bit 2 (e.g. `0b0000_0100`), or channel 2 outside the bundle would be modified
    ///
    /// When compiled with `debug-assertions`, this condition is checked at runtime.
    /// </section>
    #[inline(always)]
    pub fn set_high(&mut self, bits: u32) {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
1
        debug_assert!(
            (bits & !self.mask) == 0,
            "Trying to set bits outside of the bundle mask"
        );
        low_level::write(bits, bits); // or low_level::write(self.mask, bits);
    }
    /// Sets selected channels **low**.
    ///
    /// For every bit set to 1 in `bits`, the corresponding dedicated output channel is driven
    /// low. Bits set to 0 are left unchanged.
    ///
    /// # Examples
    ///
    /// `bundle.set_low(0b1011_0001)` sets channels 0, 4, 5, and 7 low.
    ///
    /// <section class="warning">
    ///
    /// The caller must ensure that `bits` only contains channels included in this bundle,
    /// i.e. `bits & !self.mask() == 0`.
    ///
    /// For example, if the bundle mask is `0b0000_1011` (channels 0, 1, and 3), then `bits`
    /// must not set bit 7 (e.g. `0b1000_0000`), or channel 7 outside the bundle would be cleared
    ///
    /// When compiled with `debug-assertions`, this condition is checked at runtime.
    /// </section>
    #[inline(always)]
    pub fn set_low(&mut self, bits: u32) {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
1
        debug_assert!(
            (bits & !self.mask) == 0,
            "Trying to clear bits outside of the bundle mask"
        );
        low_level::write(bits, 0);
        // or low_level::write(bits & self.mask, 0);
        // the latter is safer (preventing writing to channels outside of the bundle) but slower
    }
    /// Writes output levels for **all channels included by this bundle**.
    ///
    /// Updates the output state of every channel whose bit is set in
    /// [`Self::mask`]. Channels not included in the bundle are not modified.
    ///
    /// `bits` provides the level for each channel in the bundle:
    /// - bit = 0 -> low
    /// - bit = 1 -> high
    ///
    /// # Examples
    ///
    /// If `self.mask()` is `0b1111_0000` (bundle contains channels 4..=7), then
    /// `bundle.write_bits(0b0001_0000)` sets channel 4 high and channels 5..=7 low,
    /// while leaving channels 0..=3 unchanged.
    ///
    /// <section class="warning">
    ///
    /// Only writes channels selected by [`Self::mask`]. Does **not** change channels outside the
    /// mask, even if `bits` contains 1s there
    ///
    /// The masked write only updates channels selected by the mask.
    ///
    /// </section>
    #[inline(always)]
    pub fn write_bits(&mut self, bits: u32) {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        low_level::write(self.mask, bits);
    }
    /// Returns the current output levels of the channels included by this bundle.
    ///
    /// For channels outside the bundle mask, the corresponding bits are always 0.
    ///
    /// # Examples
    ///
    /// If the bundle mask is `0b0000_1011` (channels 0, 1, and 3), then
    /// `output_levels()` will only contain bits 0, 1, and 3, regardless of the output
    /// state of other channels.
    #[inline(always)]
    pub fn output_levels(&self) -> u32 {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
1
        low_level::read_out() & self.mask
    }
}
impl<'lt> Default for DedicatedGpioOutputBundle<'lt> {
    fn default() -> Self {
        Self::new()
    }
}
#[doc_replace]
/// A bundle of dedicated GPIO input drivers.
///
/// An input bundle precomputes a channel mask from one or more [`DedicatedGpioInput`]
/// drivers. Enables reading multiple dedicated input channels with a single low-level read (see
/// [`DedicatedGpioInputBundle::levels`])
///
/// Attaching a driver does **not** change any pin state. The bundle only stores a
/// channel mask; it does not own pins or remember which GPIOs were connected.
///
/// ## Relationship to pins
///
/// Dedicated input channels are fed by GPIO pins that were connected when creating
/// [`DedicatedGpioInput`]. The bundle operates on *channels*, not individual pins:
/// each bit in the returned mask corresponds to a dedicated input channel.
///
/// If a GPIO pin is later reconfigured (e.g. connected to a different peripheral input), that
/// changes what the channel reads. The bundle does not prevent such reconfiguration.
#[cfg_attr(
    multi_core,
    doc = r#"
<section class="warning">
Dedicated GPIO channels are tied to CPU cores. All input drivers attached to a bundle must be
configured on the same core, and the bundle must only be used on the core that created it.
</section>
"#
)]
#[doc = ""]
/// # Examples
///
/// ```rust, no_run
/// # {before_snippet}
/// #
/// use esp_hal::gpio::{
///     Input,
///     InputConfig,
///     dedicated::{DedicatedGpio, DedicatedGpioInput, DedicatedGpioInputBundle},
/// };
///
/// // Create channels:
/// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
///
/// // Create input drivers for two channels:
/// let p0 = Input::new(peripherals.GPIO0, InputConfig::default());
/// let p1 = Input::new(peripherals.GPIO1, InputConfig::default());
/// let in0 = DedicatedGpioInput::new(channels.channel0, p0);
/// let in1 = DedicatedGpioInput::new(channels.channel1, p1);
///
/// // Build a bundle that reads channels 0 and 1:
/// let mut bundle = DedicatedGpioInputBundle::new();
/// bundle.enable_input(&in0).enable_input(&in1);
///
/// // Read both channels at once:
/// let bits = bundle.levels();
/// // bit 0 -> channel 0, bit 1 -> channel 1, ...
/// #
/// # {after_snippet}
/// ```
pub struct DedicatedGpioInputBundle<'lt> {
    _marker: PhantomData<&'lt ()>,
    mask: u32,
    #[cfg(all(debug_assertions, multi_core))]
    core: Cpu,
}
impl<'lt> DedicatedGpioInputBundle<'lt> {
    /// Creates a new, empty dedicated GPIO input bundle.
    ///
    /// A bundle is a *logical* grouping of one or more [`DedicatedGpioInput`] drivers.
    /// Internally, it stores a precomputed channel mask which allows reading multiple
    /// dedicated input channels efficiently.
    ///
    /// The returned bundle initially contains no channels. Add inputs using
    /// [`Self::enable_input`].
    ///
    /// Creating a bundle does **not** configure any hardware by itself.
    pub fn new() -> Self {
        Self {
            _marker: PhantomData,
            mask: 0,
            #[cfg(all(debug_assertions, multi_core))]
            core: Cpu::current(),
        }
    }
    #[doc_replace]
    /// Returns the channel mask of this bundle.
    ///
    /// Each bit corresponds to one dedicated GPIO channel:
    /// - bit 0 -> channel 0
    /// - bit 1 -> channel 1
    /// - ...
    ///
    /// A bit is set to 1 if that channel is currently included in the bundle.
    ///
    /// # Examples
    ///
    /// ```rust, no_run
    /// # {before_snippet}
    ///
    /// use esp_hal::gpio::{
    ///     Input,
    ///     InputConfig,
    ///     dedicated::{DedicatedGpio, DedicatedGpioInput, DedicatedGpioInputBundle},
    /// };
    ///
    /// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
    /// let in0 = DedicatedGpioInput::new(
    ///     channels.channel0,
    ///     Input::new(peripherals.GPIO0, InputConfig::default()),
    /// );
    /// let in2 = DedicatedGpioInput::new(
    ///     channels.channel2,
    ///     Input::new(peripherals.GPIO2, InputConfig::default()),
    /// );
    ///
    /// let mut bundle = DedicatedGpioInputBundle::new();
    /// bundle.enable_input(&in0).enable_input(&in2);
    ///
    /// let mask = bundle.mask(); // bit 0 -> ch0, bit 2 -> ch2, ...
    /// // For this bundle: mask == 0b0000_0101 (channels 0 and 2).
    ///
    /// # {after_snippet}
    /// ```
    #[inline(always)]
    pub fn mask(&self) -> u32 {
        self.mask
    }
    /// Attaches (enables) an already-configured dedicated input driver to this bundle. After
    /// enabling, the input channels of `inp` can be read via this bundle.
    ///
    /// Logically borrows the provided [`DedicatedGpioInput`] for the lifetime `'lt`.
    /// Multiple bundles may borrow the same input driver at the same time. Check examples in the
    /// module-level documentation for more.
    ///
    /// Does not change any input state.
    #[cfg_attr(
        multi_core,
        doc = r#"
<section class="warning">
All dedicated GPIO drivers in a bundle must be configured on the same core as the bundle itself.
</section>
"#
    )]
    pub fn enable_input<'d>(&mut self, inp: &'lt DedicatedGpioInput<'d>) -> &mut Self {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            inp.core, self.core,
            "Trying to enable a dedicated GPIO driver configured on a different core from the bundle."
        );
        self.mask |= inp.mask;
        self
    }
    /// Disables a dedicated input driver in this bundle.
    ///
    /// This updates the internal mask by clearing the channel bit(s) of `inp`. After disabling,
    /// future *bundle* operations will no longer touch those channels.
    ///
    /// - This does **not** affect `inp` itself. It only changes which channels future *bundle*
    ///   operations will touch.
    /// - This does **not** end the lifetime-based borrow of `inp`. Even after disabling, `inp`
    ///   still cannot be moved or dropped while this bundle exists.
    /// - Can be re-enabled later via [`Self::enable_input`]
    #[cfg_attr(
        multi_core,
        doc = r#"
<section class="warning">
Dedicated GPIO drivers must only be disabled if configured on the same core as the bundle itself.
</section>
"#
    )]
    pub fn disable_input<'d>(&mut self, inp: &'lt DedicatedGpioInput<'d>) -> &mut Self {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            inp.core, self.core,
            "Trying to disable a dedicated GPIO driver configured on a different core from the bundle."
        );
        self.mask &= !inp.mask;
        self
    }
    /// Reads the current state of the channels included by this bundle.
    ///
    /// For channels outside the bundle mask, the corresponding bits are always 0.
    ///
    /// # Examples
    ///
    /// If the bundle mask is `0b0000_1011` (channels 0, 1, and 3), then
    /// `levels()` will only contain bits 0, 1, and 3, regardless of the state
    /// of other channels.
    #[inline(always)]
    pub fn levels(&self) -> u32 {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
1
        low_level::read_in() & self.mask
    }
    /// Returns whether all channels in this bundle are currently high.
    #[inline(always)]
    pub fn all_high(&self) -> bool {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        (low_level::read_in() & self.mask) == self.mask
    }
    /// Returns whether all channels in this bundle are currently low.
    #[inline(always)]
    pub fn all_low(&self) -> bool {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        (low_level::read_in() & self.mask) == 0
    }
}
impl<'lt> Default for DedicatedGpioInputBundle<'lt> {
    fn default() -> Self {
        Self::new()
    }
}
#[doc_replace]
/// A bundle of dedicated GPIO flex drivers (input + output).
///
/// A flex bundle precomputes a channel mask from one or more [`DedicatedGpioFlex`]
/// drivers. Enables:
/// - updating multiple dedicated output channels with a single low-level write, and
/// - reading multiple dedicated input channels with a single low-level read.
///
/// Attaching a driver does **not** change any pin state. The bundle only stores a
/// channel mask; it does not own pins or remember which GPIOs were connected.
///
/// ## Relationship to pins
///
/// A [`DedicatedGpioFlex`] connects one dedicated channel to one GPIO pin for both
/// input and output. The bundle operates on *channels*, not pins: writing channel bits
/// affects the pins currently connected to those channels.
#[cfg_attr(
    multi_core,
    doc = r#"
<section class="warning">
Dedicated GPIO channels are tied to CPU cores. All flex drivers attached to a bundle must be
configured on the current and same core, and the bundle must only be used on the core that created it.
For example, do not create the bundle on core 0 and then call <code>write_bits</code> from a task
running on core 1.
</section>
"#
)]
#[doc = ""]
/// # Examples
///
/// ```rust, no_run
/// # {before_snippet}
/// #
/// use esp_hal::gpio::{
///     Flex,
///     Level,
///     dedicated::{DedicatedGpio, DedicatedGpioFlex, DedicatedGpioFlexBundle},
/// };
///
/// // Create channels:
/// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
///
/// // Create flex drivers for two channels:
/// let p0 = Flex::new(peripherals.GPIO0);
/// let p1 = Flex::new(peripherals.GPIO1);
/// let f0 = DedicatedGpioFlex::new(channels.channel0, p0);
/// let f1 = DedicatedGpioFlex::new(channels.channel1, p1);
///
/// // Build a bundle that controls channels 0 and 1:
/// let mut bundle = DedicatedGpioFlexBundle::new();
/// bundle.enable_flex(&f0).enable_flex(&f1);
///
/// // Set channel 0 high:
/// bundle.set_high(0b0000_0001);
///
/// // Drive both channels at once:
/// bundle.write_bits(0b0000_0010); // ch0 low, ch1 high
///
/// // Read both channels at once:
/// let in_bits = bundle.levels();
/// #
/// # {after_snippet}
/// ```
/// ...
pub struct DedicatedGpioFlexBundle<'lt> {
    _marker: PhantomData<&'lt ()>,
    mask: u32,
    #[cfg(all(debug_assertions, multi_core))]
    core: Cpu,
}
impl<'lt> DedicatedGpioFlexBundle<'lt> {
    /// Creates a new, empty dedicated GPIO flex bundle.
    ///
    /// A bundle is a *logical* grouping of one or more [`DedicatedGpioFlex`] drivers.
    /// Internally, it stores a precomputed channel mask (see [`Self::mask`]) which allows
    /// writing multiple dedicated GPIO channels efficiently.
    ///
    /// The returned bundle initially contains no channels. Add flex drivers using
    /// [`Self::enable_flex`].
    ///
    /// Creating a bundle does **not** configure any hardware by itself.
    pub fn new() -> Self {
        Self {
            _marker: PhantomData,
            mask: 0,
            #[cfg(all(debug_assertions, multi_core))]
            core: Cpu::current(),
        }
    }
    #[doc_replace]
    /// Returns the channel mask of this bundle.
    ///
    /// Each bit corresponds to one dedicated GPIO channel:
    /// - bit 0 -> channel 0
    /// - bit 1 -> channel 1
    /// - ...
    ///
    /// A bit is set to 1 if that channel is currently included in the bundle.
    ///
    /// # Examples
    ///
    /// ```rust, no_run
    /// # {before_snippet}
    ///
    /// use esp_hal::gpio::{
    ///     Flex,
    ///     dedicated::{DedicatedGpio, DedicatedGpioFlex, DedicatedGpioFlexBundle},
    /// };
    ///
    /// let channels = DedicatedGpio::new(peripherals.GPIO_DEDICATED);
    /// let f0 = DedicatedGpioFlex::new(channels.channel0, Flex::new(peripherals.GPIO0));
    /// let f2 = DedicatedGpioFlex::new(channels.channel2, Flex::new(peripherals.GPIO2));
    ///
    /// let mut bundle = DedicatedGpioFlexBundle::new();
    /// bundle.enable_flex(&f0).enable_flex(&f2);
    ///
    /// let mask = bundle.mask(); // bit 0 -> ch0, bit 2 -> ch2, ...
    /// // For this bundle: mask == 0b0000_0101 (channels 0 and 2).
    ///
    /// # {after_snippet}
    /// ```
    #[inline(always)]
    pub fn mask(&self) -> u32 {
        self.mask
    }
    /// Attaches (enables) an already-configured dedicated flex driver to this bundle. After
    /// enabling, the input/output channels of `flex` can be controlled via this bundle.
    ///
    /// Logically borrows the provided [`DedicatedGpioFlex`] for the lifetime `'lt`.
    /// Multiple bundles may borrow the same flex driver at the same time. Check examples in the
    /// module-level documentation for more.
    ///
    /// Does not change any input/output state.
    #[cfg_attr(
        multi_core,
        doc = r#"
<section class="warning">
All dedicated GPIO drivers in a bundle must be configured on the same core as the bundle itself.
</section>
    "#
    )]
    pub fn enable_flex<'d>(&mut self, flex: &'lt DedicatedGpioFlex<'d>) -> &mut Self {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            flex.core, self.core,
            "Trying to enable a dedicated GPIO driver configured on a different core from the bundle."
        );
        self.mask |= flex.mask;
        self
    }
    /// Disables a dedicated flex driver in this bundle.
    ///
    /// This updates the internal mask by clearing the channel bit(s) of `flex`. After disabling,
    /// future *bundle* operations will no longer touch those channels.
    ///
    /// - This does **not** affect `flex` itself. It only changes which channels future *bundle*
    ///   operations will touch.
    /// - This does **not** end the lifetime-based borrow of `flex`. Even after disabling, `flex`
    ///   still cannot be moved or dropped while this bundle exists.
    /// - Can be re-enabled later via [`Self::enable_flex`]
    #[cfg_attr(
        multi_core,
        doc = r#"
<section class="warning">
Dedicated GPIO drivers must only be disabled if configured on the same core as the bundle itself.
</section>
"#
    )]
    pub fn disable_flex<'d>(&mut self, flex: &'lt DedicatedGpioFlex<'d>) -> &mut Self {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            flex.core, self.core,
            "Trying to disable a dedicated GPIO driver configured on a different core from the bundle."
        );
        self.mask &= !flex.mask;
        self
    }
    /// Sets selected channels **high**.
    ///
    /// For every bit set to 1 in `bits`, the corresponding channel is driven high.
    /// Bits set to 0 are left unchanged.
    ///
    /// # Examples
    ///
    /// If the bundle contains channels 0 and 2, then `bundle.set_high(0b0000_0101)` sets
    /// channel 0 and 2 high.
    ///
    /// <section class="warning">
    ///
    /// The caller must ensure that `bits` only contains channels included in this bundle,
    /// i.e. `bits & !self.mask() == 0`.
    ///
    /// For example, if the bundle mask is `0b0000_0011` (channels 0 and 1), then `bits` must not
    /// set bit 2 (e.g. `0b0000_0100`), or channel 2 outside the bundle would be modified
    ///
    /// When compiled with `debug-assertions`, this condition is checked at runtime.
    /// </section>
    #[inline(always)]
    pub fn set_high(&mut self, bits: u32) {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        debug_assert!(
            (bits & !self.mask) == 0,
            "Trying to set bits outside of the bundle mask"
        );
        low_level::write(bits, bits); // or low_level::write(self.mask, bits);
    }
    /// Sets selected channels **low**.
    ///
    /// For every bit set to 1 in `bits`, the corresponding channel is driven low.
    /// Bits set to 0 are left unchanged.
    ///
    /// # Examples
    ///
    /// If the bundle contains channels 0 and 2, then `bundle.set_low(0b0000_0101)` sets
    /// channel 0 and 2 low.
    ///
    /// <section class="warning">
    ///
    /// The caller must ensure that `bits` only contains channels included in this bundle,
    /// i.e. `bits & !self.mask() == 0`.
    ///
    /// For example, if the bundle mask is `0b0000_0011` (channels 0 and 1), then `bits` must not
    /// set bit 7 (e.g. `0b1000_0000`), or channel 7 outside the bundle would be modified
    ///
    /// When compiled with `debug-assertions`, this condition is checked at runtime.
    /// </section>
    #[inline(always)]
    pub fn set_low(&mut self, bits: u32) {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        debug_assert!(
            (bits & !self.mask) == 0,
            "Trying to clear bits outside of the bundle mask"
        );
        low_level::write(bits, 0); // or low_level::write(bits & self.mask, 0); the latter is safer but slower
    }
    /// Writes output levels for **all channels included by this bundle**.
    ///
    /// Updates the output state of every channel whose bit is set in
    /// [`Self::mask`]. Channels not included in the bundle are not modified.
    ///
    /// `bits` provides the level for each channel in the bundle:
    /// - bit = 0 -> low
    /// - bit = 1 -> high
    ///
    /// # Examples
    ///
    /// If `self.mask()` is `0b1111_0000` (bundle contains channels 4..=7), then
    /// `bundle.write_bits(0b0001_0000)` sets channel 4 high and channels 5..=7 low,
    /// while leaving channels 0..=3 unchanged.
    ///
    /// <section class="warning">
    ///
    /// Only writes channels selected by [`Self::mask`]. Does **not** change channels outside the
    /// mask, even if `bits` contains 1s there
    ///
    /// The masked write only updates channels selected by the mask.
    ///
    /// </section>
    #[inline(always)]
    pub fn write_bits(&mut self, bits: u32) {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        low_level::write(self.mask, bits);
    }
    /// Returns the current output levels of the channels included by this bundle.
    ///
    /// For channels outside the bundle mask, the corresponding bits are always 0.
    ///
    /// # Examples
    ///
    /// If the bundle mask is `0b0000_1011` (channels 0, 1, and 3), then
    /// `output_levels()` will only contain bits 0, 1, and 3, regardless of the output
    /// state of other channels.
    #[inline(always)]
    pub fn output_levels(&self) -> u32 {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        low_level::read_out() & self.mask
    }
    /// Reads the current state of the channels included by this bundle.
    ///
    /// For channels outside the bundle mask, the corresponding bits are always 0.
    ///
    /// # Examples
    ///
    /// If the bundle mask is `0b0000_1011` (channels 0, 1, and 3), then
    /// `levels()` will only contain bits 0, 1, and 3, regardless of the state
    /// of other channels.
    #[inline(always)]
    pub fn levels(&self) -> u32 {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        low_level::read_in() & self.mask
    }
    /// Returns whether all channels in this bundle are currently high.
    #[inline(always)]
    pub fn all_high(&self) -> bool {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        (low_level::read_in() & self.mask) == self.mask
    }
    /// Returns whether all channels in this bundle are currently low.
    #[inline(always)]
    pub fn all_low(&self) -> bool {
        #[cfg(all(debug_assertions, multi_core))]
        debug_assert_eq!(
            self.core,
            Cpu::current(),
            "Dedicated GPIO used on a different CPU core than it was created on"
        );
        (low_level::read_in() & self.mask) == 0
    }
}
impl<'lt> Default for DedicatedGpioFlexBundle<'lt> {
    fn default() -> Self {
        Self::new()
    }
}