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 {
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};
@ -417,10 +419,28 @@ pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
.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
#[allow(non_upper_case_globals)]
static #orig: #root::interrupt::InterruptHandler = #root::interrupt::InterruptHandler::new(#new, #root::interrupt::Priority::min());
)
.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)
- 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

View File

@ -30,10 +30,13 @@ 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>;
@ -41,8 +44,7 @@ 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 {
@ -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 {
@ -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
/// handler.
///
@ -1890,20 +1901,20 @@ impl IO {
/// 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")]
@ -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();

View File

@ -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)()
}
}

View File

@ -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 {
@ -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
@ -128,7 +129,7 @@ impl Priority {
}
}
pub fn min() -> Priority {
pub const fn min() -> Priority {
Priority::Priority1
}
}

View File

@ -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
}
}

View File

@ -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::*,
};
@ -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();
critical_section::with(|cs| {
button.listen(Event::FallingEdge);
critical_section::with(|cs| BUTTON.borrow_ref_mut(cs).replace(button));
interrupt::enable(Interrupt::GPIO, Priority::Priority2).unwrap();
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