| 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 | |