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}