aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWillaWillNot <[email protected]>2025-11-14 17:00:23 -0500
committerWillaWillNot <[email protected]>2025-11-19 17:31:50 -0500
commitde4d7f56473df58d9b3fa8ec4917ab86550005ae (patch)
tree2ece2dff81b7fae59c53557257a89ca5e72fa6b3
parent117b875134f4edcb32ec327146c2c2af01a50672 (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.rs55
-rw-r--r--embassy-stm32/src/exti.rs59
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;
10use futures_util::FutureExt; 10use futures_util::FutureExt;
11 11
12use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; 12use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull};
13use crate::interrupt::Interrupt as InterruptEnum;
14use crate::interrupt::typelevel::{AnyBinding, HandlerType, Interrupt as InterruptType, PrivateHandlerType};
13use crate::pac::EXTI; 15use crate::pac::EXTI;
14use crate::pac::exti::regs::Lines; 16use crate::pac::exti::regs::Lines;
15use crate::{Peri, interrupt, pac, peripherals}; 17use crate::{Peri, pac, peripherals};
16 18
17const EXTI_COUNT: usize = 16; 19const EXTI_COUNT: usize = 16;
18static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; 20static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT];
@@ -106,15 +108,24 @@ impl<'d> Unpin for ExtiInput<'d> {}
106 108
107impl<'d> ExtiInput<'d> { 109impl<'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
344macro_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.
364pub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> {
365 _phantom: PhantomData<T>,
353} 366}
354 367
355foreach_exti_irq!(impl_irq); 368impl<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
357trait SealedChannel {} 375trait SealedChannel {}
358 376
@@ -361,6 +379,8 @@ trait SealedChannel {}
361pub trait Channel: PeripheralType + SealedChannel + Sized { 379pub 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.
369pub struct AnyChannel { 389pub struct AnyChannel {
370 number: PinNumber, 390 number: PinNumber,
391 irq: InterruptEnum,
371} 392}
372 393
373impl_peripheral!(AnyChannel); 394impl_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
381macro_rules! impl_exti { 405macro_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 }