Skip to main content

gd32e2_hal/
gpio.rs

1//! General-purpose I/O.
2//!
3//! A pin's port, number and mode all live in its type.
4//!
5//! Pins are handed out by [`GpioExt::split`]. Modes are changed with the
6//! `into_*` methods, each returning a new type.
7//!
8//! ```ignore
9//! let parts = dp.gpioa.split(&mut rcu);
10//! let mut led = parts.pa5.into_output();
11//! led.set_high().unwrap();
12//! let tx = parts.pa9.into_alternate::<1>();
13//! ```
14//!
15//! Two modes are special. `PA13`/`PA14` start as [`Debugger`], since after
16//! reset they really are wired to SWD; leaving that goes through the separate
17//! `activate_into_*()` family. [`Pin::lock`] freezes a configuration until the
18//! next chip reset, which the type reflects as [`Locked`] — with no way back,
19//! since the hardware has none either.
20
21use core::convert::Infallible;
22use core::marker::PhantomData;
23
24use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
25
26use crate::pac;
27use crate::rcu::{Enable, Rcu, Reset};
28
29/// `CTL` field encoding: what the pin is wired to.
30///
31/// A distinct type from [`Omode`] so the two cannot be passed to each other's
32/// setter.
33enum Ctl {
34    Input = 0b00,
35    Output = 0b01,
36    Af = 0b10,
37    Analog = 0b11,
38}
39
40/// `OMODE` field encoding: how an output drives the pin.
41enum Omode {
42    PushPull = 0b0,
43    OpenDrain = 0b1,
44}
45
46/// Mode: digital input.
47pub struct Input;
48/// Output type: driven both high and low.
49pub struct PushPull;
50/// Output type: driven low, released high — needs a pull-up to reach a high level.
51pub struct OpenDrain;
52/// Mode: digital output, of type `OTYPE` ([`PushPull`] or [`OpenDrain`]).
53pub struct Output<OTYPE> {
54    _otype: PhantomData<OTYPE>,
55}
56/// Mode: analog, the input mode the ADC requires.
57pub struct Analog;
58/// Mode: alternate function `AF`, routing the pin to a peripheral, driven as
59/// `OTYPE` ([`PushPull`] or [`OpenDrain`]).
60///
61/// The output type is part of the mode because some peripherals accept only
62/// one: I²C needs open-drain lines, and a push-pull pin there fights whoever
63/// pulls the line low. Drivers bind to the type they need. Defaults to
64/// [`PushPull`].
65pub struct Alternate<const AF: u8, OTYPE = PushPull> {
66    _otype: PhantomData<OTYPE>,
67}
68/// Mode: serial-wire debug, the reset state of `PA13`/`PA14`.
69///
70/// Leaving it goes through [`Pin::activate`] or one of its `activate_into_*`
71/// siblings.
72pub struct Debugger;
73/// Mode: configuration frozen until the next chip reset, wrapping the mode it
74/// was locked in.
75///
76/// Reading and writing the pin still work exactly as they did before locking;
77/// only reconfiguration is barred.
78pub struct Locked<MODE> {
79    _mode: PhantomData<MODE>,
80}
81
82/// A single pin: `P` is the port (`'A'`, `'B'`, `'C'` or `'F'`), `N` the pin
83/// number.
84pub struct Pin<const P: char, const N: u8, MODE> {
85    _mode: PhantomData<MODE>,
86}
87
88/// Which port an [`ErasedPin`] came from.
89#[derive(Clone, Copy, PartialEq, Eq)]
90#[cfg_attr(feature = "defmt", derive(defmt::Format))]
91#[allow(missing_docs)]
92pub enum Port {
93    A,
94    B,
95    #[cfg(pads_ge_48)]
96    C,
97    F,
98}
99
100impl Port {
101    /// Converts the type-level port letter into its runtime form.
102    ///
103    /// `const` so that erasing a pin costs nothing: the letter is a constant at
104    /// every call site, and the match folds away with it.
105    #[inline]
106    const fn from_char(port: char) -> Self {
107        match port {
108            'A' => Port::A,
109            'B' => Port::B,
110            #[cfg(pads_ge_48)]
111            'C' => Port::C,
112            'F' => Port::F,
113            _ => unreachable!(),
114        }
115    }
116}
117
118/// A pin whose identity has moved into runtime values, keeping its mode.
119///
120/// Port and number become fields, so pins of different ports share a type and fit
121/// in one array. `MODE` stays a type parameter — an `ErasedPin<Input>` still has
122/// no `set_high`. One-way: the checks that need the exact pin have already run by
123/// the time it is erased.
124pub struct ErasedPin<MODE> {
125    port: Port,
126    number: u8,
127    _mode: PhantomData<MODE>,
128}
129
130/// Internal pull resistor.
131#[derive(Clone, Copy, PartialEq, Eq)]
132#[cfg_attr(feature = "defmt", derive(defmt::Format))]
133pub enum Pull {
134    /// No pull resistor.
135    Floating = 0b00,
136    /// Pulled up to the supply.
137    Up = 0b01,
138    /// Pulled down to ground.
139    Down = 0b10,
140}
141
142/// Output slew rate. Slower edges radiate less; faster ones are needed for
143/// high-speed peripherals.
144#[derive(Clone, Copy, PartialEq, Eq)]
145#[cfg_attr(feature = "defmt", derive(defmt::Format))]
146#[allow(missing_docs)]
147pub enum Speed {
148    Mhz2 = 0b00,
149    Mhz10 = 0b01,
150    Mhz50 = 0b11,
151}
152
153/// Marks `AF` as a valid alternate function number for this pin.
154pub trait ValidAf<const AF: u8> {}
155
156// Recursive, one row per step, and with the gated row spelled out as its own rule:
157// a row expands to one impl per AF number, and an attribute captured inside `$(…)?`
158// cannot be repeated across that inner loop — it would be used one nesting level
159// deeper than it was bound. Matching `#[$cfg:meta]` outside any repetition puts it
160// at depth zero, where the loop can replicate it.
161macro_rules! pin_af {
162    () => {};
163    ( #[$cfg:meta] $p:literal $n:literal => [ $($af:literal: $fun:literal),* $(,)? ] $(, $($rest:tt)*)? ) => {
164        $(
165            #[$cfg]
166            #[doc = concat!("`AF", stringify!($af), "` — ", $fun, ".")]
167            impl<MODE> ValidAf<$af> for Pin<$p, $n, MODE> {}
168        )*
169        $( pin_af! { $($rest)* } )?
170    };
171    ( $p:literal $n:literal => [ $($af:literal: $fun:literal),* $(,)? ] $(, $($rest:tt)*)? ) => {
172        $(
173            #[doc = concat!("`AF", stringify!($af), "` — ", $fun, ".")]
174            impl<MODE> ValidAf<$af> for Pin<$p, $n, MODE> {}
175        )*
176        $( pin_af! { $($rest)* } )?
177    };
178}
179
180// AF map from datasheet Table 2-13/2-14 (die-level), whose footnotes mark
181// functions present on some variants only:
182//   (1) GD32E230x4 only          -> cfg `chip_x4`
183//   (2) GD32E230x8/6             -> cfg `chip_x6` or `chip_x8`
184//   (3) GD32E230x8 only          -> cfg `chip_x8`
185// The cfgs come from build.rs, which derives them from the part's flash code.
186pin_af! {
187    // ---- Port A ----
188    'A' 0  => [1: "USART0_CTS on x4, USART1_CTS on x6/x8", 7: "CMP_OUT"],
189    'A' 1  => [0: "EVENTOUT", 1: "USART0_RTS/DE on x4, USART1_RTS/DE on x6/x8"],
190    'A' 2  => [1: "USART0_TX on x4, USART1_TX on x6/x8"],
191    'A' 3  => [1: "USART0_RX on x4, USART1_RX on x6/x8"],
192    'A' 4  => [0: "SPI0_NSS/I2S0_WS", 1: "USART0_CK on x4, USART1_CK on x6/x8", 4: "TIMER13_CH0"],
193    'A' 5  => [0: "SPI0_SCK/I2S0_CK"],
194    'A' 6  => [0: "SPI0_MISO", 1: "TIMER2_CH0", 2: "TIMER0_BRKIN", 5: "TIMER15_CH0", 6: "EVENTOUT", 7: "CMP_OUT"],
195    'A' 7  => [0: "SPI0_MOSI", 1: "TIMER2_CH1", 2: "TIMER0_CH0_ON", 4: "TIMER13_CH0", 5: "TIMER16_CH0", 6: "EVENTOUT"],
196    #[cfg(pads_ge_24)]
197    'A' 8  => [0: "CK_OUT", 1: "USART0_CK", 2: "TIMER0_CH0", 3: "EVENTOUT"],
198    'A' 9  => [1: "USART0_TX", 2: "TIMER0_CH1", 4: "I2C0_SCL", 5: "CK_OUT"],
199    'A' 10 => [0: "TIMER16_BRKIN", 1: "USART0_RX", 2: "TIMER0_CH2", 4: "I2C0_SDA"],
200    #[cfg(pads_ge_lqfp32)]
201    'A' 11 => [0: "EVENTOUT", 1: "USART0_CTS", 2: "TIMER0_CH3", 4: "I2C0_SMBA", 7: "CMP_OUT"],
202    #[cfg(pads_ge_lqfp32)]
203    'A' 12 => [0: "EVENTOUT", 1: "USART0_RTS/DE", 2: "TIMER0_ETI", 4: "I2C0_TXFRAME"],
204    'A' 13 => [0: "SWDIO", 1: "IFRP_OUT"],
205    'A' 14 => [0: "SWCLK", 1: "USART0_TX on x4, USART1_TX on x6/x8"],
206    #[cfg(pads_ge_28)]
207    'A' 15 => [0: "SPI0_NSS/I2S0_WS", 1: "USART0_RX on x4, USART1_RX on x6/x8", 3: "EVENTOUT"],
208    // ---- Port B ----
209    #[cfg(pads_ge_24)]
210    'B' 0  => [0: "EVENTOUT", 1: "TIMER2_CH2", 2: "TIMER0_CH1_ON"],
211    'B' 1  => [1: "TIMER2_CH3", 2: "TIMER13_CH0", 3: "TIMER0_CH2_ON"],
212    #[cfg(pads_ge_qfn32)]
213    'B' 2  => [1: "TIMER2_ETI"],
214    #[cfg(pads_ge_28)]
215    'B' 3  => [0: "SPI0_SCK/I2S0_CK", 1: "EVENTOUT"],
216    #[cfg(pads_ge_28)]
217    'B' 4  => [0: "SPI0_MISO", 1: "TIMER2_CH0", 2: "EVENTOUT", 4: "I2C0_TXFRAME", 6: "TIMER16_BRKIN"],
218    #[cfg(pads_ge_28)]
219    'B' 5  => [0: "SPI0_MOSI", 1: "TIMER2_CH1", 2: "I2C0_SMBA", 3: "TIMER15_BRKIN"],
220    #[cfg(pads_ge_24)]
221    'B' 6  => [0: "USART0_TX", 1: "I2C0_SCL", 2: "TIMER15_CH0_ON"],
222    #[cfg(pads_ge_24)]
223    'B' 7  => [0: "USART0_RX", 1: "I2C0_SDA", 2: "TIMER16_CH0_ON"],
224    #[cfg(pads_ge_qfn32)]
225    'B' 8  => [1: "I2C0_SCL", 2: "TIMER15_CH0"],
226    #[cfg(pads_ge_48)]
227    'B' 9  => [0: "IFRP_OUT", 1: "I2C0_SDA", 2: "TIMER16_CH0", 3: "EVENTOUT", 5: "I2S0_MCK"],
228    #[cfg(pads_ge_48)]
229    'B' 11 => [0: "EVENTOUT"],
230    #[cfg(pads_ge_48)]
231    'B' 12 => [1: "EVENTOUT", 2: "TIMER0_BRKIN"],
232    #[cfg(pads_ge_48)]
233    'B' 13 => [2: "TIMER0_CH0_ON"],
234    #[cfg(pads_ge_48)]
235    'B' 14 => [2: "TIMER0_CH1_ON"],
236    #[cfg(pads_ge_48)]
237    'B' 15 => [2: "TIMER0_CH2_ON"],
238    // ---- Port F ----
239    'F' 0  => [1: "I2C0_SDA"],
240    'F' 1  => [1: "I2C0_SCL"],
241}
242
243// ---- (1) GD32E230x4 only ----
244#[cfg(chip_x4)]
245pin_af! {
246    #[cfg(pads_ge_48)]
247    'B' 10 => [1: "I2C0_SCL"],
248    #[cfg(pads_ge_48)]
249    'B' 11 => [1: "I2C0_SDA"],
250    #[cfg(pads_ge_48)]
251    'B' 12 => [0: "SPI0_NSS"],
252    #[cfg(pads_ge_48)]
253    'B' 13 => [0: "SPI0_SCK"],
254    #[cfg(pads_ge_48)]
255    'B' 14 => [0: "SPI0_MISO"],
256    #[cfg(pads_ge_48)]
257    'B' 15 => [0: "SPI0_MOSI"],
258    #[cfg(pads_ge_48)]
259    'F' 6  => [0: "I2C0_SCL"],
260    #[cfg(pads_ge_48)]
261    'F' 7  => [0: "I2C0_SDA"],
262}
263
264// ---- (2) GD32E230x8/6 ----
265#[cfg(any(chip_x6, chip_x8))]
266pin_af! {
267    #[cfg(pads_ge_24)]
268    'A' 8  => [4: "USART1_TX"],
269    #[cfg(pads_ge_24)]
270    'B' 0  => [4: "USART1_RX"],
271}
272
273// ---- (3) GD32E230x8 only ----
274// TIMER14 functions stay here rather than under `has_timer14`: `ValidAf` gates the
275// AF number, which the die carries either way, and a channel of a timer the part
276// lacks is unreachable anyway — `channel_pins!` is what gates that.
277#[cfg(chip_x8)]
278pin_af! {
279    'A' 0  => [4: "I2C1_SCL"],
280    'A' 1  => [4: "I2C1_SDA", 5: "TIMER14_CH0_ON"],
281    'A' 2  => [0: "TIMER14_CH0"],
282    'A' 3  => [0: "TIMER14_CH1"],
283    'A' 4  => [6: "SPI1_NSS"],
284    'A' 9  => [0: "TIMER14_BRKIN"],
285    #[cfg(pads_ge_lqfp32)]
286    'A' 11 => [5: "I2C1_SCL", 6: "SPI1_IO2"],
287    #[cfg(pads_ge_lqfp32)]
288    'A' 12 => [5: "I2C1_SDA", 6: "SPI1_IO3"],
289    'A' 13 => [6: "SPI1_MISO"],
290    'A' 14 => [6: "SPI1_MOSI"],
291    #[cfg(pads_ge_28)]
292    'A' 15 => [6: "SPI1_NSS"],
293    'B' 1  => [6: "SPI1_SCK"],
294    #[cfg(pads_ge_48)]
295    'B' 9  => [7: "SPI1_NSS"],
296    #[cfg(pads_ge_48)]
297    'B' 10 => [1: "I2C1_SCL", 6: "SPI1_IO2", 7: "SPI1_SCK"],
298    #[cfg(pads_ge_48)]
299    'B' 11 => [1: "I2C1_SDA", 6: "SPI1_IO3"],
300    #[cfg(pads_ge_48)]
301    'B' 12 => [0: "SPI1_NSS", 4: "I2C1_SMBA"],
302    #[cfg(pads_ge_48)]
303    'B' 13 => [0: "SPI1_SCK", 1: "I2C1_TXFRAME", 5: "I2C1_SCL"],
304    #[cfg(pads_ge_48)]
305    'B' 14 => [0: "SPI1_MISO", 1: "TIMER14_CH0", 5: "I2C1_SDA"],
306    #[cfg(pads_ge_48)]
307    'B' 15 => [0: "SPI1_MOSI", 1: "TIMER14_CH1", 3: "TIMER14_CH0_ON"],
308    #[cfg(pads_ge_48)]
309    'F' 6  => [0: "I2C1_SCL"],
310    #[cfg(pads_ge_48)]
311    'F' 7  => [0: "I2C1_SDA"],
312}
313
314/// Marks a mode whose pin may be reconfigured.
315pub trait Active {}
316
317impl Active for Input {}
318impl Active for Analog {}
319impl<const AF: u8, OTYPE> Active for Alternate<AF, OTYPE> {}
320impl<OTYPE> Active for Output<OTYPE> {}
321
322/// Marks a pin on a port that has a `LOCK` register, gating [`Pin::lock`].
323///
324/// Ports A and B have one; ports C and F do not.
325pub trait HasLock {}
326impl<const N: u8, MODE> HasLock for Pin<'A', N, MODE> {}
327impl<const N: u8, MODE> HasLock for Pin<'B', N, MODE> {}
328
329/// Ways out of [`Debugger`], each giving up debug access on this pin.
330///
331/// Every one of them writes the mode itself rather than merely relabelling the
332/// type, so the pin never sits in a state its type misdescribes.
333///
334/// The `activate_` prefix announces the cost: once the pin leaves SWD, the board
335/// stops answering the probe until the next reset.
336impl<const P: char, const N: u8> Pin<P, N, Debugger> {
337    /// Releases the pin as a digital input.
338    pub fn activate_into_input(mut self) -> Pin<P, N, Input> {
339        self.set_mode(Ctl::Input);
340        Pin { _mode: PhantomData }
341    }
342    /// Releases the pin as an input; shorthand for
343    /// [`activate_into_input`](Self::activate_into_input).
344    pub fn activate(self) -> Pin<P, N, Input> {
345        self.activate_into_input()
346    }
347    /// Releases the pin as a push-pull output.
348    pub fn activate_into_push_pull_output(mut self) -> Pin<P, N, Output<PushPull>> {
349        self.set_mode(Ctl::Output);
350        self.set_omode(Omode::PushPull);
351        Pin { _mode: PhantomData }
352    }
353    /// Releases the pin as an open-drain output.
354    pub fn activate_into_open_drain_output(mut self) -> Pin<P, N, Output<OpenDrain>> {
355        self.set_mode(Ctl::Output);
356        self.set_omode(Omode::OpenDrain);
357        Pin { _mode: PhantomData }
358    }
359    /// Releases the pin as an output; shorthand for the push-pull variant.
360    pub fn activate_into_output(self) -> Pin<P, N, Output<PushPull>> {
361        self.activate_into_push_pull_output()
362    }
363    /// Releases the pin as an analog input, as required by the ADC.
364    pub fn activate_into_analog(mut self) -> Pin<P, N, Analog> {
365        self.set_mode(Ctl::Analog);
366        Pin { _mode: PhantomData }
367    }
368    /// Releases the pin to a peripheral through alternate function `AF`,
369    /// driven push-pull.
370    ///
371    /// Gated by [`ValidAf`] exactly as
372    /// [`into_alternate`](Pin::into_alternate) is.
373    pub fn activate_into_alternate<const AF: u8>(mut self) -> Pin<P, N, Alternate<AF>>
374    where
375        Self: ValidAf<AF>,
376    {
377        self.set_alternate(AF as u32, Omode::PushPull);
378        Pin { _mode: PhantomData }
379    }
380    /// Same, but leaves the pin open-drain, as I²C requires.
381    pub fn activate_into_alternate_open_drain<const AF: u8>(
382        mut self,
383    ) -> Pin<P, N, Alternate<AF, OpenDrain>>
384    where
385        Self: ValidAf<AF>,
386    {
387        self.set_alternate(AF as u32, Omode::OpenDrain);
388        Pin { _mode: PhantomData }
389    }
390}
391
392// State access takes the identity as arguments so `Pin` and `ErasedPin` share one
393// body apiece — the first passes constants, the second its fields. Configuration
394// helpers stay methods on `Pin`; an erased pin has no use for them.
395#[inline]
396const fn reg(port: Port) -> &'static pac::gpioa::RegisterBlock {
397    let ptr = match port {
398        Port::A => pac::Gpioa::ptr(),
399        Port::B => pac::Gpiob::ptr().cast(),
400        // Ports C and F have no LOCK, and the PAC gives port F no AFSEL0/1
401        // either, though the silicon does — reaching them through this block is
402        // what makes `PF0`/`PF1`/`PF6`/`PF7` usable as alternate functions.
403        #[cfg(pads_ge_48)]
404        Port::C => pac::Gpioc::ptr().cast(),
405        Port::F => pac::Gpiof::ptr().cast(),
406    };
407    // SAFETY: every port block lives at a fixed address for the whole program
408    // and shares port A's layout; the registers C and F lack are never reached.
409    unsafe { &*ptr }
410}
411
412#[inline]
413fn read_pin(port: Port, number: u8) -> bool {
414    let bits = reg(port).istat().read().bits();
415    ((bits >> number) & 0b1) == 0b1
416}
417
418#[inline]
419fn read_octl(port: Port, number: u8) -> bool {
420    let bits = reg(port).octl().read().bits();
421    ((bits >> number) & 0b1) == 0b1
422}
423
424#[inline]
425fn set_bop(port: Port, number: u8) {
426    // SAFETY: `number` is below 16, so the one bit set names a pin.
427    reg(port).bop().write(|w| unsafe { w.bits(1 << number) });
428}
429
430#[inline]
431fn set_bc(port: Port, number: u8) {
432    // SAFETY: `number` is below 16, so the one bit set names a pin.
433    reg(port).bc().write(|w| unsafe { w.bits(1 << number) });
434}
435
436#[inline]
437fn set_tg(port: Port, number: u8) {
438    // SAFETY: `number` is below 16, so the one bit set names a pin.
439    reg(port).tg().write(|w| unsafe { w.bits(1 << number) });
440}
441
442#[allow(clippy::unused_self)]
443impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
444    fn reg(&self) -> &'static pac::gpioa::RegisterBlock {
445        reg(Port::from_char(P))
446    }
447
448    fn read_pin(&self) -> bool {
449        read_pin(Port::from_char(P), N)
450    }
451
452    fn read_octl(&self) -> bool {
453        read_octl(Port::from_char(P), N)
454    }
455    fn set_bop(&mut self) {
456        set_bop(Port::from_char(P), N);
457    }
458
459    fn set_bc(&mut self) {
460        set_bc(Port::from_char(P), N);
461    }
462
463    fn set_tg(&mut self) {
464        set_tg(Port::from_char(P), N);
465    }
466}
467
468// The register writers sit in the unbounded block, not with the `into_*` that
469// call them: `Debugger` is not `Active`, yet leaving that mode is itself a mode
470// write, and the bounded block is invisible from outside it.
471impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
472    fn set_mode(&mut self, mode: Ctl) {
473        let offset = N * 2;
474        // SAFETY: only this pin's two bits change, to a `Ctl` code.
475        self.reg().ctl().modify(|r, w| unsafe {
476            w.bits((r.bits() & !(0b11 << offset)) | ((mode as u32) << offset))
477        });
478    }
479    fn set_af(&mut self, af: u32) {
480        let is_afsel0 = N < 8;
481        let offset = (N % 8) * 4;
482        if is_afsel0 {
483            // SAFETY: only this pin's four bits change; `af` passed a `ValidAf`
484            // bound, so it is an AF number of the pin map.
485            self.reg().afsel0().modify(|r, w| unsafe {
486                w.bits((r.bits() & !(0b1111 << offset)) | (af << offset))
487            });
488        } else {
489            // SAFETY: as in the branch above.
490            self.reg().afsel1().modify(|r, w| unsafe {
491                w.bits((r.bits() & !(0b1111 << offset)) | (af << offset))
492            });
493        }
494    }
495    fn set_pud(&mut self, bits: u32) {
496        let offset = N * 2;
497        // SAFETY: only this pin's two bits change, to a `Pull` code.
498        self.reg()
499            .pud()
500            .modify(|r, w| unsafe { w.bits((r.bits() & !(0b11 << offset)) | (bits << offset)) });
501    }
502    fn set_ospd(&mut self, bits: u32) {
503        let offset = N * 2;
504        // SAFETY: only this pin's two bits change, to a `Speed` code.
505        self.reg()
506            .ospd()
507            .modify(|r, w| unsafe { w.bits((r.bits() & !(0b11 << offset)) | (bits << offset)) });
508    }
509    fn set_omode(&mut self, omode: Omode) {
510        let offset = N;
511        // SAFETY: only this pin's bit changes, to an `Omode` code.
512        self.reg().omode().modify(|r, w| unsafe {
513            w.bits((r.bits() & !(0b1 << offset)) | ((omode as u32) << offset))
514        });
515    }
516    /// Routes the pin to `af` and drives it as `omode`, the two halves of every
517    /// `*_alternate*` method — the output type is part of the resulting mode, so
518    /// it is written here rather than left at whatever the previous mode used.
519    fn set_alternate(&mut self, af: u32, omode: Omode) {
520        self.set_mode(Ctl::Af);
521        self.set_af(af);
522        self.set_omode(omode);
523    }
524    fn set_lk(&mut self, lkk: bool) {
525        self.reg().lock().modify(|_, w| {
526            let w = match N {
527                0 => w.lk0().locked(),
528                1 => w.lk1().locked(),
529                2 => w.lk2().locked(),
530                3 => w.lk3().locked(),
531                4 => w.lk4().locked(),
532                5 => w.lk5().locked(),
533                6 => w.lk6().locked(),
534                7 => w.lk7().locked(),
535                8 => w.lk8().locked(),
536                9 => w.lk9().locked(),
537                10 => w.lk10().locked(),
538                11 => w.lk11().locked(),
539                12 => w.lk12().locked(),
540                13 => w.lk13().locked(),
541                14 => w.lk14().locked(),
542                15 => w.lk15().locked(),
543                _ => unreachable!(),
544            };
545            if lkk {
546                w.lkk().active()
547            } else {
548                w.lkk().not_active()
549            }
550        });
551    }
552}
553
554#[cfg(not(pads_ge_lqfp32))]
555impl<const P: char, const N: u8> Pin<P, N, Input> {
556    /// Hands out a pin whose pad has just changed hands in hardware — the
557    /// SYSCFG remap of `PA11`/`PA12` — reconfigured as an input. The caller
558    /// guarantees no other owner exists.
559    pub(crate) fn take_as_input() -> Self {
560        let mut pin = Pin { _mode: PhantomData };
561        pin.set_mode(Ctl::Input);
562        pin
563    }
564}
565
566impl<const P: char, const N: u8, MODE> Pin<P, N, MODE>
567where
568    MODE: Active,
569{
570    /// Reconfigures the pin as a digital input.
571    pub fn into_input(mut self) -> Pin<P, N, Input> {
572        self.set_mode(Ctl::Input);
573        Pin { _mode: PhantomData }
574    }
575    /// Reconfigures the pin as a push-pull output.
576    pub fn into_push_pull_output(mut self) -> Pin<P, N, Output<PushPull>> {
577        self.set_mode(Ctl::Output);
578        self.set_omode(Omode::PushPull);
579        Pin { _mode: PhantomData }
580    }
581    /// Reconfigures the pin as an open-drain output.
582    ///
583    /// The pin can also be read back in this mode, which shared buses such as
584    /// I²C rely on.
585    pub fn into_open_drain_output(mut self) -> Pin<P, N, Output<OpenDrain>> {
586        self.set_mode(Ctl::Output);
587        self.set_omode(Omode::OpenDrain);
588        Pin { _mode: PhantomData }
589    }
590    /// Reconfigures the pin as an output; shorthand for the push-pull variant.
591    pub fn into_output(self) -> Pin<P, N, Output<PushPull>> {
592        self.into_push_pull_output()
593    }
594    /// Reconfigures the pin as an analog input, as required by the ADC.
595    pub fn into_analog(mut self) -> Pin<P, N, Analog> {
596        self.set_mode(Ctl::Analog);
597        Pin { _mode: PhantomData }
598    }
599    /// Routes the pin to a peripheral through alternate function `AF`, driven
600    /// push-pull.
601    pub fn into_alternate<const AF: u8>(mut self) -> Pin<P, N, Alternate<AF>>
602    where
603        Self: ValidAf<AF>,
604    {
605        self.set_alternate(AF as u32, Omode::PushPull);
606        Pin { _mode: PhantomData }
607    }
608    /// Same, but leaves the pin open-drain: [`I2c`](crate::i2c::I2c) accepts
609    /// only pins that went through here.
610    pub fn into_alternate_open_drain<const AF: u8>(mut self) -> Pin<P, N, Alternate<AF, OpenDrain>>
611    where
612        Self: ValidAf<AF>,
613    {
614        self.set_alternate(AF as u32, Omode::OpenDrain);
615        Pin { _mode: PhantomData }
616    }
617    /// Freezes the pin's configuration until the next chip reset.
618    ///
619    /// Mode, pull, output type, speed and alternate function stop responding to
620    /// writes, with no way back in hardware or in the type: a [`Locked`] pin is
621    /// still read and written, never reconfigured. Ports with a `LOCK` register
622    /// only.
623    pub fn lock(mut self) -> Pin<P, N, Locked<MODE>>
624    where
625        Self: HasLock,
626    {
627        // LKK write sequence from the manual: 1 -> 0 -> 1.
628        self.set_lk(true);
629        self.set_lk(false);
630        self.set_lk(true);
631        for _ in 0..2 {
632            self.reg().lock().read();
633        }
634        Pin { _mode: PhantomData }
635    }
636    /// Moves the pin's identity into runtime values, keeping its mode.
637    ///
638    /// Trades knowing which pin this is for a type shared with every other erased
639    /// pin, so they can be collected into an array. Nothing is written; the
640    /// configuration stands. One way only.
641    pub fn erase(self) -> ErasedPin<MODE> {
642        ErasedPin {
643            port: Port::from_char(P),
644            number: N,
645            _mode: PhantomData,
646        }
647    }
648
649    /// Selects the internal pull resistor.
650    pub fn set_pull(&mut self, p: Pull) {
651        self.set_pud(p as u32);
652    }
653    /// Selects the output slew rate.
654    pub fn set_speed(&mut self, s: Speed) {
655        self.set_ospd(s as u32);
656    }
657}
658
659impl<const P: char, const N: u8, OTYPE> Pin<P, N, Output<OTYPE>> {
660    /// Drives the pin high.
661    pub fn set_high(&mut self) {
662        self.set_bop();
663    }
664    /// Drives the pin low.
665    pub fn set_low(&mut self) {
666        self.set_bc();
667    }
668    /// Inverts the driven level.
669    pub fn toggle(&mut self) {
670        self.set_tg();
671    }
672    /// Returns whether the pin is *being driven* high.
673    ///
674    /// Reads back `OCTL`, i.e. what was last written, not what the wire is at —
675    /// for the latter on an open-drain pin see [`is_high`](Pin::is_high).
676    pub fn is_set_high(&self) -> bool {
677        self.read_octl()
678    }
679    /// Returns whether the pin is *being driven* low.
680    pub fn is_set_low(&self) -> bool {
681        !self.read_octl()
682    }
683}
684
685impl<const P: char, const N: u8> Pin<P, N, Input> {
686    /// Returns whether the input reads high.
687    pub fn is_high(&self) -> bool {
688        self.read_pin()
689    }
690    /// Returns whether the input reads low.
691    pub fn is_low(&self) -> bool {
692        !self.read_pin()
693    }
694}
695
696impl<const P: char, const N: u8> Pin<P, N, Output<OpenDrain>> {
697    /// Returns whether the wire reads high.
698    ///
699    /// Open-drain can only pull low, so this is the actual line level, which
700    /// another device on a shared bus may be holding down.
701    pub fn is_high(&self) -> bool {
702        self.read_pin()
703    }
704    /// Returns whether the wire reads low.
705    pub fn is_low(&self) -> bool {
706        !self.read_pin()
707    }
708}
709
710impl<MODE> ErasedPin<MODE> {
711    /// Returns which port the pin came from.
712    pub fn port(&self) -> Port {
713        self.port
714    }
715    /// Returns the pin's number within its port.
716    pub fn number(&self) -> u8 {
717        self.number
718    }
719}
720
721impl<OTYPE> ErasedPin<Output<OTYPE>> {
722    /// Drives the pin high.
723    pub fn set_high(&mut self) {
724        set_bop(self.port, self.number);
725    }
726    /// Drives the pin low.
727    pub fn set_low(&mut self) {
728        set_bc(self.port, self.number);
729    }
730    /// Inverts the driven level.
731    pub fn toggle(&mut self) {
732        set_tg(self.port, self.number);
733    }
734    /// Returns whether the pin is *being driven* high.
735    ///
736    /// Reads back `OCTL`, i.e. what was last written, not what the wire is at —
737    /// for the latter on an open-drain pin see [`is_high`](ErasedPin::is_high).
738    pub fn is_set_high(&self) -> bool {
739        read_octl(self.port, self.number)
740    }
741    /// Returns whether the pin is *being driven* low.
742    pub fn is_set_low(&self) -> bool {
743        !read_octl(self.port, self.number)
744    }
745}
746
747impl ErasedPin<Input> {
748    /// Returns whether the input reads high.
749    pub fn is_high(&self) -> bool {
750        read_pin(self.port, self.number)
751    }
752    /// Returns whether the input reads low.
753    pub fn is_low(&self) -> bool {
754        !read_pin(self.port, self.number)
755    }
756}
757
758impl ErasedPin<Output<OpenDrain>> {
759    /// Returns whether the wire reads high.
760    ///
761    /// Open-drain can only pull low, so this is the actual line level, which
762    /// another device on a shared bus may be holding down.
763    pub fn is_high(&self) -> bool {
764        read_pin(self.port, self.number)
765    }
766    /// Returns whether the wire reads low.
767    pub fn is_low(&self) -> bool {
768        !read_pin(self.port, self.number)
769    }
770}
771
772impl<const P: char, const N: u8, OTYPE> ErrorType for Pin<P, N, Output<OTYPE>> {
773    type Error = Infallible;
774}
775impl<const P: char, const N: u8, OTYPE> OutputPin for Pin<P, N, Output<OTYPE>> {
776    fn set_high(&mut self) -> Result<(), Self::Error> {
777        self.set_bop();
778        Ok(())
779    }
780    fn set_low(&mut self) -> Result<(), Self::Error> {
781        self.set_bc();
782        Ok(())
783    }
784}
785
786impl<const P: char, const N: u8, OTYPE> StatefulOutputPin for Pin<P, N, Output<OTYPE>> {
787    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
788        Ok(self.read_octl())
789    }
790    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
791        Ok(!self.read_octl())
792    }
793    fn toggle(&mut self) -> Result<(), Self::Error> {
794        self.set_tg();
795        Ok(())
796    }
797}
798
799impl<const P: char, const N: u8> ErrorType for Pin<P, N, Input> {
800    type Error = Infallible;
801}
802impl<const P: char, const N: u8> InputPin for Pin<P, N, Input> {
803    fn is_high(&mut self) -> Result<bool, Self::Error> {
804        Ok(self.read_pin())
805    }
806    fn is_low(&mut self) -> Result<bool, Self::Error> {
807        Ok(!self.read_pin())
808    }
809}
810
811impl<const P: char, const N: u8> InputPin for Pin<P, N, Output<OpenDrain>> {
812    fn is_high(&mut self) -> Result<bool, Self::Error> {
813        Ok(self.read_pin())
814    }
815    fn is_low(&mut self) -> Result<bool, Self::Error> {
816        Ok(!self.read_pin())
817    }
818}
819
820impl<OTYPE> ErrorType for ErasedPin<Output<OTYPE>> {
821    type Error = Infallible;
822}
823impl<OTYPE> OutputPin for ErasedPin<Output<OTYPE>> {
824    fn set_high(&mut self) -> Result<(), Self::Error> {
825        set_bop(self.port, self.number);
826        Ok(())
827    }
828    fn set_low(&mut self) -> Result<(), Self::Error> {
829        set_bc(self.port, self.number);
830        Ok(())
831    }
832}
833
834impl<OTYPE> StatefulOutputPin for ErasedPin<Output<OTYPE>> {
835    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
836        Ok(read_octl(self.port, self.number))
837    }
838    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
839        Ok(!read_octl(self.port, self.number))
840    }
841    fn toggle(&mut self) -> Result<(), Self::Error> {
842        set_tg(self.port, self.number);
843        Ok(())
844    }
845}
846
847impl ErrorType for ErasedPin<Input> {
848    type Error = Infallible;
849}
850impl InputPin for ErasedPin<Input> {
851    fn is_high(&mut self) -> Result<bool, Self::Error> {
852        Ok(read_pin(self.port, self.number))
853    }
854    fn is_low(&mut self) -> Result<bool, Self::Error> {
855        Ok(!read_pin(self.port, self.number))
856    }
857}
858
859impl InputPin for ErasedPin<Output<OpenDrain>> {
860    fn is_high(&mut self) -> Result<bool, Self::Error> {
861        Ok(read_pin(self.port, self.number))
862    }
863    fn is_low(&mut self) -> Result<bool, Self::Error> {
864        Ok(!read_pin(self.port, self.number))
865    }
866}
867
868impl<const P: char, const N: u8, MODE> Pin<P, N, Locked<MODE>>
869where
870    Pin<P, N, MODE>: OutputPin,
871{
872    /// Drives the pin high.
873    pub fn set_high(&mut self) {
874        self.set_bop();
875    }
876    /// Drives the pin low.
877    pub fn set_low(&mut self) {
878        self.set_bc();
879    }
880}
881
882impl<const P: char, const N: u8, MODE> Pin<P, N, Locked<MODE>>
883where
884    Pin<P, N, MODE>: StatefulOutputPin,
885{
886    /// Inverts the driven level.
887    pub fn toggle(&mut self) {
888        self.set_tg();
889    }
890    /// Returns whether the pin is *being driven* high, read back from `OCTL`.
891    pub fn is_set_high(&self) -> bool {
892        self.read_octl()
893    }
894    /// Returns whether the pin is *being driven* low, read back from `OCTL`.
895    pub fn is_set_low(&self) -> bool {
896        !self.read_octl()
897    }
898}
899
900impl<const P: char, const N: u8, MODE> Pin<P, N, Locked<MODE>>
901where
902    Pin<P, N, MODE>: InputPin,
903{
904    /// Returns whether the pin reads high.
905    pub fn is_high(&self) -> bool {
906        self.read_pin()
907    }
908    /// Returns whether the pin reads low.
909    pub fn is_low(&self) -> bool {
910        !self.read_pin()
911    }
912}
913
914impl<const P: char, const N: u8, MODE> ErrorType for Pin<P, N, Locked<MODE>> {
915    type Error = Infallible;
916}
917
918impl<const P: char, const N: u8, MODE> OutputPin for Pin<P, N, Locked<MODE>>
919where
920    Pin<P, N, MODE>: OutputPin,
921{
922    fn set_high(&mut self) -> Result<(), Self::Error> {
923        self.set_bop();
924        Ok(())
925    }
926    fn set_low(&mut self) -> Result<(), Self::Error> {
927        self.set_bc();
928        Ok(())
929    }
930}
931
932impl<const P: char, const N: u8, MODE> StatefulOutputPin for Pin<P, N, Locked<MODE>>
933where
934    Pin<P, N, MODE>: StatefulOutputPin,
935{
936    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
937        Ok(self.read_octl())
938    }
939    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
940        Ok(!self.read_octl())
941    }
942    fn toggle(&mut self) -> Result<(), Self::Error> {
943        self.set_tg();
944        Ok(())
945    }
946}
947
948impl<const P: char, const N: u8, MODE> InputPin for Pin<P, N, Locked<MODE>>
949where
950    Pin<P, N, MODE>: InputPin,
951{
952    fn is_high(&mut self) -> Result<bool, Self::Error> {
953        Ok(self.read_pin())
954    }
955    fn is_low(&mut self) -> Result<bool, Self::Error> {
956        Ok(!self.read_pin())
957    }
958}
959
960/// Extension trait splitting a GPIO port into its individual pins.
961pub trait GpioExt {
962    /// The struct of individual pins this port yields.
963    type Parts;
964
965    /// Enables the port's clock and returns its pins.
966    ///
967    /// Consumes the port, so its pins can only be obtained once.
968    fn split(self, rcu: &mut Rcu) -> Self::Parts;
969}
970
971macro_rules! gpio {
972    ($Parts:ident, $Gpio:ty, $P:literal, [
973        $( $(#[$cfg:meta])? $name:ident : $num:literal : $mode:ty ),+ $(,)?
974    ]) => {
975        /// The pins of this port, in their reset modes.
976        #[allow(missing_docs)]
977        pub struct $Parts {
978            $(
979                $(#[$cfg])?
980                pub $name: Pin<$P, $num, $mode>,
981            )+
982        }
983
984        impl GpioExt for $Gpio {
985            type Parts = $Parts;
986            fn split(self, rcu: &mut Rcu) -> Self::Parts {
987                <$Gpio as Enable>::enable(rcu);
988                <$Gpio as Reset>::reset(rcu);
989                $Parts {
990                    $(
991                        $(#[$cfg])?
992                        $name: Pin::<$P, $num, $mode> { _mode: PhantomData },
993                    )+
994                }
995            }
996        }
997    };
998}
999
1000// Which pads a package bonds, from the pinout figures 2-2 … 2-9 of the datasheet.
1001// The sets nest — 20 ⊂ 24 ⊂ 28 ⊂ LQFP32 ⊂ QFN32 ⊂ 48 — so each pin carries one gate,
1002// naming the smallest package that has it; `pads_ge_*` comes from build.rs.
1003gpio!(PartsA, pac::Gpioa, 'A', [
1004    pa0:0:Input,
1005    pa1:1:Input,
1006    pa2:2:Input,
1007    pa3:3:Input,
1008    pa4:4:Input,
1009    pa5:5:Input,
1010    pa6:6:Input,
1011    pa7:7:Input,
1012    #[cfg(pads_ge_24)] pa8:8:Input,
1013    // Below 32 pins these two pads are shared with PA11 / PA12 through
1014    // `Syscfg::remap_pa11_pa12`, and PA9 / PA10 is the state after reset.
1015    pa9:9:Input,
1016    pa10:10:Input,
1017    #[cfg(pads_ge_lqfp32)] pa11:11:Input,
1018    #[cfg(pads_ge_lqfp32)] pa12:12:Input,
1019    pa13:13:Debugger,
1020    pa14:14:Debugger,
1021    #[cfg(pads_ge_28)] pa15:15:Input,
1022]);
1023
1024gpio!(PartsB, pac::Gpiob, 'B', [
1025    #[cfg(pads_ge_24)] pb0:0:Input,
1026    pb1:1:Input,
1027    // PB2 and PB8 need at least a QFN32: an LQFP32 spends both pins on VSS, which
1028    // the QFN moves onto its thermal pad.
1029    #[cfg(pads_ge_qfn32)] pb2:2:Input,
1030    #[cfg(pads_ge_28)] pb3:3:Input,
1031    #[cfg(pads_ge_28)] pb4:4:Input,
1032    #[cfg(pads_ge_28)] pb5:5:Input,
1033    #[cfg(pads_ge_24)] pb6:6:Input,
1034    #[cfg(pads_ge_24)] pb7:7:Input,
1035    #[cfg(pads_ge_qfn32)] pb8:8:Input,
1036    #[cfg(pads_ge_48)] pb9:9:Input,
1037    #[cfg(pads_ge_48)] pb10:10:Input,
1038    #[cfg(pads_ge_48)] pb11:11:Input,
1039    #[cfg(pads_ge_48)] pb12:12:Input,
1040    #[cfg(pads_ge_48)] pb13:13:Input,
1041    #[cfg(pads_ge_48)] pb14:14:Input,
1042    #[cfg(pads_ge_48)] pb15:15:Input,
1043]);
1044
1045// Port C bonds nothing below 48 pins, so the whole port goes rather than each of
1046// its pins: an empty `Parts` would still offer a `split` for a port with no pads.
1047#[cfg(pads_ge_48)]
1048gpio!(PartsC, pac::Gpioc, 'C', [
1049    pc13:13:Input,
1050    pc14:14:Input,
1051    pc15:15:Input,
1052]);
1053
1054gpio!(PartsF, pac::Gpiof, 'F', [
1055    pf0:0:Input,
1056    pf1:1:Input,
1057    #[cfg(pads_ge_48)] pf6:6:Input,
1058    #[cfg(pads_ge_48)] pf7:7:Input,
1059]);