Add InterruptHandler wrapper (#1299)

* Add InterruptHandler

* Which takes the handler and a prio.
* Updates the `#[handler]` macro to create this for us.

* changelog
This commit is contained in:
Scott Mabin 2024-03-21 15:45:35 +00:00 committed by GitHub
parent 25f509ce74
commit 93d31e76d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 82 additions and 24 deletions

View File

@ -368,6 +368,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream { pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
use darling::ast::NestedMeta; use darling::ast::NestedMeta;
use proc_macro::Span; use proc_macro::Span;
use proc_macro2::Ident;
use proc_macro_crate::{crate_name, FoundCrate};
use proc_macro_error::abort; use proc_macro_error::abort;
use syn::{parse::Error as ParseError, spanned::Spanned, ItemFn, ReturnType, Type}; use syn::{parse::Error as ParseError, spanned::Spanned, ItemFn, ReturnType, Type};
@ -417,10 +419,28 @@ pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
.into(); .into();
} }
f.sig.abi = syn::parse_quote_spanned!(original_span => extern "C"); let root = Ident::new(
if let Ok(FoundCrate::Name(ref name)) = crate_name("esp-hal") {
&name
} else {
"crate"
},
Span::call_site().into(),
);
quote::quote_spanned!( original_span => f.sig.abi = syn::parse_quote_spanned!(original_span => extern "C");
let orig = f.sig.ident;
f.sig.ident = Ident::new(
&format!("__esp_hal_internal_{}", orig),
proc_macro2::Span::call_site(),
);
let new = f.sig.ident.clone();
quote::quote_spanned!(original_span =>
#f #f
#[allow(non_upper_case_globals)]
static #orig: #root::interrupt::InterruptHandler = #root::interrupt::InterruptHandler::new(#new, #root::interrupt::Priority::min());
) )
.into() .into()
} }

View File

