Skip to content

Commit

Permalink
Add InterruptHandler wrapper (#1299)
Browse files Browse the repository at this point in the history
* Add InterruptHandler

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

* changelog
  • Loading branch information
MabezDev authored Mar 21, 2024
1 parent 25f509c commit 93d31e7
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 23 deletions.
22 changes: 21 additions & 1 deletion esp-hal-procmacros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
use darling::ast::NestedMeta;
use proc_macro::Span;
use proc_macro2::Ident;
use proc_macro_crate::{crate_name, FoundCrate};
use proc_macro_error::abort;
use syn::{parse::Error as ParseError, spanned::Spanned, ItemFn, ReturnType, Type};

Expand Down Expand Up @@ -417,10 +419,28 @@ pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
.into();
}

let root = Ident::new(
if let Ok(FoundCrate::Name(ref name)) = crate_name("esp-hal") {
&name
} else {
"crate"
},
Span::call_site().into(),
);

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 =>
quote::quote_spanned!(original_span =>
#f

#[allow(non_upper_case_globals)]
static #orig: #root::interrupt::InterruptHandler = #root::interrupt::InterruptHandler::new(#new, #root::interrupt::Priority::min());
)
.into()
}
Expand Down
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- ESP32-C6: Add timer wakeup source for deepsleep (#1201)
- Introduce `InterruptExecutor::spawner()` (#1211)
- Add `InterruptHandler` struct, which couples interrupt handlers and their priority together (#1299)

### Fixed

Expand Down
32 changes: 21 additions & 11 deletions esp-hal/src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,21 @@ use procmacros::handler;
#[cfg(any(adc, dac))]
pub(crate) use crate::analog;
pub(crate) use crate::gpio;
use crate::peripherals::{GPIO, IO_MUX};
#[cfg(any(xtensa, esp32c3))]
pub(crate) use crate::rtc_pins;
pub use crate::soc::gpio::*;
use crate::{
interrupt::InterruptHandler,
peripherals::{GPIO, IO_MUX},
};

/// Convenience type-alias for a no-pin / don't care - pin
pub type NoPinType = Gpio0<Unknown>;

/// Convenience constant for `Option::None` pin
pub const NO_PIN: Option<NoPinType> = None;

static USER_INTERRUPT_HANDLER: Mutex<Cell<Option<unsafe extern "C" fn()>>> =
Mutex::new(Cell::new(None));
static USER_INTERRUPT_HANDLER: Mutex<Cell<Option<InterruptHandler>>> = Mutex::new(Cell::new(None));

#[derive(Copy, Clone)]
pub enum Event {
Expand Down Expand Up @@ -1872,9 +1874,9 @@ pub struct IO {
}

impl IO {
/// Initialize the I/O driver.
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();

IO {
Expand All @@ -1883,27 +1885,36 @@ 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
/// handler.
///
/// When the async feature is enabled the handler will be called first and
/// the internal async handler will run after. In that case it's
/// important to not reset the interrupt status when mixing sync and
/// 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| {
crate::interrupt::enable(crate::peripherals::Interrupt::GPIO, handler.priority())
.unwrap();
USER_INTERRUPT_HANDLER.borrow(cs).set(Some(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())
{
unsafe {
user_handler();
}
user_handler.call();
}

#[cfg(feature = "async")]
Expand Down Expand Up @@ -3095,7 +3106,6 @@ mod asynch {
use embedded_hal_async::digital::Wait;

use super::*;
use crate::prelude::*;

#[allow(clippy::declare_interior_mutable_const)]
const NEW_AW: AtomicWaker = AtomicWaker::new();
Expand Down
28 changes: 28 additions & 0 deletions esp-hal/src/interrupt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,31 @@ pub use self::xtensa::*;
mod riscv;
#[cfg(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)()
}
}
5 changes: 3 additions & 2 deletions esp-hal/src/interrupt/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub enum CpuInterrupt {
}

/// Interrupt priority levels.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
Expand Down Expand Up @@ -118,7 +119,7 @@ pub enum Priority {
}

impl Priority {
pub fn max() -> Priority {
pub const fn max() -> Priority {
cfg_if::cfg_if! {
if #[cfg(not(clic))] {
Priority::Priority15
Expand All @@ -128,7 +129,7 @@ impl Priority {
}
}

pub fn min() -> Priority {
pub const fn min() -> Priority {
Priority::Priority1
}
}
Expand Down
4 changes: 2 additions & 2 deletions esp-hal/src/interrupt/xtensa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,11 @@ mod vectored {
}

impl Priority {
pub fn max() -> Priority {
pub const fn max() -> Priority {
Priority::Priority3
}

pub fn min() -> Priority {
pub const fn min() -> Priority {
Priority::Priority1
}
}
Expand Down
12 changes: 5 additions & 7 deletions examples/src/bin/gpio_interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ use esp_hal::{
clock::ClockControl,
delay::Delay,
gpio::{self, Event, Input, PullDown, IO},
interrupt::{self, Priority},
macros::ram,
peripherals::{Interrupt, Peripherals},
peripherals::Peripherals,
prelude::*,
};

Expand All @@ -46,12 +45,11 @@ fn main() -> ! {
let mut button = io.pins.gpio0.into_pull_down_input();
#[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))]
let mut button = io.pins.gpio9.into_pull_down_input();
button.listen(Event::FallingEdge);

critical_section::with(|cs| BUTTON.borrow_ref_mut(cs).replace(button));

interrupt::enable(Interrupt::GPIO, Priority::Priority2).unwrap();

critical_section::with(|cs| {
button.listen(Event::FallingEdge);
BUTTON.borrow_ref_mut(cs).replace(button)
});
led.set_high().unwrap();

// Initialize the Delay peripheral, and use it to toggle the LED state in a
Expand Down

0 comments on commit 93d31e7

Please sign in to comment.