diff options
| author | Raul Alimbekov <[email protected]> | 2025-12-16 09:05:22 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-16 09:05:22 +0300 |
| commit | c9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch) | |
| tree | 6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /embassy-stm32/src/exti.rs | |
| parent | cde24a3ef1117653ba5ed4184102b33f745782fb (diff) | |
| parent | 5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff) | |
Merge branch 'main' into main
Diffstat (limited to 'embassy-stm32/src/exti.rs')
| -rw-r--r-- | embassy-stm32/src/exti.rs | 165 |
1 files changed, 105 insertions, 60 deletions
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 9fce78f95..458174b5d 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -5,13 +5,16 @@ use core::marker::PhantomData; | |||
| 5 | use core::pin::Pin; | 5 | use core::pin::Pin; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use futures_util::FutureExt; | ||
| 10 | 11 | ||
| 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; | 12 | use crate::gpio::{AnyPin, ExtiPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; |
| 12 | use crate::pac::exti::regs::Lines; | 13 | use crate::interrupt::Interrupt as InterruptEnum; |
| 14 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType}; | ||
| 13 | use crate::pac::EXTI; | 15 | use crate::pac::EXTI; |
| 14 | use crate::{interrupt, pac, peripherals, Peri}; | 16 | use crate::pac::exti::regs::Lines; |
| 17 | use crate::{Peri, pac}; | ||
| 15 | 18 | ||
| 16 | const EXTI_COUNT: usize = 16; | 19 | const EXTI_COUNT: usize = 16; |
| 17 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; | 20 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; |
| @@ -31,11 +34,11 @@ fn cpu_regs() -> pac::exti::Exti { | |||
| 31 | EXTI | 34 | EXTI |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] | 37 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 35 | fn exticr_regs() -> pac::syscfg::Syscfg { | 38 | fn exticr_regs() -> pac::syscfg::Syscfg { |
| 36 | pac::SYSCFG | 39 | pac::SYSCFG |
| 37 | } | 40 | } |
| 38 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 41 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 39 | fn exticr_regs() -> pac::exti::Exti { | 42 | fn exticr_regs() -> pac::exti::Exti { |
| 40 | EXTI | 43 | EXTI |
| 41 | } | 44 | } |
| @@ -45,9 +48,9 @@ fn exticr_regs() -> pac::afio::Afio { | |||
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | unsafe fn on_irq() { | 50 | unsafe fn on_irq() { |
| 48 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 51 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 49 | let bits = EXTI.pr(0).read().0; | 52 | let bits = EXTI.pr(0).read().0; |
| 50 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 53 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 51 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; | 54 | let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; |
| 52 | 55 | ||
| 53 | // We don't handle or change any EXTI lines above 16. | 56 | // We don't handle or change any EXTI lines above 16. |
| @@ -62,16 +65,16 @@ unsafe fn on_irq() { | |||
| 62 | } | 65 | } |
| 63 | 66 | ||
| 64 | // Clear pending | 67 | // Clear pending |
| 65 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 68 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 66 | EXTI.pr(0).write_value(Lines(bits)); | 69 | EXTI.pr(0).write_value(Lines(bits)); |
| 67 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 70 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 68 | { | 71 | { |
| 69 | EXTI.rpr(0).write_value(Lines(bits)); | 72 | EXTI.rpr(0).write_value(Lines(bits)); |
| 70 | EXTI.fpr(0).write_value(Lines(bits)); | 73 | EXTI.fpr(0).write_value(Lines(bits)); |
| 71 | } | 74 | } |
| 72 | 75 | ||
| 73 | #[cfg(feature = "low-power")] | 76 | #[cfg(feature = "low-power")] |
| 74 | crate::low_power::on_wakeup_irq(); | 77 | crate::low_power::Executor::on_wakeup_irq_or_event(); |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 77 | struct BitIter(u32); | 80 | struct BitIter(u32); |
| @@ -105,10 +108,17 @@ impl<'d> Unpin for ExtiInput<'d> {} | |||
| 105 | 108 | ||
| 106 | impl<'d> ExtiInput<'d> { | 109 | impl<'d> ExtiInput<'d> { |
| 107 | /// Create an EXTI input. | 110 | /// Create an EXTI input. |
| 108 | pub fn new<T: GpioPin>(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull) -> Self { | 111 | /// |
| 109 | // Needed if using AnyPin+AnyChannel. | 112 | /// The Binding must bind the Channel's IRQ to [InterruptHandler]. |
| 110 | assert_eq!(pin.pin(), ch.number()); | 113 | pub fn new<T: ExtiPin + GpioPin>( |
| 111 | 114 | pin: Peri<'d, T>, | |
| 115 | _ch: Peri<'d, T::ExtiChannel>, | ||
| 116 | pull: Pull, | ||
| 117 | _irq: impl Binding< | ||
| 118 | <<T as ExtiPin>::ExtiChannel as Channel>::IRQ, | ||
| 119 | InterruptHandler<<<T as ExtiPin>::ExtiChannel as Channel>::IRQ>, | ||
| 120 | >, | ||
| 121 | ) -> Self { | ||
| 112 | Self { | 122 | Self { |
| 113 | pin: Input::new(pin, pull), | 123 | pin: Input::new(pin, pull), |
| 114 | } | 124 | } |
| @@ -133,7 +143,7 @@ impl<'d> ExtiInput<'d> { | |||
| 133 | /// | 143 | /// |
| 134 | /// This returns immediately if the pin is already high. | 144 | /// This returns immediately if the pin is already high. |
| 135 | pub async fn wait_for_high(&mut self) { | 145 | pub async fn wait_for_high(&mut self) { |
| 136 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); | 146 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true); |
| 137 | if self.is_high() { | 147 | if self.is_high() { |
| 138 | return; | 148 | return; |
| 139 | } | 149 | } |
| @@ -144,7 +154,7 @@ impl<'d> ExtiInput<'d> { | |||
| 144 | /// | 154 | /// |
| 145 | /// This returns immediately if the pin is already low. | 155 | /// This returns immediately if the pin is already low. |
| 146 | pub async fn wait_for_low(&mut self) { | 156 | pub async fn wait_for_low(&mut self) { |
| 147 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); | 157 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true); |
| 148 | if self.is_low() { | 158 | if self.is_low() { |
| 149 | return; | 159 | return; |
| 150 | } | 160 | } |
| @@ -155,19 +165,40 @@ impl<'d> ExtiInput<'d> { | |||
| 155 | /// | 165 | /// |
| 156 | /// If the pin is already high, it will wait for it to go low then back high. | 166 | /// If the pin is already high, it will wait for it to go low then back high. |
| 157 | pub async fn wait_for_rising_edge(&mut self) { | 167 | pub async fn wait_for_rising_edge(&mut self) { |
| 158 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await | 168 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true).await |
| 169 | } | ||
| 170 | |||
| 171 | /// Asynchronously wait until the pin sees a rising edge. | ||
| 172 | /// | ||
| 173 | /// If the pin is already high, it will wait for it to go low then back high. | ||
| 174 | pub fn poll_for_rising_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 175 | let _ = | ||
| 176 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, false).poll_unpin(cx); | ||
| 159 | } | 177 | } |
| 160 | 178 | ||
| 161 | /// Asynchronously wait until the pin sees a falling edge. | 179 | /// Asynchronously wait until the pin sees a falling edge. |
| 162 | /// | 180 | /// |
| 163 | /// If the pin is already low, it will wait for it to go high then back low. | 181 | /// If the pin is already low, it will wait for it to go high then back low. |
| 164 | pub async fn wait_for_falling_edge(&mut self) { | 182 | pub async fn wait_for_falling_edge(&mut self) { |
| 165 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await | 183 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true).await |
| 184 | } | ||
| 185 | |||
| 186 | /// Asynchronously wait until the pin sees a falling edge. | ||
| 187 | /// | ||
| 188 | /// If the pin is already low, it will wait for it to go high then back low. | ||
| 189 | pub fn poll_for_falling_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 190 | let _ = | ||
| 191 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, false).poll_unpin(cx); | ||
| 166 | } | 192 | } |
| 167 | 193 | ||
| 168 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | 194 | /// Asynchronously wait until the pin sees any edge (either rising or falling). |
| 169 | pub async fn wait_for_any_edge(&mut self) { | 195 | pub async fn wait_for_any_edge(&mut self) { |
| 170 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await | 196 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, true).await |
| 197 | } | ||
| 198 | |||
| 199 | /// Asynchronously wait until the pin sees any edge (either rising or falling). | ||
| 200 | pub fn poll_for_any_edge<'a>(&mut self, cx: &mut Context<'a>) { | ||
| 201 | let _ = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, false).poll_unpin(cx); | ||
| 171 | } | 202 | } |
| 172 | } | 203 | } |
| 173 | 204 | ||
| @@ -226,12 +257,13 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { | |||
| 226 | 257 | ||
| 227 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 258 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 228 | struct ExtiInputFuture<'a> { | 259 | struct ExtiInputFuture<'a> { |
| 229 | pin: u8, | 260 | pin: PinNumber, |
| 261 | drop: bool, | ||
| 230 | phantom: PhantomData<&'a mut AnyPin>, | 262 | phantom: PhantomData<&'a mut AnyPin>, |
| 231 | } | 263 | } |
| 232 | 264 | ||
| 233 | impl<'a> ExtiInputFuture<'a> { | 265 | impl<'a> ExtiInputFuture<'a> { |
| 234 | fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { | 266 | fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool, drop: bool) -> Self { |
| 235 | critical_section::with(|_| { | 267 | critical_section::with(|_| { |
| 236 | let pin = pin as usize; | 268 | let pin = pin as usize; |
| 237 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); | 269 | exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); |
| @@ -239,9 +271,9 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 239 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); | 271 | EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); |
| 240 | 272 | ||
| 241 | // clear pending bit | 273 | // clear pending bit |
| 242 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 274 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6)))] |
| 243 | EXTI.pr(0).write(|w| w.set_line(pin, true)); | 275 | EXTI.pr(0).write(|w| w.set_line(pin, true)); |
| 244 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 276 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50, exti_n6))] |
| 245 | { | 277 | { |
| 246 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); | 278 | EXTI.rpr(0).write(|w| w.set_line(pin, true)); |
| 247 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); | 279 | EXTI.fpr(0).write(|w| w.set_line(pin, true)); |
| @@ -252,6 +284,7 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 252 | 284 | ||
| 253 | Self { | 285 | Self { |
| 254 | pin, | 286 | pin, |
| 287 | drop, | ||
| 255 | phantom: PhantomData, | 288 | phantom: PhantomData, |
| 256 | } | 289 | } |
| 257 | } | 290 | } |
| @@ -259,10 +292,12 @@ impl<'a> ExtiInputFuture<'a> { | |||
| 259 | 292 | ||
| 260 | impl<'a> Drop for ExtiInputFuture<'a> { | 293 | impl<'a> Drop for ExtiInputFuture<'a> { |
| 261 | fn drop(&mut self) { | 294 | fn drop(&mut self) { |
| 262 | critical_section::with(|_| { | 295 | if self.drop { |
| 263 | let pin = self.pin as _; | 296 | critical_section::with(|_| { |
| 264 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); | 297 | let pin = self.pin as _; |
| 265 | }); | 298 | cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); |
| 299 | }); | ||
| 300 | } | ||
| 266 | } | 301 | } |
| 267 | } | 302 | } |
| 268 | 303 | ||
| @@ -302,7 +337,7 @@ macro_rules! foreach_exti_irq { | |||
| 302 | (EXTI15) => { $action!(EXTI15); }; | 337 | (EXTI15) => { $action!(EXTI15); }; |
| 303 | 338 | ||
| 304 | // plus the weird ones | 339 | // plus the weird ones |
| 305 | (EXTI0_1) => { $action!( EXTI0_1 ); }; | 340 | (EXTI0_1) => { $action!(EXTI0_1); }; |
| 306 | (EXTI15_10) => { $action!(EXTI15_10); }; | 341 | (EXTI15_10) => { $action!(EXTI15_10); }; |
| 307 | (EXTI15_4) => { $action!(EXTI15_4); }; | 342 | (EXTI15_4) => { $action!(EXTI15_4); }; |
| 308 | (EXTI1_0) => { $action!(EXTI1_0); }; | 343 | (EXTI1_0) => { $action!(EXTI1_0); }; |
| @@ -315,57 +350,67 @@ macro_rules! foreach_exti_irq { | |||
| 315 | }; | 350 | }; |
| 316 | } | 351 | } |
| 317 | 352 | ||
| 318 | macro_rules! impl_irq { | 353 | ///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler. |
| 319 | ($e:ident) => { | 354 | /// |
| 320 | #[allow(non_snake_case)] | 355 | /// It is generic over the [Interrupt](InterruptType) rather |
| 321 | #[cfg(feature = "rt")] | 356 | /// than the [Channel] because it should not be bound multiple |
| 322 | #[interrupt] | 357 | /// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector. |
| 323 | unsafe fn $e() { | 358 | // |
| 324 | on_irq() | 359 | // It technically doesn't need to be generic at all, except to satisfy the generic argument |
| 325 | } | 360 | // of [Handler]. All EXTI interrupts eventually land in the same on_irq() function. |
| 326 | }; | 361 | pub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> { |
| 362 | _phantom: PhantomData<T>, | ||
| 327 | } | 363 | } |
| 328 | 364 | ||
| 329 | foreach_exti_irq!(impl_irq); | 365 | impl<T: InterruptType> Handler<T> for InterruptHandler<T> { |
| 366 | unsafe fn on_interrupt() { | ||
| 367 | on_irq() | ||
| 368 | } | ||
| 369 | } | ||
| 330 | 370 | ||
| 331 | trait SealedChannel {} | 371 | trait SealedChannel {} |
| 332 | 372 | ||
| 333 | /// EXTI channel trait. | 373 | /// EXTI channel trait. |
| 334 | #[allow(private_bounds)] | 374 | #[allow(private_bounds)] |
| 335 | pub trait Channel: PeripheralType + SealedChannel + Sized { | 375 | pub trait Channel: PeripheralType + SealedChannel + Sized { |
| 336 | /// Get the EXTI channel number. | 376 | /// EXTI channel number. |
| 337 | fn number(&self) -> u8; | 377 | fn number(&self) -> PinNumber; |
| 378 | /// [Enum-level Interrupt](InterruptEnum), which may be the same for multiple channels. | ||
| 379 | fn irq(&self) -> InterruptEnum; | ||
| 380 | /// [Type-level Interrupt](InterruptType), which may be the same for multiple channels. | ||
| 381 | type IRQ: InterruptType; | ||
| 338 | } | 382 | } |
| 339 | 383 | ||
| 340 | /// Type-erased EXTI channel. | 384 | //Doc isn't hidden in order to surface the explanation to users, even though it's completely inoperable, not just deprecated. |
| 385 | //Entire type along with doc can probably be removed after deprecation has appeared in a release once. | ||
| 386 | /// Deprecated type-erased EXTI channel. | ||
| 341 | /// | 387 | /// |
| 342 | /// This represents ownership over any EXTI channel, known at runtime. | 388 | /// Support for AnyChannel was removed in order to support manually bindable EXTI interrupts via bind_interrupts; [ExtiInput::new()] |
| 389 | /// must know the required IRQ at compile time, and therefore cannot support type-erased channels. | ||
| 390 | #[deprecated = "type-erased EXTI channels are no longer supported, in order to support manually bindable EXTI interrupts (more info: https://github.com/embassy-rs/embassy/pull/4922)"] | ||
| 343 | pub struct AnyChannel { | 391 | pub struct AnyChannel { |
| 344 | number: u8, | 392 | #[allow(unused)] |
| 345 | } | 393 | number: PinNumber, |
| 346 | |||
| 347 | impl_peripheral!(AnyChannel); | ||
| 348 | impl SealedChannel for AnyChannel {} | ||
| 349 | impl Channel for AnyChannel { | ||
| 350 | fn number(&self) -> u8 { | ||
| 351 | self.number | ||
| 352 | } | ||
| 353 | } | 394 | } |
| 354 | 395 | ||
| 355 | macro_rules! impl_exti { | 396 | macro_rules! impl_exti { |
| 356 | ($type:ident, $number:expr) => { | 397 | ($type:ident, $number:expr) => { |
| 357 | impl SealedChannel for peripherals::$type {} | 398 | impl SealedChannel for crate::peripherals::$type {} |
| 358 | impl Channel for peripherals::$type { | 399 | impl Channel for crate::peripherals::$type { |
| 359 | fn number(&self) -> u8 { | 400 | fn number(&self) -> PinNumber { |
| 360 | $number | 401 | $number |
| 361 | } | 402 | } |
| 403 | fn irq(&self) -> InterruptEnum { | ||
| 404 | crate::_generated::peripheral_interrupts::EXTI::$type::IRQ | ||
| 405 | } | ||
| 406 | type IRQ = crate::_generated::peripheral_interrupts::EXTI::$type; | ||
| 362 | } | 407 | } |
| 363 | 408 | ||
| 364 | impl From<peripherals::$type> for AnyChannel { | 409 | //Still here to surface deprecation messages to the user - remove when removing AnyChannel |
| 365 | fn from(val: peripherals::$type) -> Self { | 410 | #[allow(deprecated)] |
| 366 | Self { | 411 | impl From<crate::peripherals::$type> for AnyChannel { |
| 367 | number: val.number() as u8, | 412 | fn from(_val: crate::peripherals::$type) -> Self { |
| 368 | } | 413 | Self { number: $number } |
| 369 | } | 414 | } |
| 370 | } | 415 | } |
| 371 | }; | 416 | }; |