@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensuring that the random number generator is TRNG. (#1200) - Ensuring that the random number generator is TRNG. (#1200)
- ESP32-C6: Add timer wakeup source for deepsleep (#1201) - ESP32-C6: Add timer wakeup source for deepsleep (#1201)
- Introduce `InterruptExecutor::spawner()` (#1211) - Introduce `InterruptExecutor::spawner()` (#1211)
- Add `InterruptHandler` struct, which couples interrupt handlers and their priority together (#1299)
### Fixed ### Fixed

View File

@ -30,10 +30,13 @@ use procmacros::handler;
#[cfg(any(adc, dac))] #[cfg(any(adc, dac))]
pub(crate) use crate::analog; pub(crate) use crate::analog;
pub(crate) use crate::gpio; pub(crate) use crate::gpio;
use crate::peripherals::{GPIO, IO_MUX};
#[cfg(any(xtensa, esp32c3))] #[cfg(any(xtensa, esp32c3))]
pub(crate) use crate::rtc_pins; pub(crate) use crate::rtc_pins;
pub use crate::soc::gpio::*; pub use crate::soc::gpio::*;
use crate::{
interrupt::InterruptHandler,
peripherals::{GPIO, IO_MUX},
};
/// Convenience type-alias for a no-pin / don't care - pin /// Convenience type-alias for a no-pin / don't care - pin
pub type NoPinType = Gpio0<Unknown>; pub type NoPinType = Gpio0<Unknown>;
@ -41,8 +44,7 @@ pub type NoPinType = Gpio0<Unknown>;
/// Convenience constant for `Option::None` pin /// Convenience constant for `Option::None` pin
pub const NO_PIN: Option<NoPinType> = None; pub const NO_PIN: Option<NoPinType> = None;
static USER_INTERRUPT_HANDLER: Mutex<Cell<Option<unsafe extern "C" fn()>>> = static USER_INTERRUPT_HANDLER: Mutex<Cell<Option<InterruptHandler>>> = Mutex::new(Cell::new(None));
Mutex::new(Cell::new(None));
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum Event { pub enum Event {
@ -1872,9 +1874,9 @@ pub struct IO {
} }
impl IO { impl IO {
/// Initialize the I/O driver.
pub fn new(mut gpio: GPIO, io_mux: IO_MUX) -> Self { pub fn new(mut gpio: GPIO, io_mux: IO_MUX) -> Self {
gpio.bind_gpio_interrupt(gpio_interrupt_handler); gpio.bind_gpio_interrupt(gpio_interrupt_handler.handler());
let pins = gpio.split(); let pins = gpio.split();
IO { IO {
@ -1883,6 +1885,15 @@ impl IO {
} }
} }
/// Initialize the I/O driver with a interrupt priority.
///
/// This decides the priority for the interrupt when only using async.
pub fn new_with_priority(gpio: GPIO, io_mux: IO_MUX, prio: crate::interrupt::Priority) -> Self {
crate::interrupt::enable(crate::peripherals::Interrupt::GPIO, prio).unwrap();
Self::new(gpio, io_mux)
}
/// Install the given interrupt handler replacing any previously set /// Install the given interrupt handler replacing any previously set
/// handler. /// handler.
/// ///
@ -1890,20 +1901,20 @@ impl IO {
/// the internal async handler will run after. In that case it's /// the internal async handler will run after. In that case it's
/// important to not reset the interrupt status when mixing sync and /// important to not reset the interrupt status when mixing sync and
/// async (i.e. using async wait) interrupt handling. /// async (i.e. using async wait) interrupt handling.
pub fn set_interrupt_handler(&mut self, handler: unsafe extern "C" fn() -> ()) { pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
critical_section::with(|cs| { critical_section::with(|cs| {
crate::interrupt::enable(crate::peripherals::Interrupt::GPIO, handler.priority())
.unwrap();
USER_INTERRUPT_HANDLER.borrow(cs).set(Some(handler)); USER_INTERRUPT_HANDLER.borrow(cs).set(Some(handler));
}); });
} }
} }
#[handler] #[handler]
unsafe fn gpio_interrupt_handler() { fn gpio_interrupt_handler() {
if let Some(user_handler) = critical_section::with(|cs| USER_INTERRUPT_HANDLER.borrow(cs).get()) if let Some(user_handler) = critical_section::with(|cs| USER_INTERRUPT_HANDLER.borrow(cs).get())
{ {
unsafe { user_handler.call();
user_handler();
}
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
@ -3095,7 +3106,6 @@ mod asynch {
use embedded_hal_async::digital::Wait; use embedded_hal_async::digital::Wait;
use super::*; use super::*;
use crate::prelude::*;
#[allow(clippy::declare_interior_mutable_const)] #[allow(clippy::declare_interior_mutable_const)]
const NEW_AW: AtomicWaker = AtomicWaker::new(); const NEW_AW: AtomicWaker = AtomicWaker::new();

View File

@ -60,3 +60,31 @@ pub use self::xtensa::*;
mod riscv; mod riscv;
#[cfg(xtensa)] #[cfg(xtensa)]
mod xtensa; mod xtensa;
/// An interrupt handler
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct InterruptHandler {
f: extern "C" fn(),
prio: Priority,
}
impl InterruptHandler {
pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
Self { f, prio }
}
#[inline]
pub fn handler(&self) -> extern "C" fn() {
self.f
}
#[inline]
pub fn priority(&self) -> Priority {
self.prio
}
#[inline]
pub(crate) extern "C" fn call(&self) {
(self.f)()
}
}

View File

@ -88,6 +88,7 @@ pub enum CpuInterrupt {
} }
/// Interrupt priority levels. /// Interrupt priority levels.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum Priority { pub enum Priority {
@ -118,7 +119,7 @@ pub enum Priority {
} }
impl Priority { impl Priority {
pub fn max() -> Priority { pub const fn max() -> Priority {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(not(clic))] { if #[cfg(not(clic))] {
Priority::Priority15 Priority::Priority15
@ -128,7 +129,7 @@ impl Priority {
} }
} }
pub fn min() -> Priority { pub const fn min() -> Priority {
Priority::Priority1 Priority::Priority1
} }
} }

View File

@ -241,11 +241,11 @@ mod vectored {
} }
impl Priority { impl Priority {
pub fn max() -> Priority { pub const fn max() -> Priority {
Priority::Priority3 Priority::Priority3
} }
pub fn min() -> Priority { pub const fn min() -> Priority {
Priority::Priority1 Priority::Priority1
} }
} }

View File

@ -18,9 +18,8 @@ use esp_hal::{
clock::ClockControl, clock::ClockControl,
delay::Delay, delay::Delay,
gpio::{self, Event, Input, PullDown, IO}, gpio::{self, Event, Input, PullDown, IO},
interrupt::{self, Priority},
macros::ram, macros::ram,
peripherals::{Interrupt, Peripherals}, peripherals::Peripherals,
prelude::*, prelude::*,
}; };
@ -46,12 +45,11 @@ fn main() -> ! {
let mut button = io.pins.gpio0.into_pull_down_input(); let mut button = io.pins.gpio0.into_pull_down_input();
#[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] #[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))]
let mut button = io.pins.gpio9.into_pull_down_input(); let mut button = io.pins.gpio9.into_pull_down_input();
critical_section::with(|cs| {
button.listen(Event::FallingEdge); button.listen(Event::FallingEdge);
BUTTON.borrow_ref_mut(cs).replace(button)
critical_section::with(|cs| BUTTON.borrow_ref_mut(cs).replace(button)); });
interrupt::enable(Interrupt::GPIO, Priority::Priority2).unwrap();
led.set_high().unwrap(); led.set_high().unwrap();
// Initialize the Delay peripheral, and use it to toggle the LED state in a // Initialize the Delay peripheral, and use it to toggle the LED state in a