Runtime interrupt binding (#1231)
* Interrupt runtime binding * Add `set_interrupt_handler` for GPIO * Fix * Fix typo * Make sure to produce a warning for an unused `#[handler]` * Simplify GPIO interrupt handling * Appease Clippy * Make sure to patch the PACS in esp-hal * Use latest PAC commit * CHANGELOG.md entry
This commit is contained in:
parent
1f129744fd
commit
2b4c408333
@ -359,6 +359,72 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Mark a function as an interrupt handler.
|
||||
///
|
||||
/// This is really just a nicer looking way to make a function `unsafe extern
|
||||
/// "C"`
|
||||
#[cfg(feature = "interrupt")]
|
||||
#[proc_macro_attribute]
|
||||
pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use darling::ast::NestedMeta;
|
||||
use proc_macro::Span;
|
||||
use proc_macro_error::abort;
|
||||
use syn::{parse::Error as ParseError, spanned::Spanned, ItemFn, ReturnType, Type};
|
||||
|
||||
use self::interrupt::{check_attr_whitelist, WhiteListCaller};
|
||||
|
||||
let mut f: ItemFn = syn::parse(input).expect("`#[handler]` must be applied to a function");
|
||||
let original_span = f.span();
|
||||
|
||||
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return TokenStream::from(darling::Error::from(e).write_errors());
|
||||
}
|
||||
};
|
||||
|
||||
if attr_args.len() > 0 {
|
||||
abort!(Span::call_site(), "This attribute accepts no arguments")
|
||||
}
|
||||
|
||||
// XXX should we blacklist other attributes?
|
||||
|
||||
if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) {
|
||||
return error;
|
||||
}
|
||||
|
||||
let valid_signature = f.sig.constness.is_none()
|
||||
&& f.sig.abi.is_none()
|
||||
&& f.sig.generics.params.is_empty()
|
||||
&& f.sig.generics.where_clause.is_none()
|
||||
&& f.sig.variadic.is_none()
|
||||
&& match f.sig.output {
|
||||
ReturnType::Default => true,
|
||||
ReturnType::Type(_, ref ty) => match **ty {
|
||||
Type::Tuple(ref tuple) => tuple.elems.is_empty(),
|
||||
Type::Never(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
&& f.sig.inputs.len() <= 1;
|
||||
|
||||
if !valid_signature {
|
||||
return ParseError::new(
|
||||
f.span(),
|
||||
"`#[handler]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
f.sig.abi = syn::parse_quote_spanned!(original_span => extern "C");
|
||||
|
||||
quote::quote_spanned!( original_span =>
|
||||
#f
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Create an enum for erased GPIO pins, using the enum-dispatch pattern
|
||||
///
|
||||
/// Only used internally
|
||||
|
||||
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
- Prefer mutable references over moving for DMA transactions (#1238)
|
||||
- Support runtime interrupt binding, adapt GPIO driver (#1231)
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
@ -221,3 +221,13 @@ opsram-16m = []
|
||||
|
||||
[lints.clippy]
|
||||
mixed_attributes_style = "allow"
|
||||
|
||||
[patch.crates-io]
|
||||
esp32 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32s2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32s3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32c2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32c3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32c6 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32p4 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
|
||||
@ -22,7 +22,10 @@
|
||||
//!
|
||||
//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
|
||||
|
||||
use core::{convert::Infallible, marker::PhantomData};
|
||||
use core::{cell::Cell, convert::Infallible, marker::PhantomData};
|
||||
|
||||
use critical_section::Mutex;
|
||||
use procmacros::interrupt;
|
||||
|
||||
#[cfg(any(adc, dac))]
|
||||
pub(crate) use crate::analog;
|
||||
@ -38,6 +41,9 @@ 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));
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Event {
|
||||
RisingEdge = 1,
|
||||
@ -1854,6 +1860,32 @@ impl IO {
|
||||
pins,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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() -> ()) {
|
||||
critical_section::with(|cs| {
|
||||
USER_INTERRUPT_HANDLER.borrow(cs).set(Some(handler));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
unsafe fn GPIO() {
|
||||
if let Some(user_handler) = critical_section::with(|cs| USER_INTERRUPT_HANDLER.borrow(cs).get())
|
||||
{
|
||||
unsafe {
|
||||
user_handler();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
asynch::handle_gpio_interrupt();
|
||||
}
|
||||
|
||||
pub trait GpioProperties {
|
||||
@ -3149,19 +3181,7 @@ mod asynch {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(any(esp32p4)))]
|
||||
#[interrupt]
|
||||
unsafe fn GPIO() {
|
||||
handle_gpio_interrupt();
|
||||
}
|
||||
|
||||
#[cfg(esp32p4)]
|
||||
#[interrupt]
|
||||
unsafe fn GPIO_INT0() {
|
||||
handle_gpio_interrupt();
|
||||
}
|
||||
|
||||
fn handle_gpio_interrupt() {
|
||||
pub(super) fn handle_gpio_interrupt() {
|
||||
let intrs_bank0 = InterruptStatusRegisterAccessBank0::interrupt_status_read();
|
||||
|
||||
#[cfg(any(esp32, esp32s2, esp32s3, esp32p4))]
|
||||
|
||||
@ -199,6 +199,13 @@ mod vectored {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Bind the given interrupt to the given handler
|
||||
pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn() -> ()) {
|
||||
let ptr = &peripherals::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as *const _
|
||||
as *mut unsafe extern "C" fn() -> ();
|
||||
ptr.write_volatile(handler);
|
||||
}
|
||||
|
||||
/// Enables an interrupt at a given priority, maps it to the given CPU
|
||||
/// interrupt and assigns the given priority.
|
||||
///
|
||||
|
||||
@ -311,6 +311,13 @@ mod vectored {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Bind the given interrupt to the given handler
|
||||
pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn() -> ()) {
|
||||
let ptr = &peripherals::__INTERRUPTS[interrupt as usize]._handler as *const _
|
||||
as *mut unsafe extern "C" fn() -> ();
|
||||
ptr.write_volatile(handler);
|
||||
}
|
||||
|
||||
fn interrupt_level_to_cpu_interrupt(
|
||||
level: Priority,
|
||||
is_edge: bool,
|
||||
|
||||
@ -221,7 +221,7 @@ mod peripheral_macros {
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! peripherals {
|
||||
($($(#[$cfg:meta])? $name:ident <= $from_pac:tt),*$(,)?) => {
|
||||
($($(#[$cfg:meta])? $name:ident <= $from_pac:tt $(($($interrupt:ident),*))? ),*$(,)?) => {
|
||||
|
||||
/// Contains the generated peripherals which implement [`Peripheral`]
|
||||
mod peripherals {
|
||||
@ -278,6 +278,21 @@ mod peripheral_macros {
|
||||
$(
|
||||
pub use peripherals::$name;
|
||||
)*
|
||||
|
||||
$(
|
||||
$(
|
||||
impl peripherals::$name {
|
||||
$(
|
||||
paste::paste!{
|
||||
pub fn [<bind_ $interrupt:lower _interrupt >](&mut self, handler: unsafe extern "C" fn() -> ()) {
|
||||
unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$interrupt, handler); }
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
)*
|
||||
)*
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ crate::peripherals! {
|
||||
EFUSE <= EFUSE,
|
||||
FLASH_ENCRYPTION <= FLASH_ENCRYPTION,
|
||||
FRC_TIMER <= FRC_TIMER,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_NMI),
|
||||
GPIO_SD <= GPIO_SD,
|
||||
HINF <= HINF,
|
||||
I2C0 <= I2C0,
|
||||
|
||||
@ -28,7 +28,7 @@ crate::peripherals! {
|
||||
ECC <= ECC,
|
||||
EFUSE <= EFUSE,
|
||||
EXTMEM <= EXTMEM,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_NMI),
|
||||
I2C0 <= I2C0,
|
||||
INTERRUPT_CORE0 <= INTERRUPT_CORE0,
|
||||
IO_MUX <= IO_MUX,
|
||||
|
||||
@ -30,7 +30,7 @@ crate::peripherals! {
|
||||
DS <= DS,
|
||||
EFUSE <= EFUSE,
|
||||
EXTMEM <= EXTMEM,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_NMI),
|
||||
GPIO_SD <= GPIO_SD,
|
||||
HMAC <= HMAC,
|
||||
I2C0 <= I2C0,
|
||||
|
||||
@ -30,7 +30,7 @@ crate::peripherals! {
|
||||
ECC <= ECC,
|
||||
EFUSE <= EFUSE,
|
||||
EXTMEM <= EXTMEM,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_NMI),
|
||||
GPIO_SD <= GPIO_SD,
|
||||
HINF <= HINF,
|
||||
HMAC <= HMAC,
|
||||
|
||||
@ -28,7 +28,7 @@ crate::peripherals! {
|
||||
DS <= DS,
|
||||
ECC <= ECC,
|
||||
EFUSE <= EFUSE,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_NMI),
|
||||
GPIO_SD <= GPIO_SD,
|
||||
HMAC <= HMAC,
|
||||
HP_APM <= HP_APM,
|
||||
|
||||
@ -32,7 +32,7 @@ crate::peripherals! {
|
||||
ECC <= ECC,
|
||||
ECDSA <= ECDSA,
|
||||
EFUSE <= EFUSE,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_INT1,GPIO_INT2,GPIO_INT3),
|
||||
GPIO_SD <= GPIO_SD,
|
||||
H264 <= H264,
|
||||
H264_DMA <= H264_DMA,
|
||||
|
||||
@ -30,7 +30,7 @@ crate::peripherals! {
|
||||
DS <= DS,
|
||||
EFUSE <= EFUSE,
|
||||
EXTMEM <= EXTMEM,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_NMI),
|
||||
GPIO_SD <= GPIO_SD,
|
||||
HMAC <= HMAC,
|
||||
I2C0 <= I2C0,
|
||||
|
||||
@ -30,7 +30,7 @@ crate::peripherals! {
|
||||
DS <= DS,
|
||||
EFUSE <= EFUSE,
|
||||
EXTMEM <= EXTMEM,
|
||||
GPIO <= GPIO,
|
||||
GPIO <= GPIO (GPIO,GPIO_NMI),
|
||||
GPIO_SD <= GPIO_SD,
|
||||
HMAC <= HMAC,
|
||||
I2C0 <= I2C0,
|
||||
|
||||
@ -70,3 +70,13 @@ psram-2m = ["esp-hal/psram-2m"]
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
[patch.crates-io]
|
||||
esp32 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32s2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32s3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32c2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32c3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32c6 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
esp32p4 = { git = "https://github.com/esp-rs/esp-pacs", rev = "963c280621f0b7ec26546a5eff24a5032305437f" }
|
||||
|
||||
@ -36,7 +36,8 @@ fn main() -> ! {
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Set GPIO2 as an output, and set its state high initially.
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
let mut io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
io.set_interrupt_handler(handler);
|
||||
let mut led = io.pins.gpio2.into_push_pull_output();
|
||||
|
||||
#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))]
|
||||
@ -61,9 +62,9 @@ fn main() -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[handler]
|
||||
#[ram]
|
||||
#[interrupt]
|
||||
fn GPIO() {
|
||||
fn handler() {
|
||||
#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))]
|
||||
esp_println::println!(
|
||||
"GPIO Interrupt with priority {}",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user