1 | //! External Interrupts (EXTI) |
2 | use core::convert::Infallible; |
3 | use core::future::Future; |
4 | use core::marker::PhantomData; |
5 | use core::pin::Pin; |
6 | use core::task::{Context, Poll}; |
7 | |
8 | use embassy_hal_internal::{impl_peripheral, into_ref}; |
9 | use embassy_sync::waitqueue::AtomicWaker; |
10 | |
11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; |
12 | use crate::pac::exti::regs::Lines; |
13 | use crate::pac::EXTI; |
14 | use crate::{interrupt, pac, peripherals, Peripheral}; |
15 | |
16 | const EXTI_COUNT: usize = 16; |
17 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; |
18 | |
19 | #[cfg (all(exti_w, feature = "_core-cm0p" ))] |
20 | fn cpu_regs() -> pac::exti::Cpu { |
21 | EXTI.cpu(1) |
22 | } |
23 | |
24 | #[cfg (all(exti_w, not(feature = "_core-cm0p" )))] |
25 | fn cpu_regs() -> pac::exti::Cpu { |
26 | EXTI.cpu(0) |
27 | } |
28 | |
29 | #[cfg (not(exti_w))] |
30 | fn cpu_regs() -> pac::exti::Exti { |
31 | EXTI |
32 | } |
33 | |
34 | #[cfg (not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] |
35 | fn exticr_regs() -> pac::syscfg::Syscfg { |
36 | pac::SYSCFG |
37 | } |
38 | #[cfg (any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] |
39 | fn exticr_regs() -> pac::exti::Exti { |
40 | EXTI |
41 | } |
42 | #[cfg (gpio_v1)] |
43 | fn exticr_regs() -> pac::afio::Afio { |
44 | pac::AFIO |
45 | } |
46 | |
47 | unsafe fn on_irq() { |
48 | #[cfg (not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] |
49 | let bits = EXTI.pr(0).read().0; |
50 | #[cfg (any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] |
51 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; |
52 | |
53 | // We don't handle or change any EXTI lines above 16. |
54 | let bits = bits & 0x0000FFFF; |
55 | |
56 | // Mask all the channels that fired. |
57 | cpu_regs().imr(0).modify(|w| w.0 &= !bits); |
58 | |
59 | // Wake the tasks |
60 | for pin in BitIter(bits) { |
61 | EXTI_WAKERS[pin as usize].wake(); |
62 | } |
63 | |
64 | // Clear pending |
65 | #[cfg (not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] |
66 | EXTI.pr(0).write_value(Lines(bits)); |
67 | #[cfg (any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] |
68 | { |
69 | EXTI.rpr(0).write_value(Lines(bits)); |
70 | EXTI.fpr(0).write_value(Lines(bits)); |
71 | } |
72 | |
73 | #[cfg (feature = "low-power" )] |
74 | crate::low_power::on_wakeup_irq(); |
75 | } |
76 | |
77 | struct BitIter(u32); |
78 | |
79 | impl Iterator for BitIter { |
80 | type Item = u32; |
81 | |
82 | fn next(&mut self) -> Option<Self::Item> { |
83 | match self.0.trailing_zeros() { |
84 | 32 => None, |
85 | b: u32 => { |
86 | self.0 &= !(1 << b); |
87 | Some(b) |
88 | } |
89 | } |
90 | } |
91 | } |
92 | |
93 | /// EXTI input driver. |
94 | /// |
95 | /// This driver augments a GPIO `Input` with EXTI functionality. EXTI is not |
96 | /// built into `Input` itself because it needs to take ownership of the corresponding |
97 | /// EXTI channel, which is a limited resource. |
98 | /// |
99 | /// Pins PA5, PB5, PC5... all use EXTI channel 5, so you can't use EXTI on, say, PA5 and PC5 at the same time. |
100 | pub struct ExtiInput<'d> { |
101 | pin: Input<'d>, |
102 | } |
103 | |
104 | impl<'d> Unpin for ExtiInput<'d> {} |
105 | |
106 | impl<'d> ExtiInput<'d> { |
107 | /// Create an EXTI input. |
108 | pub fn new<T: GpioPin>( |
109 | pin: impl Peripheral<P = T> + 'd, |
110 | ch: impl Peripheral<P = T::ExtiChannel> + 'd, |
111 | pull: Pull, |
112 | ) -> Self { |
113 | into_ref!(pin, ch); |
114 | |
115 | // Needed if using AnyPin+AnyChannel. |
116 | assert_eq!(pin.pin(), ch.number()); |
117 | |
118 | Self { |
119 | pin: Input::new(pin, pull), |
120 | } |
121 | } |
122 | |
123 | /// Get whether the pin is high. |
124 | pub fn is_high(&self) -> bool { |
125 | self.pin.is_high() |
126 | } |
127 | |
128 | /// Get whether the pin is low. |
129 | pub fn is_low(&self) -> bool { |
130 | self.pin.is_low() |
131 | } |
132 | |
133 | /// Get the pin level. |
134 | pub fn get_level(&self) -> Level { |
135 | self.pin.get_level() |
136 | } |
137 | |
138 | /// Asynchronously wait until the pin is high. |
139 | /// |
140 | /// This returns immediately if the pin is already high. |
141 | pub async fn wait_for_high(&mut self) { |
142 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); |
143 | if self.is_high() { |
144 | return; |
145 | } |
146 | fut.await |
147 | } |
148 | |
149 | /// Asynchronously wait until the pin is low. |
150 | /// |
151 | /// This returns immediately if the pin is already low. |
152 | pub async fn wait_for_low(&mut self) { |
153 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); |
154 | if self.is_low() { |
155 | return; |
156 | } |
157 | fut.await |
158 | } |
159 | |
160 | /// Asynchronously wait until the pin sees a rising edge. |
161 | /// |
162 | /// If the pin is already high, it will wait for it to go low then back high. |
163 | pub async fn wait_for_rising_edge(&mut self) { |
164 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await |
165 | } |
166 | |
167 | /// Asynchronously wait until the pin sees a falling edge. |
168 | /// |
169 | /// If the pin is already low, it will wait for it to go high then back low. |
170 | pub async fn wait_for_falling_edge(&mut self) { |
171 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await |
172 | } |
173 | |
174 | /// Asynchronously wait until the pin sees any edge (either rising or falling). |
175 | pub async fn wait_for_any_edge(&mut self) { |
176 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await |
177 | } |
178 | } |
179 | |
180 | impl<'d> embedded_hal_02::digital::v2::InputPin for ExtiInput<'d> { |
181 | type Error = Infallible; |
182 | |
183 | fn is_high(&self) -> Result<bool, Self::Error> { |
184 | Ok(self.is_high()) |
185 | } |
186 | |
187 | fn is_low(&self) -> Result<bool, Self::Error> { |
188 | Ok(self.is_low()) |
189 | } |
190 | } |
191 | |
192 | impl<'d> embedded_hal_1::digital::ErrorType for ExtiInput<'d> { |
193 | type Error = Infallible; |
194 | } |
195 | |
196 | impl<'d> embedded_hal_1::digital::InputPin for ExtiInput<'d> { |
197 | fn is_high(&mut self) -> Result<bool, Self::Error> { |
198 | Ok((*self).is_high()) |
199 | } |
200 | |
201 | fn is_low(&mut self) -> Result<bool, Self::Error> { |
202 | Ok((*self).is_low()) |
203 | } |
204 | } |
205 | |
206 | impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { |
207 | async fn wait_for_high(&mut self) -> Result<(), Self::Error> { |
208 | self.wait_for_high().await; |
209 | Ok(()) |
210 | } |
211 | |
212 | async fn wait_for_low(&mut self) -> Result<(), Self::Error> { |
213 | self.wait_for_low().await; |
214 | Ok(()) |
215 | } |
216 | |
217 | async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { |
218 | self.wait_for_rising_edge().await; |
219 | Ok(()) |
220 | } |
221 | |
222 | async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { |
223 | self.wait_for_falling_edge().await; |
224 | Ok(()) |
225 | } |
226 | |
227 | async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { |
228 | self.wait_for_any_edge().await; |
229 | Ok(()) |
230 | } |
231 | } |
232 | |
233 | #[must_use = "futures do nothing unless you `.await` or poll them" ] |
234 | struct ExtiInputFuture<'a> { |
235 | pin: u8, |
236 | phantom: PhantomData<&'a mut AnyPin>, |
237 | } |
238 | |
239 | impl<'a> ExtiInputFuture<'a> { |
240 | fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { |
241 | critical_section::with(|_| { |
242 | let pin = pin as usize; |
243 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); |
244 | EXTI.rtsr(0).modify(|w| w.set_line(pin, rising)); |
245 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); |
246 | |
247 | // clear pending bit |
248 | #[cfg (not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] |
249 | EXTI.pr(0).write(|w| w.set_line(pin, true)); |
250 | #[cfg (any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] |
251 | { |
252 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); |
253 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); |
254 | } |
255 | |
256 | cpu_regs().imr(0).modify(|w| w.set_line(pin, true)); |
257 | }); |
258 | |
259 | Self { |
260 | pin, |
261 | phantom: PhantomData, |
262 | } |
263 | } |
264 | } |
265 | |
266 | impl<'a> Drop for ExtiInputFuture<'a> { |
267 | fn drop(&mut self) { |
268 | critical_section::with(|_| { |
269 | let pin: usize = self.pin as _; |
270 | cpu_regs().imr(0).modify(|w: &mut Lines| w.set_line(n:pin, val:false)); |
271 | }); |
272 | } |
273 | } |
274 | |
275 | impl<'a> Future for ExtiInputFuture<'a> { |
276 | type Output = (); |
277 | |
278 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
279 | EXTI_WAKERS[self.pin as usize].register(cx.waker()); |
280 | |
281 | let imr: Lines = cpu_regs().imr(0).read(); |
282 | if !imr.line(self.pin as _) { |
283 | Poll::Ready(()) |
284 | } else { |
285 | Poll::Pending |
286 | } |
287 | } |
288 | } |
289 | |
290 | macro_rules! foreach_exti_irq { |
291 | ($action:ident) => { |
292 | foreach_interrupt!( |
293 | (EXTI0) => { $action!(EXTI0); }; |
294 | (EXTI1) => { $action!(EXTI1); }; |
295 | (EXTI2) => { $action!(EXTI2); }; |
296 | (EXTI3) => { $action!(EXTI3); }; |
297 | (EXTI4) => { $action!(EXTI4); }; |
298 | (EXTI5) => { $action!(EXTI5); }; |
299 | (EXTI6) => { $action!(EXTI6); }; |
300 | (EXTI7) => { $action!(EXTI7); }; |
301 | (EXTI8) => { $action!(EXTI8); }; |
302 | (EXTI9) => { $action!(EXTI9); }; |
303 | (EXTI10) => { $action!(EXTI10); }; |
304 | (EXTI11) => { $action!(EXTI11); }; |
305 | (EXTI12) => { $action!(EXTI12); }; |
306 | (EXTI13) => { $action!(EXTI13); }; |
307 | (EXTI14) => { $action!(EXTI14); }; |
308 | (EXTI15) => { $action!(EXTI15); }; |
309 | |
310 | // plus the weird ones |
311 | (EXTI0_1) => { $action!( EXTI0_1 ); }; |
312 | (EXTI15_10) => { $action!(EXTI15_10); }; |
313 | (EXTI15_4) => { $action!(EXTI15_4); }; |
314 | (EXTI1_0) => { $action!(EXTI1_0); }; |
315 | (EXTI2_3) => { $action!(EXTI2_3); }; |
316 | (EXTI2_TSC) => { $action!(EXTI2_TSC); }; |
317 | (EXTI3_2) => { $action!(EXTI3_2); }; |
318 | (EXTI4_15) => { $action!(EXTI4_15); }; |
319 | (EXTI9_5) => { $action!(EXTI9_5); }; |
320 | ); |
321 | }; |
322 | } |
323 | |
324 | macro_rules! impl_irq { |
325 | ($e:ident) => { |
326 | #[allow(non_snake_case)] |
327 | #[cfg(feature = "rt" )] |
328 | #[interrupt] |
329 | unsafe fn $e() { |
330 | on_irq() |
331 | } |
332 | }; |
333 | } |
334 | |
335 | foreach_exti_irq!(impl_irq); |
336 | |
337 | trait SealedChannel {} |
338 | |
339 | /// EXTI channel trait. |
340 | #[allow (private_bounds)] |
341 | pub trait Channel: SealedChannel + Sized { |
342 | /// Get the EXTI channel number. |
343 | fn number(&self) -> u8; |
344 | |
345 | /// Type-erase (degrade) this channel into an `AnyChannel`. |
346 | /// |
347 | /// This converts EXTI channel singletons (`EXTI0`, `EXTI1`, ...), which |
348 | /// are all different types, into the same type. It is useful for |
349 | /// creating arrays of channels, or avoiding generics. |
350 | fn degrade(self) -> AnyChannel { |
351 | AnyChannel { |
352 | number: self.number() as u8, |
353 | } |
354 | } |
355 | } |
356 | |
357 | /// Type-erased (degraded) EXTI channel. |
358 | /// |
359 | /// This represents ownership over any EXTI channel, known at runtime. |
360 | pub struct AnyChannel { |
361 | number: u8, |
362 | } |
363 | |
364 | impl_peripheral!(AnyChannel); |
365 | impl SealedChannel for AnyChannel {} |
366 | impl Channel for AnyChannel { |
367 | fn number(&self) -> u8 { |
368 | self.number |
369 | } |
370 | } |
371 | |
372 | macro_rules! impl_exti { |
373 | ($type:ident, $number:expr) => { |
374 | impl SealedChannel for peripherals::$type {} |
375 | impl Channel for peripherals::$type { |
376 | fn number(&self) -> u8 { |
377 | $number |
378 | } |
379 | } |
380 | }; |
381 | } |
382 | |
383 | impl_exti!(EXTI0, 0); |
384 | impl_exti!(EXTI1, 1); |
385 | impl_exti!(EXTI2, 2); |
386 | impl_exti!(EXTI3, 3); |
387 | impl_exti!(EXTI4, 4); |
388 | impl_exti!(EXTI5, 5); |
389 | impl_exti!(EXTI6, 6); |
390 | impl_exti!(EXTI7, 7); |
391 | impl_exti!(EXTI8, 8); |
392 | impl_exti!(EXTI9, 9); |
393 | impl_exti!(EXTI10, 10); |
394 | impl_exti!(EXTI11, 11); |
395 | impl_exti!(EXTI12, 12); |
396 | impl_exti!(EXTI13, 13); |
397 | impl_exti!(EXTI14, 14); |
398 | impl_exti!(EXTI15, 15); |
399 | |
400 | macro_rules! enable_irq { |
401 | ($e:ident) => { |
402 | crate::interrupt::typelevel::$e::enable(); |
403 | }; |
404 | } |
405 | |
406 | /// safety: must be called only once |
407 | pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { |
408 | use crate::interrupt::typelevel::Interrupt; |
409 | |
410 | foreach_exti_irq!(enable_irq); |
411 | } |
412 | |