diff options
| author | WillaWillNot <[email protected]> | 2025-11-14 17:00:23 -0500 |
|---|---|---|
| committer | WillaWillNot <[email protected]> | 2025-11-19 17:31:50 -0500 |
| commit | de4d7f56473df58d9b3fa8ec4917ab86550005ae (patch) | |
| tree | 2ece2dff81b7fae59c53557257a89ca5e72fa6b3 | |
| parent | 117b875134f4edcb32ec327146c2c2af01a50672 (diff) | |
Added type-erased AnyBinding for interrupt-handler bindings, and changed Exti driver to accept custom bindings without sacrificing its ability to accept type-erased Channels
| -rw-r--r-- | embassy-hal-internal/src/interrupt.rs | 55 | ||||
| -rw-r--r-- | embassy-stm32/src/exti.rs | 59 |
2 files changed, 97 insertions, 17 deletions
diff --git a/embassy-hal-internal/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs index ce6057e2d..c0ea666da 100644 --- a/embassy-hal-internal/src/interrupt.rs +++ b/embassy-hal-internal/src/interrupt.rs | |||
| @@ -124,6 +124,10 @@ macro_rules! interrupt_mod { | |||
| 124 | /// | 124 | /// |
| 125 | /// This function must ONLY be called from the interrupt handler for `I`. | 125 | /// This function must ONLY be called from the interrupt handler for `I`. |
| 126 | unsafe fn on_interrupt(); | 126 | unsafe fn on_interrupt(); |
| 127 | |||
| 128 | /// Source ID of the Handler. No need to override the default outside of internal implementations, | ||
| 129 | /// where it will always be HandlerType::User. | ||
| 130 | const SOURCE_ID: HandlerType = HandlerType::User; | ||
| 127 | } | 131 | } |
| 128 | 132 | ||
| 129 | /// Compile-time assertion that an interrupt has been bound to a handler. | 133 | /// Compile-time assertion that an interrupt has been bound to a handler. |
| @@ -137,7 +141,56 @@ macro_rules! interrupt_mod { | |||
| 137 | /// to be called every time the `I` interrupt fires. | 141 | /// to be called every time the `I` interrupt fires. |
| 138 | /// | 142 | /// |
| 139 | /// This allows drivers to check bindings at compile-time. | 143 | /// This allows drivers to check bindings at compile-time. |
| 140 | pub unsafe trait Binding<I: Interrupt, H: Handler<I>> {} | 144 | pub unsafe trait Binding<I: Interrupt, H: Handler<I>> { |
| 145 | /// Obtain a type-erased Binding. | ||
| 146 | /// | ||
| 147 | /// If using the `bind_interrupts!` macro, you will likely have to use a fully qualified path | ||
| 148 | /// to call this on the output struct: `<Irqs as Binding<Interrupt, Hander>>::into_any()` | ||
| 149 | fn into_any() -> AnyBinding { | ||
| 150 | AnyBinding { | ||
| 151 | irq: I::IRQ, | ||
| 152 | handler_source: H::SOURCE_ID, | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | #[doc(hidden)] | ||
| 158 | #[derive(Copy, Clone)] | ||
| 159 | pub struct PrivateHandlerType { | ||
| 160 | pub(crate) _private: (), | ||
| 161 | } | ||
| 162 | impl PrivateHandlerType { | ||
| 163 | pub(crate) const fn new() -> Self { | ||
| 164 | Self { | ||
| 165 | _private: (), | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Driver which defined the Handler. Always User for user-defined handlers. | ||
| 171 | #[derive(Copy, Clone)] | ||
| 172 | pub enum HandlerType { | ||
| 173 | User, | ||
| 174 | Embassy(PrivateHandlerType), | ||
| 175 | EmbassyStm32Exti(PrivateHandlerType), | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Type-erased Binding. | ||
| 179 | /// | ||
| 180 | /// Useful for proving a particular binding has been made to a driver which also accepts | ||
| 181 | /// type-erased peripheral arguments that hide the necessary Interrupt type at compile time. | ||
| 182 | pub struct AnyBinding { | ||
| 183 | irq: super::Interrupt, | ||
| 184 | handler_source: HandlerType, | ||
| 185 | } | ||
| 186 | impl AnyBinding { | ||
| 187 | pub const fn irq(&self) -> super::Interrupt { | ||
| 188 | self.irq | ||
| 189 | } | ||
| 190 | pub const fn source(&self) -> HandlerType { | ||
| 191 | self.handler_source | ||
| 192 | } | ||
| 193 | } | ||
| 141 | } | 194 | } |
| 142 | } | 195 | } |
| 143 | }; | 196 | }; |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 899d5e677..172435caa 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -10,9 +10,11 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 10 | use futures_util::FutureExt; | 10 | use futures_util::FutureExt; |
| 11 | 11 | ||
| 12 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; | 12 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; |
| 13 | use crate::interrupt::Interrupt as InterruptEnum; | ||
| 14 | use crate::interrupt::typelevel::{AnyBinding, HandlerType, Interrupt as InterruptType, PrivateHandlerType}; | ||
| 13 | use crate::pac::EXTI; | 15 | use crate::pac::EXTI; |
| 14 | use crate::pac::exti::regs::Lines; | 16 | use crate::pac::exti::regs::Lines; |
| 15 | use crate::{Peri, interrupt, pac, peripherals}; | 17 | use crate::{Peri, pac, peripherals}; |
| 16 | 18 | ||
| 17 | const EXTI_COUNT: usize = 16; | 19 | const EXTI_COUNT: usize = 16; |
| 18 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; | 20 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; |
| @@ -106,15 +108,24 @@ impl<'d> Unpin for ExtiInput<'d> {} | |||
| 106 | 108 | ||
| 107 | impl<'d> ExtiInput<'d> { | 109 | impl<'d> ExtiInput<'d> { |
| 108 | /// Create an EXTI input. | 110 | /// Create an EXTI input. |
| 109 | pub fn new<T: GpioPin>(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull) -> Self { | 111 | /// |
| 112 | /// The interrupt [Binding] must be type-erased to [AnyBinding] via [Binding::into_any()] in order | ||
| 113 | /// to support type-erased [AnyChannel] arguments. | ||
| 114 | /// | ||
| 115 | /// The Binding must bind the Channel's IRQ to [InterruptHandler]. | ||
| 116 | pub fn new<T: GpioPin>(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull, binding: AnyBinding) -> Self { | ||
| 110 | // Needed if using AnyPin+AnyChannel. | 117 | // Needed if using AnyPin+AnyChannel. |
| 111 | assert_eq!(pin.pin(), ch.number()); | 118 | assert_eq!(pin.pin(), ch.number()); |
| 119 | assert_eq!(ch.irq(), binding.irq()); | ||
| 120 | assert!(matches!(binding.source(), HandlerType::EmbassyStm32Exti(_))); | ||
| 112 | 121 | ||
| 113 | Self { | 122 | Self { |
| 114 | pin: Input::new(pin, pull), | 123 | pin: Input::new(pin, pull), |
| 115 | } | 124 | } |
| 116 | } | 125 | } |
| 117 | 126 | ||
| 127 | //pub fn new<P: GpioPin<ExtiChannel: Channel>, I: Instance, C: Channel>( | ||
| 128 | |||
| 118 | /// Get whether the pin is high. | 129 | /// Get whether the pin is high. |
| 119 | pub fn is_high(&self) -> bool { | 130 | pub fn is_high(&self) -> bool { |
| 120 | self.pin.is_high() | 131 | self.pin.is_high() |
| @@ -328,7 +339,7 @@ macro_rules! foreach_exti_irq { | |||
| 328 | (EXTI15) => { $action!(EXTI15); }; | 339 | (EXTI15) => { $action!(EXTI15); }; |
| 329 | 340 | ||
| 330 | // plus the weird ones | 341 | // plus the weird ones |
| 331 | (EXTI0_1) => { $action!( EXTI0_1 ); }; | 342 | (EXTI0_1) => { $action!(EXTI0_1); }; |
| 332 | (EXTI15_10) => { $action!(EXTI15_10); }; | 343 | (EXTI15_10) => { $action!(EXTI15_10); }; |
| 333 | (EXTI15_4) => { $action!(EXTI15_4); }; | 344 | (EXTI15_4) => { $action!(EXTI15_4); }; |
| 334 | (EXTI1_0) => { $action!(EXTI1_0); }; | 345 | (EXTI1_0) => { $action!(EXTI1_0); }; |
| @@ -341,18 +352,25 @@ macro_rules! foreach_exti_irq { | |||
| 341 | }; | 352 | }; |
| 342 | } | 353 | } |
| 343 | 354 | ||
| 344 | macro_rules! impl_irq { | 355 | ///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler. |
| 345 | ($e:ident) => { | 356 | /// |
| 346 | #[allow(non_snake_case)] | 357 | /// It is generic over the [Interrupt](crate::interrupt::typelevel::Interrupt) rather |
| 347 | #[cfg(feature = "rt")] | 358 | /// than the [Instance](crate::exti::Instance) because it should not be bound multiple |
| 348 | #[interrupt] | 359 | /// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector. |
| 349 | unsafe fn $e() { | 360 | // |
| 350 | on_irq() | 361 | // It technically doesn't need to be generic at all, except to satisfy the generic argument |
| 351 | } | 362 | // of [Handler](crate::interrupt::typelevel::Handler). All EXTI interrupts eventually |
| 352 | }; | 363 | // land in the same on_irq() function. |
| 364 | pub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> { | ||
| 365 | _phantom: PhantomData<T>, | ||
| 353 | } | 366 | } |
| 354 | 367 | ||
| 355 | foreach_exti_irq!(impl_irq); | 368 | impl<T: crate::interrupt::typelevel::Interrupt> crate::interrupt::typelevel::Handler<T> for InterruptHandler<T> { |
| 369 | const SOURCE_ID: HandlerType = HandlerType::EmbassyStm32Exti(PrivateHandlerType::new()); | ||
| 370 | unsafe fn on_interrupt() { | ||
| 371 | on_irq() | ||
| 372 | } | ||
| 373 | } | ||
| 356 | 374 | ||
| 357 | trait SealedChannel {} | 375 | trait SealedChannel {} |
| 358 | 376 | ||
| @@ -361,6 +379,8 @@ trait SealedChannel {} | |||
| 361 | pub trait Channel: PeripheralType + SealedChannel + Sized { | 379 | pub trait Channel: PeripheralType + SealedChannel + Sized { |
| 362 | /// Get the EXTI channel number. | 380 | /// Get the EXTI channel number. |
| 363 | fn number(&self) -> PinNumber; | 381 | fn number(&self) -> PinNumber; |
| 382 | /// Get the EXTI IRQ, which may be the same for multiple channels | ||
| 383 | fn irq(&self) -> InterruptEnum; | ||
| 364 | } | 384 | } |
| 365 | 385 | ||
| 366 | /// Type-erased EXTI channel. | 386 | /// Type-erased EXTI channel. |
| @@ -368,6 +388,7 @@ pub trait Channel: PeripheralType + SealedChannel + Sized { | |||
| 368 | /// This represents ownership over any EXTI channel, known at runtime. | 388 | /// This represents ownership over any EXTI channel, known at runtime. |
| 369 | pub struct AnyChannel { | 389 | pub struct AnyChannel { |
| 370 | number: PinNumber, | 390 | number: PinNumber, |
| 391 | irq: InterruptEnum, | ||
| 371 | } | 392 | } |
| 372 | 393 | ||
| 373 | impl_peripheral!(AnyChannel); | 394 | impl_peripheral!(AnyChannel); |
| @@ -376,6 +397,9 @@ impl Channel for AnyChannel { | |||
| 376 | fn number(&self) -> PinNumber { | 397 | fn number(&self) -> PinNumber { |
| 377 | self.number | 398 | self.number |
| 378 | } | 399 | } |
| 400 | fn irq(&self) -> InterruptEnum { | ||
| 401 | self.irq | ||
| 402 | } | ||
| 379 | } | 403 | } |
| 380 | 404 | ||
| 381 | macro_rules! impl_exti { | 405 | macro_rules! impl_exti { |
| @@ -385,12 +409,15 @@ macro_rules! impl_exti { | |||
| 385 | fn number(&self) -> PinNumber { | 409 | fn number(&self) -> PinNumber { |
| 386 | $number | 410 | $number |
| 387 | } | 411 | } |
| 412 | fn irq(&self) -> InterruptEnum { | ||
| 413 | crate::_generated::peripheral_interrupts::EXTI::$type::IRQ | ||
| 414 | } | ||
| 388 | } | 415 | } |
| 389 | |||
| 390 | impl From<peripherals::$type> for AnyChannel { | 416 | impl From<peripherals::$type> for AnyChannel { |
| 391 | fn from(val: peripherals::$type) -> Self { | 417 | fn from(_val: peripherals::$type) -> Self { |
| 392 | Self { | 418 | Self { |
| 393 | number: val.number() as PinNumber, | 419 | number: $number, |
| 420 | irq: crate::_generated::peripheral_interrupts::EXTI::$type::IRQ, | ||
| 394 | } | 421 | } |
| 395 | } | 422 | } |
| 396 | } | 423 | } |
