Skip to main content

gd32e2_hal/
exti.rs

1//! External interrupts and events.
2//!
3//! GPIO has no interrupt of its own: an edge on a pin becomes one only here.
4//! The detector is asynchronous, so it works with the system clock stopped,
5//! which is what makes EXTI the way out of deep sleep and standby.
6//!
7//! [`ExtiExt::split`] hands out one token per line. Lines 0 to 15 arrive
8//! without a source and reach the rest of their API by being given a pin, which
9//! they then own; the internal lines have their source wired in silicon.
10//!
11//! The pin lines share three vectors — `EXTI0_1`, `EXTI2_3`, `EXTI4_15` — so a
12//! handler serves several lines and has to ask which of them is pending. The
13//! internal lines have no EXTI vector at all: each arrives on the vector of the
14//! peripheral behind it, LVD on its own and the rest on RTC, ADC/CMP and
15//! USART0.
16//!
17//! The two outputs are not symmetric in what they leave behind: only the
18//! interrupt path latches `PD`, so a line used for events alone has no flag to
19//! clear after `WFE`.
20//!
21//! ```ignore
22//! let mut syscfg = dp.syscfg.constrain(&mut rcu);
23//! let pa0 = dp.gpioa.split(&mut rcu).pa0.into_input();
24//! let mut line = dp.exti.split().line0.source(&mut syscfg, pa0);
25//! line.edge(EdgeTrigger::Rising);
26//! line.listen();
27//! unsafe { NVIC::unmask(pac::Interrupt::EXTI0_1) };
28//! ```
29
30use crate::gpio::Pin;
31use crate::pac;
32use crate::syscfg::Syscfg;
33
34/// A line that has no source yet, as [`ExtiExt::split`] hands it out.
35pub struct PinSrc;
36
37/// A line whose source is wired in silicon and needs no pin.
38pub struct InternalSrc;
39
40/// Marks a line whose source is settled, which is what the rest of the line's
41/// API hangs off.
42pub trait ExtiConfigured {}
43
44impl ExtiConfigured for InternalSrc {}
45
46/// Source selection code for gpioa
47const PA_SS_CODE: u8 = 0b000;
48/// Source selection code for gpiob
49const PB_SS_CODE: u8 = 0b001;
50/// Source selection code for gpioc
51const PC_SS_CODE: u8 = 0b010;
52/// Source selection code for gpiof
53const PF_SS_CODE: u8 = 0b101;
54
55/// The `EXTISS` code of a port, so the table below states a pin once rather
56/// than repeating its port's encoding on every row.
57const fn ss_code(port: char) -> u8 {
58    match port {
59        'A' => PA_SS_CODE,
60        'B' => PB_SS_CODE,
61        'C' => PC_SS_CODE,
62        'F' => PF_SS_CODE,
63        _ => unreachable!(),
64    }
65}
66
67/// Marks a pin as a source line `N` can be pointed at, and carries the `EXTISS`
68/// code that points it there.
69pub trait ExtiPin: ExtiConfigured {
70    /// Value written to this line's `EXTISS` field to select the pin's port.
71    const SS_CODE: u8;
72}
73
74// One impl per row, so the gate can stay inside the optional fragment — unlike
75// `pin_af!`, where a row expands to several impls and the attribute has to be
76// replicated by recursion instead.
77macro_rules! exti_pin {
78    () => {};
79    ( $(#[$cfg:meta])? $p:literal $n:literal $(, $($rest:tt)*)? ) => {
80        $(#[$cfg])?
81        #[doc = concat!("`P", $p, stringify!($n), "` on EXTI line ", stringify!($n), ".")]
82        impl<MODE> ExtiPin for Pin<$p, $n, MODE> {
83            const SS_CODE: u8 = ss_code($p);
84        }
85        $(#[$cfg])?
86        impl<MODE> ExtiConfigured for Pin<$p, $n, MODE> {}
87        $( exti_pin! { $($rest)* } )?
88    };
89}
90
91// Which ports bond a given pin number, gated exactly as the pins themselves are
92// in `gpio.rs`.
93exti_pin! {
94    // ---- Port A ----
95    'A' 0, 'A' 1, 'A' 2, 'A' 3, 'A' 4, 'A' 5, 'A' 6, 'A' 7,
96    #[cfg(pads_ge_24)] 'A' 8,
97    'A' 9, 'A' 10,
98    #[cfg(pads_ge_lqfp32)] 'A' 11,
99    #[cfg(pads_ge_lqfp32)] 'A' 12,
100    'A' 13, 'A' 14,
101    #[cfg(pads_ge_28)] 'A' 15,
102    // ---- Port B ----
103    #[cfg(pads_ge_24)] 'B' 0,
104    'B' 1,
105    #[cfg(pads_ge_qfn32)] 'B' 2,
106    #[cfg(pads_ge_28)] 'B' 3,
107    #[cfg(pads_ge_28)] 'B' 4,
108    #[cfg(pads_ge_28)] 'B' 5,
109    #[cfg(pads_ge_24)] 'B' 6,
110    #[cfg(pads_ge_24)] 'B' 7,
111    #[cfg(pads_ge_qfn32)] 'B' 8,
112    #[cfg(pads_ge_48)] 'B' 9,
113    #[cfg(pads_ge_48)] 'B' 10,
114    #[cfg(pads_ge_48)] 'B' 11,
115    #[cfg(pads_ge_48)] 'B' 12,
116    #[cfg(pads_ge_48)] 'B' 13,
117    #[cfg(pads_ge_48)] 'B' 14,
118    #[cfg(pads_ge_48)] 'B' 15,
119    // ---- Port C ----
120    #[cfg(pads_ge_48)] 'C' 13,
121    #[cfg(pads_ge_48)] 'C' 14,
122    #[cfg(pads_ge_48)] 'C' 15,
123    // ---- Port F ----
124    'F' 0, 'F' 1,
125    #[cfg(pads_ge_48)] 'F' 6,
126    #[cfg(pads_ge_48)] 'F' 7,
127}
128
129/// Which edges on the source raise the line.
130#[derive(Clone, Copy, PartialEq, Eq)]
131#[cfg_attr(feature = "defmt", derive(defmt::Format))]
132pub enum EdgeTrigger {
133    /// Neither edge; the line answers only to software.
134    None,
135    /// Low to high.
136    Rising,
137    /// High to low.
138    Falling,
139    /// Either direction.
140    Both,
141}
142
143/// One EXTI line, identified by its number at the type level, holding whatever
144/// drives it.
145///
146/// `SRC` is [`PinSrc`] before a port is picked, the pin itself afterwards, and
147/// [`InternalSrc`] on the lines that have no pin.
148pub struct ExtiLine<const N: u8, SRC> {
149    src: SRC,
150}
151
152impl<const N: u8, SRC> ExtiLine<N, SRC> {
153    #[allow(clippy::unused_self)]
154    fn reg(&self) -> &pac::exti::RegisterBlock {
155        // SAFETY: the block lives at a fixed address for the whole program, and
156        // a line touches only its own bit of it.
157        unsafe { &*pac::Exti::ptr() }
158    }
159}
160
161impl<const N: u8> ExtiLine<N, PinSrc> {
162    /// Points the line at `pin` and takes the pin in.
163    ///
164    /// The pin keeps working as it was — EXTI taps the pad and changes nothing
165    /// about the port. What it loses is its mode transitions: `into_*` take the
166    /// pin by value, and while the line holds it only references get out.
167    pub fn source<const P: char, MODE>(
168        self,
169        syscfg: &mut Syscfg,
170        pin: Pin<P, N, MODE>,
171    ) -> ExtiLine<N, Pin<P, N, MODE>>
172    where
173        Pin<P, N, MODE>: ExtiPin,
174    {
175        syscfg.set_extiss(N, <Pin<P, N, MODE> as ExtiPin>::SS_CODE);
176        ExtiLine { src: pin }
177    }
178}
179
180impl<const N: u8, SRC> ExtiLine<N, SRC>
181where
182    SRC: ExtiConfigured,
183{
184    /// This line's bit, the same position in every per-line register.
185    const MASK: u32 = 1 << N;
186
187    /// `bits` with this line's bit set or cleared and the others left alone.
188    ///
189    /// Every per-line register is written through this: the registers are
190    /// shared by all twenty-one lines, so only read-modify-write leaves the
191    /// neighbours standing. The sequence is not atomic, so two lines configured
192    /// from different contexts can still lose one another's edit — put such a
193    /// call in a critical section.
194    const fn with_bit(bits: u32, on: bool) -> u32 {
195        if on {
196            bits | Self::MASK
197        } else {
198            bits & !Self::MASK
199        }
200    }
201    fn set_rten(&mut self, on: bool) {
202        // SAFETY: only this line's bit changes; the rest is written back as read.
203        self.reg()
204            .rten()
205            .modify(|r, w| unsafe { w.bits(Self::with_bit(r.bits(), on)) });
206    }
207    fn set_ften(&mut self, on: bool) {
208        // SAFETY: only this line's bit changes; the rest is written back as read.
209        self.reg()
210            .ften()
211            .modify(|r, w| unsafe { w.bits(Self::with_bit(r.bits(), on)) });
212    }
213    fn set_inten(&mut self, on: bool) {
214        // SAFETY: only this line's bit changes; the rest is written back as read.
215        self.reg()
216            .inten()
217            .modify(|r, w| unsafe { w.bits(Self::with_bit(r.bits(), on)) });
218    }
219    fn set_even(&mut self, on: bool) {
220        // SAFETY: only this line's bit changes; the rest is written back as read.
221        self.reg()
222            .even()
223            .modify(|r, w| unsafe { w.bits(Self::with_bit(r.bits(), on)) });
224    }
225
226    /// Detects rising edges, falling edges, both, or neither.
227    ///
228    /// `RTEN` and `FTEN` are independent, so a line with neither set is left
229    /// firing only from software.
230    ///
231    /// Read-modify-write on registers shared by every line: when lines are
232    /// configured from different contexts, put the call in a critical section.
233    pub fn edge(&mut self, edge: EdgeTrigger) {
234        let (rising, falling) = match edge {
235            EdgeTrigger::None => (false, false),
236            EdgeTrigger::Rising => (true, false),
237            EdgeTrigger::Falling => (false, true),
238            EdgeTrigger::Both => (true, true),
239        };
240        self.set_rten(rising);
241        self.set_ften(falling);
242    }
243
244    /// Raises this line's interrupt in the NVIC.
245    ///
246    /// One vector serves several lines, so a handler still has to ask which of
247    /// them is pending.
248    ///
249    /// Read-modify-write on a register shared by every line: when lines are
250    /// configured from different contexts, put the call in a critical section.
251    pub fn listen(&mut self) {
252        self.set_inten(true);
253    }
254    /// Stops the line reaching the NVIC. The pending flag still latches.
255    ///
256    /// Read-modify-write on a register shared by every line: when lines are
257    /// configured from different contexts, put the call in a critical section.
258    pub fn unlisten(&mut self) {
259        self.set_inten(false);
260    }
261    /// Whether the line reaches the NVIC.
262    pub fn is_listening(&self) -> bool {
263        self.reg().inten().read().bits() & Self::MASK != 0
264    }
265
266    /// Raises this line as an event instead of an interrupt.
267    ///
268    /// An event enters no handler: it sets the core's event latch, which is
269    /// what `WFE` waits on, and execution carries on from there.
270    ///
271    /// Read-modify-write on a register shared by every line: when lines are
272    /// configured from different contexts, put the call in a critical section.
273    pub fn listen_event(&mut self) {
274        self.set_even(true);
275    }
276    /// Stops the line raising events.
277    ///
278    /// Read-modify-write on a register shared by every line: when lines are
279    /// configured from different contexts, put the call in a critical section.
280    pub fn unlisten_event(&mut self) {
281        self.set_even(false);
282    }
283    /// Whether the line raises events.
284    pub fn is_listening_event(&self) -> bool {
285        self.reg().even().read().bits() & Self::MASK != 0
286    }
287
288    /// Raises the line from software, edge or no edge.
289    ///
290    /// `SWIEV` goes straight to the pending flag, so a line left on
291    /// [`EdgeTrigger::None`] still fires this way.
292    ///
293    /// Read-modify-write on a register shared by every line: when lines are
294    /// configured from different contexts, put the call in a critical section.
295    pub fn pend(&mut self) {
296        // SAFETY: only this line's bit is set; the rest is written back as read.
297        self.reg()
298            .swiev()
299            .modify(|r, w| unsafe { w.bits(Self::with_bit(r.bits(), true)) });
300    }
301
302    /// Whether the line has fired and not been cleared since.
303    ///
304    /// Only the interrupt path latches this: a line raised while it is listening
305    /// for events alone leaves the flag clear, so polling it without
306    /// [`listen`](Self::listen) never sees anything.
307    pub fn is_pending(&self) -> bool {
308        self.reg().pd().read().bits() & Self::MASK != 0
309    }
310
311    /// Clears the pending flag.
312    ///
313    /// The request is a level, so a handler that returns without this is
314    /// entered again at once.
315    pub fn clear_interrupt(&mut self) {
316        // SAFETY: `PD` clears on one and ignores zero, so only this line clears.
317        self.reg().pd().write(|w| unsafe { w.bits(Self::MASK) });
318    }
319}
320
321impl<const P: char, const N: u8, MODE> ExtiLine<N, Pin<P, N, MODE>> {
322    /// The pin the line listens to, for reading it.
323    pub fn pin(&self) -> &Pin<P, N, MODE> {
324        &self.src
325    }
326    /// The pin the line listens to, for driving it.
327    ///
328    /// A line watching a pin this code drives itself is legal, and this is how
329    /// that pin is still reached.
330    pub fn pin_mut(&mut self) -> &mut Pin<P, N, MODE> {
331        &mut self.src
332    }
333
334    /// Gives the pin back and returns the line to its unsourced state.
335    ///
336    /// Disarms the line first — no edges, neither output, no pending flag — so
337    /// the next owner of the pin does not inherit an interrupt on it.
338    ///
339    /// Read-modify-write on registers shared by every line: when lines are
340    /// configured from different contexts, put the call in a critical section.
341    pub fn release(mut self) -> (ExtiLine<N, PinSrc>, Pin<P, N, MODE>)
342    where
343        Pin<P, N, MODE>: ExtiConfigured,
344    {
345        self.edge(EdgeTrigger::None);
346        self.unlisten();
347        self.unlisten_event();
348        self.clear_interrupt();
349        (ExtiLine { src: PinSrc }, self.src)
350    }
351}
352
353/// The EXTI lines, as handed out by [`ExtiExt::split`].
354#[allow(missing_docs)]
355pub struct ExtiLines {
356    pub line0: ExtiLine<0, PinSrc>,
357    pub line1: ExtiLine<1, PinSrc>,
358    pub line2: ExtiLine<2, PinSrc>,
359    pub line3: ExtiLine<3, PinSrc>,
360    pub line4: ExtiLine<4, PinSrc>,
361    pub line5: ExtiLine<5, PinSrc>,
362    pub line6: ExtiLine<6, PinSrc>,
363    pub line7: ExtiLine<7, PinSrc>,
364    pub line8: ExtiLine<8, PinSrc>,
365    pub line9: ExtiLine<9, PinSrc>,
366    pub line10: ExtiLine<10, PinSrc>,
367    pub line11: ExtiLine<11, PinSrc>,
368    pub line12: ExtiLine<12, PinSrc>,
369    pub line13: ExtiLine<13, PinSrc>,
370    pub line14: ExtiLine<14, PinSrc>,
371    pub line15: ExtiLine<15, PinSrc>,
372    /// Low voltage detector, in PMU.
373    pub line16: ExtiLine<16, InternalSrc>,
374    /// RTC alarm.
375    pub line17: ExtiLine<17, InternalSrc>,
376    /// RTC tamper and timestamp.
377    pub line19: ExtiLine<19, InternalSrc>,
378    /// Comparator output.
379    pub line21: ExtiLine<21, InternalSrc>,
380    /// USART0 wakeup.
381    pub line25: ExtiLine<25, InternalSrc>,
382}
383
384/// Splits the EXTI peripheral into its individual lines.
385pub trait ExtiExt {
386    /// The lines this peripheral hands out.
387    type Lines;
388
389    /// Hands out the lines.
390    ///
391    /// Consumes the peripheral, so the lines it returns are the only ones that
392    /// will ever exist.
393    fn split(self) -> Self::Lines;
394}
395
396impl ExtiExt for pac::Exti {
397    type Lines = ExtiLines;
398    fn split(self) -> Self::Lines {
399        ExtiLines {
400            line0: ExtiLine { src: PinSrc },
401            line1: ExtiLine { src: PinSrc },
402            line2: ExtiLine { src: PinSrc },
403            line3: ExtiLine { src: PinSrc },
404            line4: ExtiLine { src: PinSrc },
405            line5: ExtiLine { src: PinSrc },
406            line6: ExtiLine { src: PinSrc },
407            line7: ExtiLine { src: PinSrc },
408            line8: ExtiLine { src: PinSrc },
409            line9: ExtiLine { src: PinSrc },
410            line10: ExtiLine { src: PinSrc },
411            line11: ExtiLine { src: PinSrc },
412            line12: ExtiLine { src: PinSrc },
413            line13: ExtiLine { src: PinSrc },
414            line14: ExtiLine { src: PinSrc },
415            line15: ExtiLine { src: PinSrc },
416            line16: ExtiLine { src: InternalSrc },
417            line17: ExtiLine { src: InternalSrc },
418            line19: ExtiLine { src: InternalSrc },
419            line21: ExtiLine { src: InternalSrc },
420            line25: ExtiLine { src: InternalSrc },
421        }
422    }
423}